[英]Android Architecture Components: bind to ViewModel
我對使用新架構組件時數據綁定應該如何工作感到困惑。
假設我有一個帶有列表,ProgressBar和TextView的簡單Activity。 Activity應該負責控制所有視圖的狀態,但ViewModel應該保存數據和邏輯。 例如,我的Activity現在看起來像這樣:
@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);
}
而ViewModel只負責加載數據並在列表准備好后通知Activity,以便它可以准備適配器並顯示數據:
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);
}
我的問題是:顯示/隱藏列表,進度條和錯誤文本的正確方法是什么?
我應該為ViewModel中的每個View添加一個Integer,使其控制視圖並使用它,如:
<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}" />
或者ViewModel是否應為每個屬性實例化一個LiveData對象:
private MutableLiveData<Integer> progressVisibility = new MutableLiveData<>();
private MutableLiveData<Integer> listVisibility = new MutableLiveData<>();
private MutableLiveData<Integer> errorVisibility = new MutableLiveData<>();
在需要時更新它們的值並使Activity觀察它們的值?
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);
}
});
我真的很難理解這一點。 如果有人能澄清這一點,那就太棒了。
謝謝
這是一個簡單的步驟:
public class MainViewModel extends ViewModel {
MutableLiveData<ArrayList<Game>> gamesLiveData = new MutableLiveData<>();
// ObservableBoolean or ObservableField are classes from
// databinding library (android.databinding.ObservableBoolean)
public ObservableBoolean progressVisibile = new ObservableBoolean();
public ObservableBoolean listVisibile = new ObservableBoolean();
public ObservableBoolean errorVisibile = new ObservableBoolean();
public ObservableField<String> error = new ObservableField<String>();
// ...
// For example we want to change list and progress visibility
// We should just change ObservableBoolean property
// databinding knows how to bind view to changed of field
public void loadGames(){
GamesAPI.create().create(GameService.class)
.fetchGames().enqueue(this);
listVisibile.set(false);
progressVisibile.set(true);
}
@Override
public void onResponse(Call<GamesResponse> call, Response<GamesResponse> response) {
if(response.body().response.equals("success")){
gamesLiveData.setValue(response.body().data);
listVisibile.set(true);
progressVisibile.set(false);
}
}
}
接着
<data>
<import type="android.view.View"/>
<variable
name="viewModel"
type="MainViewModel"/>
</data>
...
<ProgressBar
android:layout_width="32dp"
android:layout_height="32dp"
android:visibility="@{viewModel.progressVisibile ? View.VISIBLE : View.GONE}"/>
<ListView
android:layout_width="32dp"
android:layout_height="32dp"
android:visibility="@{viewModel.listVisibile ? View.VISIBLE : View.GONE}"/>
<TextView
android:id="@+id/main_list_error"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{viewModel.error}"
android:visibility="@{viewModel.errorVisibile ? View.VISIBLE : View.GONE}"/>
另請注意,您可以選擇觀察視圖
ObservableBoolean : false / true
// or
ObservableInt : View.VISIBLE / View.INVISIBLE / View.GONE
但ObservableBoolean更適合ViewModel測試。
考慮到生命周期,你應該觀察LiveData:
@Override
protected void onCreate(Bundle savedInstanceState) {
...
listViewModel.getList().observe((LifecycleOwner) this, new Observer<List<Game>>() {
@Override
public void onChanged(@Nullable List<Game> items) {
setUpList(items);
}
});
}
以下是實現您的觀點的簡單步驟。
首先 ,讓ViewModel
公開一個LiveData
對象,然后可以使用空值啟動LiveData
。
private MutableLiveData<List<Game>> list = new MutableLiveData<>();
public MutableLiveData<List<Game>> getList() {
return list;
}
其次 ,讓您的視圖(活動/片段)觀察LiveData
並相應地更改UI。
listViewModel = ViewModelProviders.of(this).get(ListViewModel.class);
listViewModel.data.observe(this, new Observer<List<Game>>() {
@Override
public void onChanged(@Nullable final List<Game> games) {
setUpList(games);
}
});
在這里使用observe(LifecycleOwner, Observer)
變體非常重要,這樣你的觀察者就不會在LifecycleOwner
不再活動之后接收事件,這基本上就意味着當你的片段活動不再有效時,你就不會泄漏那個聽眾。
第三 ,由於數據可用,您需要更新LiveData
對象。
@Override
public void onResponse(Call<GamesResponse> call, Response<GamesResponse> response) {
if(response.body().response.equals("success")){
List<Game> newGames = response.body().data; // Assuming this is a list
list.setValue(newGames); // Update your LiveData object by calling setValue
}
}
通過在LiveData
上調用setValue()
,這將導致調用視圖偵聽器上的onChanged
,並且應該自動更新UI。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.