旋转设备时 Recyclerview 数据消失

Posted

技术标签:

【中文标题】旋转设备时 Recyclerview 数据消失【英文标题】:Recyclerview data disappears when device is rotated 【发布时间】:2020-08-02 08:14:32 【问题描述】:

即使我使用的是 ViewModel,只要设备旋转,Recyclerview 中的数据就会消失。我不得不将 makeSearch() 方法放在 onClick() 方法中,因为我需要获取按钮抓取的文本并将其用作搜索参数。有没有更好的方法可以解决这个问题来避免这个问题?我的代码就在这里:

搜索活动:

@Override
protected void onCreate(Bundle savedInstanceState) 
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_search);

// What happens when the search button is clicked
materialButton.setOnClickListener(new View.OnClickListener() 
    @Override
     public void onClick(View v) 
        if (Objects.requireNonNull(textInputEditText.getText()).toString().isEmpty()) 
             textInputEditText.setError("Type a search query");
             else 
                mSearchInput = Objects.requireNonNull(textInputEditText.getText()).toString();
                textInputEditText.setText("");
                makeSearch();
            
        
    );


// Gets the ViewModel, Observes the Question LiveData and delivers it to the Recyclerview
private void makeSearch() 
    final SearchAdapter searchAdapter = new SearchAdapter();
    SearchViewModel mSearchViewModel = new ViewModelProvider(this,
            new CustomSearchViewModelFactory(new SearchRepository())).get(SearchViewModel.class);
    mSearchViewModel.setQuery(mSearchInput);
    mSearchViewModel.getQuestionLiveData().observe(this, new Observer<List<Question>>() 
            @Override
            public void onChanged(List<Question> questions) 
                    mQuestions = questions;
                    searchAdapter.setQuestions(questions);
            
        );
        mRecyclerView.setAdapter(searchAdapter);
        searchAdapter.setOnClickListener(mOnClickListener);

搜索视图模型:

public class SearchViewModel extends ViewModel 

private SearchRepository mSearchRepository;
private MutableLiveData<String> mSearchLiveData = new MutableLiveData<>();
private LiveData<List<Question>> mQuestionLiveData = Transformations.switchMap(mSearchLiveData, (query) -> 
    return mSearchRepository.getQuestions(query);
);

SearchViewModel(SearchRepository searchRepository) 
    this.mSearchRepository = searchRepository;


public LiveData<List<Question>> getQuestionLiveData() 
    return mQuestionLiveData;


public void setQuery(String query) 
    mSearchLiveData.setValue(query);


搜索存储库:

public class SearchRepository 

//private String inTitle;
private MutableLiveData<List<Question>> mQuestions = new MutableLiveData<>();

public SearchRepository() 
    //getQuestionsWithTextInTitle();


private void getQuestionsWithTextInTitle(String inTitle) 
    ApiService apiService = RestApiClient.getApiService(ApiService.class);
    Call<QuestionsResponse> call = apiService.getQuestionsWithTextInTitle(inTitle);
    call.enqueue(new Callback<QuestionsResponse>() 
        @Override
        public void onResponse(Call<QuestionsResponse> call, Response<QuestionsResponse> response) 
            QuestionsResponse questionsResponse = response.body();
            if (questionsResponse != null) 
                mQuestions.postValue(questionsResponse.getItems());
                //shouldShowData = true;
             else 
                Log.d("SearchRepository", "No matching question");
                //shouldShowData = false;
            
        

        @Override
        public void onFailure(Call<QuestionsResponse> call, Throwable t) 
            //shouldShowData = false;
            t.printStackTrace();
        
    );


public LiveData<List<Question>> getQuestions(String inTitle) 
    getQuestionsWithTextInTitle(inTitle);
    return mQuestions;


【问题讨论】:

据我所知,您没有在 onCreate() 中执行任何操作来重新填充您的 RecyclerView。您说inTitle 需要成为您的视图模型的构造函数参数,这极大地限制了自己,因为该值会随着用户在搜索中键入而改变。您可能希望让您的视图模型公开一个稳定的LiveData(例如,MediatorLiveData),该活动始终可以观察到,并让搜索更新该稳定的输出。有关示例,请参见 gitlab.com/commonsguy/cw-jetpack-java/-/tree/v0.8/Weather。 【参考方案1】:

您通过CustomSearchViewModelFactory 将搜索输入传递到ViewModel 的构造函数和SearchRepository 的构造函数的方法在任何情况下都行不通。当您第一次搜索 CustomSearchViewModelFactory 创建 ViewModel 时,第二次点击搜索时,您的 SearchViewModel 已经创建,并且您的工厂没有第二次调用,这意味着您永远不会收到第二个查询。

相反,您应该归档ViewModel Overview documentation,并使用Transformations.switchMap() 将您的输入(搜索字符串)转换为给定查询的新LiveData&lt;List&lt;Question&gt;&gt;

这意味着您的 ViewModel 看起来像

public class SearchViewModel extends ViewModel 

    private SearchRepository mSearchRepository;
    private MutableLiveData<String> mSearchLiveData = new MutableLiveData<String>();
    private LiveData<List<Question>> mQuestionLiveData =
        Transformations.switchMap(mSearchLiveData, (query) -> 
            return mSearchRepository.getQuestions(query);
         );

    public SearchViewModel() 
        mSearchRepository = new SearchRepository();
    

    public void setQuery(String query) 
        mSearchLiveData.setValue(query);
    

    public LiveData<List<Question>> getQuestionLiveData() 
        return mQuestionLiveData;
    

然后您将 Activity 更新为:

    始终观察getQuestionLiveData()(请注意,在您实际设置第一个查询之前,您不会收到对观察者的回调) 在您的SearchViewModel 上致电setQuery() makeSearch() 完全删除您的CustomSearchViewModelFactory(不再需要)。

【讨论】:

嗨@ianhanniballake,感谢您的回复。我对您编写的部分代码遇到了挑战。首先,mQuestionLiveDataLiveData&lt;List&lt;Question&gt;&gt; 而不是 LiveData&lt;String&gt;。其次,mSearchRepository.getQuestions() 返回一个LiveData&lt;List&lt;Question&gt;&gt;,正如我在上面添加的SearchRepository 类中所见。这使得实现您编写的代码有点困难,请帮助 @MayorJay - 是的,你是对的。我已经更正了我的代码 更正仍然没有帮助解决我的问题。请查看我上面的SearchRepository 课程。谢谢。当我尝试你的代码时,我收到了这个错误java.lang.RuntimeException: Cannot create an instance of class com.example.josycom.flowoverstack.viewmodel.SearchViewModel 您还需要更改您的 SearchRepository,使其不接受构造函数参数,而是将输入输入到 getQuestions(),为每个查询构建一个新的 LiveData 实例 我使用您在ViewModel documentation 中看到的答案和示例修改了我的代码,我将SearchRespository 作为参数传递给SearchViewModel 构造函数。它的工作原理是每次我输入新的搜索查询时,都会返回并显示一个新的相应结果,但是在设备旋转时,显示的数据仍然消失

以上是关于旋转设备时 Recyclerview 数据消失的主要内容,如果未能解决你的问题,请参考以下文章

旋转后recyclerview行中的图像消失

使用 Glide 和 RecyclerView 上下滚动时图像消失,图像之间的空间增加

为啥我在 iPhone 6+ 上旋转时导航栏会从拆分视图中消失?

在 iOS 中旋转设备时将内容水平居中

在 iOS 6 上显示的视图消失了。我该如何解决?

(转载) Scrollview 嵌套 RecyclerView 及在Android 5.1版本滑动时 惯性消失问题