I have an application that I am making in order to practice MVVM. What this application does is simply call Retrofit and paint the data from a JSON into a RecyclerView. In the row of the RecyclerView I have a checkbox whose functionality is that if it is marked, pressing a button with u shows me those marked elements in another RecyclerView of another fragment. The problem is that when I mark a check, before sending it or anything, if I turn the screen, it's like the list is reloaded or I don't know very well what behavior it is doing, but the fact is that that checkbox is unchecked again and I want that even though I haven't sent those changes to Retrofit yet, that checkbox press remains:
This is my class for the view:
package com.example.menunavegacion.ui.fragments.tabs.fragmentpets.view;
....
public class FragmentPets extends Fragment {
@BindView(R.id.recyclerView)
RecyclerView recyclerView;
@BindView(R.id.progressBar)
ProgressBar progressBar;
PetsViewModel mWordViewModel;
PetsAdapter adapter;
@Inject
RequestInterface requestInterface;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_tab_one, container, false);
ButterKnife.bind(this, v);
initViews();
return v;
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mWordViewModel = ViewModelProviders.of(this).get(PetsViewModel.class);
mWordViewModel.getAllPets().observe(this, new Observer<ArrayList<PetsDTO>>() {
@Override
public void onChanged(ArrayList<PetsDTO> petsDTOS) {
adapter = new PetsAdapter(petsDTOS, getContext(), false);
recyclerView.setAdapter(adapter);
}
});
mWordViewModel.getLoadingLiveData().observe(this, new Observer<Boolean>() {
@Override
public void onChanged(Boolean loading) {
if (loading) {
progressBar.setVisibility(View.VISIBLE);
} else {
progressBar.setVisibility(View.GONE);
}
}
});
mWordViewModel.loadJSON();
mWordViewModel.showFinishMessage().observe(this, new Observer<Boolean>() {
@Override
public void onChanged(Boolean aBoolean) {
if (aBoolean) {
Toast.makeText(getActivity(), getActivity().getString(R.string.update_toast_message), Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(getActivity(), getActivity().getString(R.string.message_send_error), Toast.LENGTH_SHORT).show();
}
}
});
}
@OnClick(R.id.btnSend)
public void onSend() {
mWordViewModel.updateList(adapter.getPetList());
}
private void initViews() {
recyclerView.setHasFixedSize(true);
RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(getContext());
recyclerView.setLayoutManager(layoutManager);
}
}
This is my ViewModel:
package com.example.menunavegacion.ui.fragments.tabs.fragmentpets.viewmodel;
....
public class PetsViewModel extends AndroidViewModel implements ViewModelInterface.ViewModel {
private PetsRepository mPetsRepository;
private LiveData<ArrayList<PetsDTO>> mAllPets;
public PetsViewModel (Application application){
super(application);
mPetsRepository = new PetsRepository(application);
mAllPets = mPetsRepository.getAllPetsLiveData();
}
public LiveData<ArrayList<PetsDTO>> getAllPets() {
return mPetsRepository.getAllPetsLiveData();
}
public LiveData<Boolean> getLoadingLiveData() {
return mPetsRepository.getLoadingLiveData();
}
public LiveData<Boolean> showFinishMessage() {
return mPetsRepository.getFinishMessage();
}
@Override
public void loadJSON() {
mPetsRepository.useCaseLoadJSON();
}
@Override
public void updateList(ArrayList<PetsDTO> pets) {
mPetsRepository.useCaseUpdateList(pets);
}
}
This is the repository:
package com.example.menunavegacion.ui.fragments.tabs;
....
public class PetsRepository {
LoadJSONUseCase loadJSONUseCase;
UpdateListUseCase updateListUseCase;
private MutableLiveData<ArrayList<PetsDTO>> allPetsLiveData = new MutableLiveData<>();
private MutableLiveData<Boolean> loadingLiveData = new MutableLiveData<>();
private MutableLiveData<Boolean> showFinishMessage = new MutableLiveData<>();
public LiveData<ArrayList<PetsDTO>> getAllPetsLiveData() {
return allPetsLiveData;
}
public LiveData<Boolean> getLoadingLiveData() {
return loadingLiveData;
}
public LiveData<Boolean> getFinishMessage() {
return showFinishMessage;
}
public PetsRepository(Application application) {
loadJSONUseCase = new LoadJSONUseCase();
updateListUseCase = new UpdateListUseCase();
}
public void useCaseLoadJSON() {
loadJSONUseCase.loadJSON(new RepositoryInterface() {
@Override
public void showError(Boolean show) {
showFinishMessage.postValue(show);
}
@Override
public void onLoading(boolean loading) {
loadingLiveData.postValue(loading);
}
@Override
public void onSuccess(ArrayList<PetsDTO> data) {
allPetsLiveData.postValue(data);
}
});
}
public void useCaseUpdateList(ArrayList<PetsDTO> pets) {
updateListUseCase.updateList(pets, new RepositoryInterface() {
@Override
public void showError(Boolean show) {
showFinishMessage.postValue(show);
}
@Override
public void onLoading(boolean loading) {
loadingLiveData.postValue(loading);
}
@Override
public void onSuccess(ArrayList<PetsDTO> data) {
allPetsLiveData.postValue(pets);
}
});
}
}
This is my usecase that is responsible for loading the Retrofit JSON:
package com.example.menunavegacion.ui.fragments.usecase;
....
public class LoadJSONUseCase {
@Inject
RequestInterface requestInterface;
public LoadJSONUseCase(){
DaggerComponenTest.builder().build().inject(this);
}
public void loadJSON(RepositoryInterface repositoryInterface) {
Call<JSONResponse> call = requestInterface.getJSON();
repositoryInterface.onLoading(true);
call.enqueue(new Callback<JSONResponse>() {
@Override
public void onResponse(Call<JSONResponse> call, Response<JSONResponse> response) {
repositoryInterface.onLoading(false);
JSONResponse jsonResponse = response.body();
ArrayList<PetsDTO> data = new ArrayList<>();
data.clear();
data.addAll(jsonResponse.getPetsDTO());
repositoryInterface.onSuccess(data);
}
@Override
public void onFailure(Call<JSONResponse> call, Throwable t) {
repositoryInterface.onLoading(false);
repositoryInterface.showError(false);
}
});
}
}
This would be all my code. I also accept advice about MVVM since I'm learning and well, they're welcome, so I'll stay with you. Thank you very much.
This has to do with how Android handles the Activities lifecycle , basically when you rotate the screen Android "destroys" the activity and then recreates it, this removes any state from the UI. I can think of two ways to fix it, the first one is the simplest and the second one is a bit tricky:
Solution 1: You can restrict the screen orientation of that activity, by adding android:screenOrientation="portrait" in the AndroidManifest.xml , that activity will only look vertical and thus avoid losing the state of the checkbox:
Solution 2: You can fix it using, in the activity where that code is executed, the onSaveInstanceState() method with this you can save the state of the checkbox in a Bundle :
And to recover it, you use the onRestoreInstanceState() method in the same activity :
Although also in the documentation it is mentioned that the Bundle can be retrieved in the onCreate() method, but it is necessary to verify that the bundle savedIntanceState is not null .
You can try to save your fix to the bundle and retrieve it after rotating the screen.
First you need your PetsDTO model to implement Parcelable and implement the necessary methods:
Then in your snippet use the onSaveInstanceState methods to save the current state of your list to the Bundle:
In your inViewCreated() retrieve the arrayList saved in the Bundle:
And in your Activiy you also need to save the state of the fragment using the OnCreate and onSaveInstanceState methods:
I hope it helps you.