I'm a little confused about how data binding should work when using new architecture components.
let's say I have a simple activity with a list, ProgressBar and TextView. the action should be responsible for managing the state of all views, but the ViewModel must contain data and logic. For example, my activity now looks like this:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); binding = DataBindingUtil.setContentView(this, R.layout.activity_main); listViewModel = ViewModelProviders.of(this).get(ListViewModel.class); binding.setViewModel(listViewModel); list = findViewById(R.id.games_list); listViewModel.getList().observeForever(new Observer<List<Game>>() { @Override public void onChanged(@Nullable List<Game> items) { setUpList(items); } }); listViewModel.loadGames(); } private void setUpList(List<Game> items){ list.setLayoutManager(new LinearLayoutManager(this)); GameAdapter adapter = new GameAdapter(); adapter.setList(items); list.setAdapter(adapter); }
and ViewModel it is only responsible for loading the data and notifies Activity when the list is ready so that it can prepare the adapter and show the data:
public int progressVisibility = View.VISIBLE; private MutableLiveData<List<Game>> list; public void loadGames(){ Retrofit retrofit = GamesAPI.create(); GameService service = retrofit.create(GameService.class); Call<GamesResponse> call = service.fetchGames(); call.enqueue(this); } @Override public void onResponse(Call<GamesResponse> call, Response<GamesResponse> response) { if(response.body().response.equals("success")){ setList(response.body().data); } } @Override public void onFailure(Call<GamesResponse> call, Throwable t) { } public MutableLiveData<List<Game>> getList() { if(list == null) list = new MutableLiveData<>(); if(list.getValue() == null) list.setValue(new ArrayList<Game>()); return list; } public void setList(List<Game> list) { this.list.postValue(list); }
My question is: what is the correct way to show / hide the list, progress bar and error text?
you should add an Integer for each view in the ViewModel so that it manages the views and uses it like:
<TextView android:id="@+id/main_list_error" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{viewModel.error}" android:visibility="@{viewModel.errorVisibility}" />
or if the ViewModel instantiates a LiveData object for each property:
private MutableLiveData<Integer> progressVisibility = new MutableLiveData<>(); private MutableLiveData<Integer> listVisibility = new MutableLiveData<>(); private MutableLiveData<Integer> errorVisibility = new MutableLiveData<>();
update their value when necessary, and force activity to observe their value?
viewModel.getProgressVisibility().observeForever(new Observer<Integer>() { @Override public void onChanged(@Nullable Integer visibility) { progress.setVisibility(visibility); } }); viewModel.getListVisibility().observeForever(new Observer<Integer>() { @Override public void onChanged(@Nullable Integer visibility) { list.setVisibility(visibility); } }); viewModel.getErrorVisibility().observeForever(new Observer<Integer>() { @Override public void onChanged(@Nullable Integer visibility) { error.setVisibility(visibility); } });
I'm really trying to figure it out. If someone can clarify this, that would be great.
thanks