1.MVC,MVP,MVVVM
1.1什么是MVVM
1.MVVM,是Model-View-ViewModel的簡寫,是M-V-VM三部分組成。它本質上就是MVC 的改進 版。MVVM 就是將其中的View 的狀態和行為抽象化,其中ViewModel將視圖 UI 和業務邏輯分 開,它可以取出 Model 的數據同時幫忙處理 View 中由于需要展示內容而涉及的業務邏輯。
2.MVVM采用雙向數據綁定,view中數據變化將自動反映到viewmodel上,反之,model中數據變化 也將會自動展示在頁面上。把Model和View關聯起來的就是ViewModel。ViewModel負責把Model 的數據同步到View顯示出來,還負責把View的修改同步回Model。
3.MVVM核心思想,是關注model的變化,讓MVVM框架利用自己的機制自動更新DOM,也就是所 謂的數據-視圖分離,數據不會影響視圖。
如圖(重點):
1.2 Android MVVM
MVVM分為Model,View,ViewModel 三個部分
Model:數據層,包含數據實體和對數據實體的操作,和MVP的model沒有區別。
View: 界面層,對應于Activity,XML,負責數據顯示以及用戶交互。相比MVP的view,這里面的 view視圖數據一般是在xml中使用DataBinding進來雙向綁定數據的。
ViewModel:關聯層,作為中間橋梁 去通知model數據層處理數據業務,并將結果回調給 UI 層處 理 UI 邏輯。ViewModel中只有activity持有vm引用,vm是不持有view的引用的,所以vm的構造方 法中不能傳入視圖相關的對象。所以重點在于怎么通知view,可以通過觀察者回調的方式。但是現 在一般是結合Jetpack來進行view的更新的。
2.MVVM項目框架
如圖:
3 MVVM核心構成
3.1 DataBinding (重點)(詳細講解)
數據和UI雙向綁定
3.2 LifeCycle
界面生命周期感知
3.3 ViewModel
業務邏輯
3.4 LiveData
可觀察數據項
3.5 MVVM項目框架搭建 通用性的快速開發框架 可應用各項目中
4 DataBinding
4.1 DataBinding的簡介
1.Data binding 在2015年7月發布的android Studio v1.3.0 版本上引入,在2016年4月Android Studio v2.0.0 上正式支持。目前為止,Data Binding 已經支持雙向綁定了,實在2016年的google I/O大會上發布的。現在已經很普及啦,在項目中也在慢慢使用。
2.I/O大會上發布的。現在已經很普及啦,在項目中也在慢慢使用。 Databinding 是一個實現數據和UI綁定的框架,是一個實現 MVVM 模式的工具,有了 Data Binding,在Android中也可以很方便的實現MVVM開發模式。會JAVA web開發的會更好的理解在 xml中綁定數據的模式,在web開發中也是使用@{}來實現數據的顯示的。
3.Data Binding 是一個support庫,最低支持到Android 2.1(API Level 7+)。使用 DataBing,Gradle的Android Plugin需要在1.5.0-alpha1以上。
4.Data Binding 之前我們不可避免地要編寫大量的毫無營養的代碼,如 findViewById()、 setText(),setVisibility(),setEnabled() 或 setOnClickListener() 等,通過 Data Binding , 我們可 以通過聲明式布局以精簡的代碼來綁定應用程序邏輯和布局,這樣就不用編寫大量的毫無營養的代 碼了。
缺點:
1. ViewModel與View一一對應;
2. 使用起來靈活性比較低;
3. Model屬性發生變化時,ViewDatabinding采用異步更新數據,對于現實大量數據的ListView,會 有一定延遲,在實踐測試中發現,Databing效率較低,對于負責的界面不太適用;
4. 自動生成大量代碼和屬性字段:ViewDataBinding 實現類 DataBinderMApper 等。
4.2使用DataBinding
具體步驟:
1.構建環境(Build Environment)
在build.gradle中添加如下代碼:
Java
dataBinding {
enabled = true
}
2. 數據綁定布局文件
數據綁定的布局文件和我們以前經常寫的布局文件稍有不同,并從布局的根標記開始,后面依次是數據 元素和視圖根元素,即根布局是 layout,接下來是 data 節點,variable 節點,示例如下:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="user"
type="com.hp.mvvmdemo.model.User" />
<variable
name="handler"
type="com.hp.mvvmdemo.view.activity.MainActivity.Handler" />
</data>
<LinearLayout
android:gravity="center"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/tv_show"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!" />
<TextView
android:layout_marginTop="30dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="20dp"
android:text="@{user.username}" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="20dp"
android:text="@{user.password}" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="@{()->handler.onClick()}"/>
</LinearLayout>
</layout>
3.在MainActivity.java中獲取bingding對象
Java
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//setContentView(R.layout.activity_main);
ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
}
4.4databinding點擊事件
xml:
activity:
5.單向綁定
5.1單個值的綁定:
1.集成DataDinding 添加 dataBinding.enabled true 和 dataBinding { enabled = true }
Java
android {
....
dataBinding {
enabled = true
}
}
2.實現xml:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="title"
type="java.lang.String" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:textColor="#000000"
android:id="@+id/tvTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{title}"/>
</LinearLayout>
</layout>
5.2對象的綁定:
1.xml:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable name="user" type="com.example.User"/>
</data>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.firstName}"/>
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.lastName}"/>
</LinearLayout>
</layout>
2.實體類:
Java
public class User {
private final String firstName;
private final String lastName;
public User(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public String getFirstName() {
return this.firstName;
}
public String getLastName() {
return this.lastName;
}
}
3.databinding:
Java
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
MainActivityBinding binding = DataBindingUtil.setContentView(this,
R.layout.main_activity);
User user = new User("Test", "User");
binding.setUser(user);
}
6.雙向綁定
雙向綁定是指其中任意一個變化后都會同步更新到另一個。雙向綁定使用@={}表達式來實現 4.5.1 目前已經支持雙向綁定的列表
7.從網頁獲取圖片
整體架構:
導入要使用的依賴:
build.gradle
Java
implementation 'com.squareup.picasso:picasso:2.71828'
//for rxjava
implementation 'io.reactivex.rxjava2:rxjava:2.2.6'
//for rxandroid
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
//銜接 Retrofit & RxJava,此處一定要注意使用RxJava2的版本
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.5.0'
//添加Retrofit依賴
implementation 'com.squareup.retrofit2:retrofit:2.5.0'
//添加Gson解析
implementation 'com.squareup.retrofit2:converter-gson:2.5.0'
//添加圖片加載庫依賴
implementation 'com.github.bumptech.glide:glide:4.12.0'
xml層:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="imgurl"
type="String" />
<variable
name="localurl"
type="int" />
<variable
name="mainviewmodel"
type="com.hp.day48stage01.viewmodel.MainViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".view.activity.MainActivity">
<ImageView
app.NETImage="@{imgurl}"
app:localImage="@{localurl}"
android:id="@+id/imageView"
android:layout_width="300dp"
android:layout_height="300dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_launcher_background" />
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="64dp"
android:text="點擊見美女"
android:onClick="@{()->mainviewmodel.onChange()}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="@+id/imageView"
app:layout_constraintHorizontal_bias="0.495"
app:layout_constraintStart_toStartOf="@+id/imageView"
app:layout_constraintTop_toBottomOf="@+id/imageView"
app:layout_constraintVertical_bias="0.029" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
編寫實體類:
ModelGril.java
Java
public class ModelGirl {
private boolean success;
private String imgurl;
private Info info;
@Override
public String toString() {
return "MobileGirl{" +
"success=" + success +
", imgurl='" + imgurl + ''' +
", info=" + info +
'}';
}
public boolean isSuccess() {
return success;
}
public void setSuccess(boolean success) {
this.success = success;
}
public String getImgurl() {
return imgurl;
}
public void setImgurl(String imgurl) {
this.imgurl = imgurl;
}
public Info getInfo() {
return info;
}
public void setInfo(Info info) {
this.info = info;
}
public static class Info{
private int width;
private int height;
private String type;
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
}
編寫網絡接口:
Java
public interface GetImage_Interface {
@GET("api/mobil.girl?type=json")
Observable<ModelGirl> getPic();
}
適配器:
Java
public class ImageBindingAdapter {
@BindingAdapter(value = {"netImage","localImage"},requireAll = false)
public static void setImage(ImageView imageView,String url,int path){
if (url != null && !"".equals(imageView)) {
/*Glide.with(imageView.getContext())
.load(url)
.override(300,300)
.centerCrop()
.into(imageView);*/
Picasso.get().load(url).placeholder(R.mipmap.jiazai).into(imageView);
}else {
imageView.setImageResource(path);
}
}
}
MainViewModelJava
public class MainViewModel {
private String imgurl;
private ModelGirl modelGirl;
private ActivityMainBinding binding;
private final String TAG = "MainViewModel";
public ModelGirl getModelGirl() {
return modelGirl;
}
public void setModelGirl(ModelGirl modelGirl) {
this.modelGirl = modelGirl;
}
public MainViewModel() {
}
public MainViewModel(ActivityMainBinding binding,String imgurl) {
this.binding = binding;
this.imgurl = imgurl;
initGirl();
}
private void initGirl() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.vvhan.com/")
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
GetImage_Interface request = retrofit.create(GetImage_Interface.class);
Observable<ModelGirl> observable = request.getPic();
observable.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<ModelGirl>() {
@Override
public void accept(ModelGirl modelGirl) throws Exception {
Log.i(TAG,"連接成功");
//Log.i(TAG, modelGirl.toString());
imgurl = modelGirl.getImgurl();
binding.setImgurl(imgurl);
}
}, new Consumer<Throwable>() {
@Override
public void accept(Throwable throwable) throws Exception {
Log.i(TAG,"連接失敗");
}
});
}
public void onChange(){
initGirl();
}
}
MainActivity:Java
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityMainBinding binding = DataBindingUtil.setContentView(this,R.layout.activity_main);
binding.setMainviewmodel(new MainViewModel(binding,""));
}
}