MayorJay
MayorJay

Reputation: 107

Recyclerview data disappears when device is rotated

Even though I am using ViewModel, whenever the device is rotated, the data in the Recyclerview disappears. I had to put the makeSearch() method inside the onClick() method because I need to get the text that the button grabs and use it as the search parameter. Is there a better way I can handle this to avoid this problem? My code is right here:

SearchActivity:

@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);
}

SearchViewModel:

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);
}
}

SearchRepository:

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;
}
}

Upvotes: 0

Views: 158

Answers (1)

ianhanniballake
ianhanniballake

Reputation: 199815

Your approach of passing the search input in through your CustomSearchViewModelFactory and into the constructor for the ViewModel and into the constructor for your SearchRepository isn't going to work in any case. While the first time you search your CustomSearchViewModelFactory creates the ViewModel, the second time you hit search, your SearchViewModel is already created and your factory is not invoked a second time, meaning you never get the second query.

Instead, you should file the ViewModel Overview documentation, and use Transformations.switchMap() to convert your input (the search string) into a new LiveData<List<Question>> for that given query.

This means that your ViewModel would look something like

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;
    }
}

You'd then update your Activity to:

  1. Always observe the getQuestionLiveData() (note that you won't get a callback to your Observer until you actually set the first query)
  2. Call setQuery() on your SearchViewModel in your makeSearch()
  3. Remove your CustomSearchViewModelFactory entirely (it would no longer be needed).

Upvotes: 2

Related Questions