ViewModel
ViewModel的引入
如果系統銷毀或重新創建界面控制器,則存儲在其中的任何臨時性界面相關數據都會丟失。例如,應用的某個 Activity 中可能包含用戶列表。因配置更改而重新創建 Activity 后,新 Activity 必須重新提取用戶列表。對於簡單的數據,Activity 可以使用 onSaveInstanceState() 方法從 onCreate() 中的捆綁包恢復其數據,但此方法僅適合可以序列化再反序列化的少量數據,而不適合數量可能較大的數據,如用戶列表或位圖。
架構組件為界面控制器提供了 ViewModel 輔助程序類,該類負責為界面准備數據。在配置更改期間會自動保留 ViewModel 對象,以便它們存儲的數據立即可供下一個 Activity 或 Fragment 實例使用。
實現ViewModel
package com.zyb.viewmodeltest;
import androidx.lifecycle.ViewModel;
public class MyViewModel extends ViewModel {
public int num = 0;
}
從 Activity 訪問該列表,如下所示:
package com.zyb.viewmodeltest;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import androidx.lifecycle.ViewModelProviders;
public class MainActivity extends AppCompatActivity {
MyViewModel myViewModel;
Button button1,button2;
TextView showScore;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myViewModel = ViewModelProviders.of(this).get(MyViewModel.class);
button1 = findViewById(R.id.button);
button2 = findViewById(R.id.button2);
showScore = findViewById(R.id.textView);
showScore.setText(myViewModel.num+"");
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
myViewModel.num++;
showScore.setText(myViewModel.num+"");
}
});
button2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
myViewModel.num += 2;
showScore.setText(myViewModel.num+"");
}
});
}
}
ui:
功能:點擊+1,數字+1,點擊+2,數字在原來基礎上+2
優點:不用我們保存之前加過數字的狀態,因為在配置更改期間會自動保留 ViewModel 對象
缺點: showScore.setText();出現過多,代碼累贅,setOnClickListener出現過多代碼不好看
LiveData
使用 LiveData 具有以下優勢:
確保界面符合數據狀態
LiveData 遵循觀察者模式。當生命周期狀態發生變化時,LiveData 會通知 Observer 對象。您可以整合代碼以在這些 Observer 對象中更新界面。觀察者可以在每次發生更改時更新界面,而不是在每次應用數據發生更改時更新界面。
不會發生內存泄露
觀察者會綁定到 Lifecycle 對象,並在其關聯的生命周期遭到銷毀后進行自我清理。
不會因 Activity 停止而導致崩潰
如果觀察者的生命周期處於非活躍狀態(如返回棧中的 Activity),則它不會接收任何 LiveData 事件。
不再需要手動處理生命周期
界面組件只是觀察相關數據,不會停止或恢復觀察。LiveData 將自動管理所有這些操作,因為它在觀察時可以感知相關的生命周期狀態變化。
數據始終保持最新狀態
如果生命周期變為非活躍狀態,它會在再次變為活躍狀態時接收最新的數據。例如,曾經在后台的 Activity 會在返回前台后立即接收最新的數據。
適當的配置更改
如果由於配置更改(如設備旋轉)而重新創建了 Activity 或 Fragment,它會立即接收最新的可用數據。
共享資源
您可以使用單一實例模式擴展 LiveData 對象以封裝系統服務,以便在應用中共享它們。LiveData 對象連接到系統服務一次,然后需要相應資源的任何觀察者只需觀察 LiveData 對象。有關詳情,請參閱擴展 LiveData。
實現LiveData
package com.zyb.livedatatest;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
public class MyViewModel extends ViewModel {
private MutableLiveData<Integer> num;
public MutableLiveData<Integer> getNum() {
if(num == null){
num = new MutableLiveData<>();
num.setValue(0);
}
return num;
}
public MutableLiveData<Integer> addNum(int n){
num.setValue(num.getValue()+n);
return num;
}
}
從 Activity 訪問該列表,如下所示:
package com.zyb.livedatatest;
import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModel;
import androidx.lifecycle.ViewModelProviders;
import android.os.Bundle;
import android.view.View;
import android.widget.ImageButton;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
ImageButton redButton,blueButton;
TextView score;
MyViewModel myViewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
redButton = findViewById(R.id.imageButton);
blueButton = findViewById(R.id.imageButton3);
score = findViewById(R.id.textView);
myViewModel = ViewModelProviders.of(this).get(MyViewModel.class);
//觀察數據是否改變,改變就重寫
myViewModel.getNum().observe(this, new Observer<Integer>() {
@Override
public void onChanged(Integer integer) {
score.setText(integer+"");
}
});
redButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
myViewModel.addNum(1);
}
});
blueButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
myViewModel.addNum(-1);
}
});
}
}
ui:
功能:左邊+1,右邊-1
優點:將setText()減少到一句
缺點:setOnClickListener出現過多代碼不好看
DataBinding
在buid.gradle開啟數據綁定:
之后重新構建項目sync
進入activity_main.xml中點擊
之后會多兩個標簽
package com.zyb.databindingtest;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
public class MyViewModel extends ViewModel {
MutableLiveData<Integer> cnt;
public MutableLiveData<Integer> getCnt() {
if(cnt == null){
cnt = new MutableLiveData<>();
cnt.setValue(0);
}
return cnt;
}
public void add(int n){
cnt.setValue(cnt.getValue()+n);
}
}
MainActivity.java
package com.zyb.databindingtest;
import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;
import androidx.lifecycle.ViewModel;
import androidx.lifecycle.ViewModelProvider;
import androidx.lifecycle.ViewModelProviders;
import android.os.Bundle;
import com.zyb.databindingtest.databinding.ActivityMainBinding;
public class MainActivity extends AppCompatActivity {
//綁定1:將界面對象綁定到控制器的ActivityMainBinding對象中(可以調用bind.xml標簽名)
ActivityMainBinding bind;//注意這個類的名字和你Java文件的名字相同,不是固定的
MyViewModel myViewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
bind = DataBindingUtil.setContentView(this,R.layout.activity_main);
myViewModel = ViewModelProviders.of(this).get(MyViewModel.class);
//綁定2:將屏幕數據和控件的xml綁定
bind.setData1(myViewModel);//setData1是因為在xml那個數據的name自己設定的data1,所以這setData1也是不固定的
//這句很重要,相當於之前設置的觀察者語句
bind.setLifecycleOwner(this);
}
}
ui:
功能:顯示點擊按鈕次數
優點:控制器代碼減少,分工更明確
activity_main.xml變化:這個寫法有點像javaweb的模板引擎thymeleaf
最后的
thymeleaf相關圖:
相關變化圖:
最原始的,通過findViewById將單個組件的引用和控制器來連接起來,數據顯示和變化都在控制器中
加入ViewModel之后將頁面數據(UIData)單獨提出來封裝
加入LiveData,將之前提出來的UIData換為LiveData類型的,因為它滿足觀察者類型,可以減少setText的書寫,降低代碼耦合性
加入DataBinding將數據操作綁定到相關界面的xml文件中,減少控制器的代碼量,分工更明確了
ui