隨着5G技術的流行,MVVM一定會大火起來。支持5G的手機肯定有更好的性能。而MVVM恰好對性能要求很高,MVVM內存消耗大,但開發速度爲各種模式之最。
M是model,V是View,VM是viewmodle,MVVM有一個雙向綁定的功能,在view和model之間,有一個叫做databinding的組件,這個組件負責把View和modle綁定。是一種數據驅動的形式,對應的modle層數據發生變化,view層馬上發生變化。比如modle層變成了1,那麼view層馬上變成1,modle層變成2,那麼View層馬上變成2,數據驅動UI模式在電腦上用的很多,目前手機上用的還不多。
開始擼代碼
第一部分
bean類
package com.example.testmvvm;
//這個類就相當於ViewModel
public class User {
private String name;
private String password;
public User(String name, String password) {
this.name = name;
this.password = password;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
佈局代碼
<?xml version="1.0" encoding="utf-8"?>
<!--與我們以前的佈局有些不一樣,這是我們MVVVM的佈局-->
<layout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
>
<data>
<!--此處定義該佈局要用到的數據的名字和類型-->
<!--name是自定義的,type是我們的bean類-->
<variable
name="abc"
type="com.example.testmvvm.User" />
</data>
<!--這裏定義我們的佈局-->
<LinearLayout
android:layout_width="match_parent"
android:orientation="vertical"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:text="@{`姓名:`+abc.name}"
android:layout_height="wrap_content"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{`密碼`+abc.password}"/>
</LinearLayout>
</layout>
gradle的配置
apply plugin: 'com.android.application'
android {
compileSdkVersion 29
buildToolsVersion "29.0.2"
//build.gradle的配置
dataBinding{
enabled true
}
defaultConfig {
applicationId "com.example.testmvvm"
minSdkVersion 15
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
//加載圖片的庫
implementation 'com.squareup.picasso:picasso:2.5.2'
implementation 'com.squareup.okhttp3:okhttp:3.10.0'
}
MainActivity代碼
package com.example.testmvvm;
import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;
import android.os.Bundle;
import com.example.testmvvm.databinding.ActivityMainBinding;
public class MainActivity extends AppCompatActivity {
User user;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// setContentView(R.layout.activity_main);
//DataBindingUtil編譯出來的類,不再用setContentView加載佈局
ActivityMainBinding binding = DataBindingUtil.setContentView(this,R.layout.activity_main);
user = new User("zhang_xin","123");
//user已經到UI上去了
binding.setAbc(user);
}
}
看下運行效果
第二部分
大家不要離開,接下來還有更牛叉的。現在我們時時更新數據,我們在User類中進行修改
package com.example.testmvvm;
import androidx.databinding.BaseObservable;
import androidx.databinding.Bindable;
//這個類就相當於ViewModel
public class User extends BaseObservable {
private String name;
private String password;
public User(String name, String password) {
this.name = name;
this.password = password;
}
@Bindable
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
//更新數據,該方法需要一個ID,這個ID是什麼呢?
//我們自己寫的ViewModel裏面的每個成員,會生成一個BR的文件,把ID註冊
//到裏面,類似我們的R文件
notifyPropertyChanged(BR.name);
}
@Bindable
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
//通知UI更新的動作
notifyPropertyChanged(BR.password);
}
}
修改MainActivity
package com.example.testmvvm;
import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;
import android.os.Bundle;
import android.os.Handler;
import com.example.testmvvm.databinding.ActivityMainBinding;
public class MainActivity extends AppCompatActivity {
User user;
//延遲更新
Handler handler = new Handler();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// setContentView(R.layout.activity_main);
//DataBindingUtil編譯出來的類,不再用setContentView加載佈局
ActivityMainBinding binding = DataBindingUtil.setContentView(this,R.layout.activity_main);
user = new User("zhang_xin","123");
//user已經到UI上去了
binding.setAbc(user);
//5秒後更新數據
handler.postDelayed(new Runnable() {
@Override
public void run() {
user.setName("aaaaaaaaaaa");
user.setPassword("22222222222222");
}
},5000);
}
}
運行會發現,界面在5秒之後會發生改變
數據發生變化的時候會直接返回到我們UI上
第三部分
我們現在要加載圖片,看看怎麼加載圖片
先看下xml文件的修改
<?xml version="1.0" encoding="utf-8"?>
<!--與我們以前的佈局有些不一樣,這是我們MVVVM的佈局-->
<layout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
>
<data>
<!--此處定義該佈局要用到的數據的名字和類型-->
<!--name是自定義的,type是我們的bean類-->
<variable
name="abc"
type="com.example.testmvvm.User" />
</data>
<!--這裏定義我們的佈局-->
<LinearLayout
android:layout_width="match_parent"
android:orientation="vertical"
android:layout_height="wrap_content">
<!--這裏需要自定義屬性,這裏要和User關聯-->
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:headImage = "@{abc.headImage}"
/>
<TextView
android:layout_width="wrap_content"
android:text="@{`姓名:`+abc.name}"
android:layout_height="wrap_content"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{`密碼`+abc.password}"/>
</LinearLayout>
</layout>
再看下User類
package com.example.testmvvm;
import android.widget.ImageView;
import androidx.databinding.BaseObservable;
import androidx.databinding.Bindable;
import androidx.databinding.BindingAdapter;
import com.squareup.picasso.Picasso;
//這個類就相當於ViewModel
public class User extends BaseObservable {
private String name;
private String password;
//新增的屬性,可以填到xml文件<ImageView/>標籤中
private String headImage;
public User(String name, String password, String headImage) {
this.name = name;
this.password = password;
this.headImage = headImage;
}
public String getHeadImage() {
return headImage;
}
public void setHeadImage(String headImage) {
this.headImage = headImage;
}
@Bindable
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
//更新數據,該方法需要一個ID,這個ID是什麼呢?
//我們自己寫的ViewModel裏面的每個成員,會生成一個BR的文件,把ID註冊
//到裏面,類似我們的R文件
notifyPropertyChanged(BR.name);
}
@Bindable
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
//通知UI更新的動作
notifyPropertyChanged(BR.password);
}
//自定義屬性:提供一個靜態方法,注意必須是靜態方法,來加載headImage
//bind後面就是屬性的名字
@BindingAdapter("bind:headImage")
public static void getImaage(ImageView view, String url) {
//view就通過bind 綁定到了app:headImage = ""屬性,而@{abc.headImage}就相當於這裏的url
Picasso.with(view.getContext()).load(url).into(view);//加載圖片
}
}
看下ManiActivity類
package com.example.testmvvm;
import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;
import android.os.Bundle;
import android.os.Handler;
import com.example.testmvvm.databinding.ActivityMainBinding;
public class MainActivity extends AppCompatActivity {
User user;
//延遲更新
Handler handler = new Handler();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// setContentView(R.layout.activity_main);
//DataBindingUtil編譯出來的類,不再用setContentView加載佈局
ActivityMainBinding binding = DataBindingUtil.setContentView(this,R.layout.activity_main);
user = new User("zhang_xin","123","https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1573721346897&di=a236e053ff5f8b91f2c97964ccadb336&imgtype=0&src=http%3A%2F%2F5b0988e595225.cdn.sohucs.com%2Fq_70%2Cc_zoom%2Cw_640%2Fimages%2F20191017%2F8fd83a2e2b694ba2bcbeabdcf21f0510.jpeg");
//user已經到UI上去了
binding.setAbc(user);
//5秒後更新數據
handler.postDelayed(new Runnable() {
@Override
public void run() {
user.setName("aaaaaaaaaaa");
user.setPassword("22222222222222");
}
},5000);
}
}
看下運行效果
第四部分
圖片也可以實時更新,我們修改下代碼
package com.example.testmvvm;
import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;
import android.os.Bundle;
import android.os.Handler;
import com.example.testmvvm.databinding.ActivityMainBinding;
public class MainActivity extends AppCompatActivity {
User user;
Handler handler = new Handler();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityMainBinding binding = DataBindingUtil.setContentView(this,R.layout.activity_main);
user = new User("zhang_xin","123","https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1573723095492&di=e831e4354d4923542cdabf97cef383a6&imgtype=0&src=http%3A%2F%2Fi1.sinaimg.cn%2Fent%2Fd%2F2008-06-04%2FU105P28T3D2048907F326DT20080604225106.jpg");
binding.setAbc(user);
handler.postDelayed(new Runnable() {
@Override
public void run() {
user.setName("aaaaaaaaaaa");
user.setPassword("22222222222222");
//添加這段代碼
user.setHeadImage("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1573721346897&di=a236e053ff5f8b91f2c97964ccadb336&imgtype=0&src=http%3A%2F%2F5b0988e595225.cdn.sohucs.com%2Fq_70%2Cc_zoom%2Cw_640%2Fimages%2F20191017%2F8fd83a2e2b694ba2bcbeabdcf21f0510.jpeg");
}
},5000);
}
}
修改User類
package com.example.testmvvm;
import android.widget.ImageView;
import androidx.databinding.BaseObservable;
import androidx.databinding.Bindable;
import androidx.databinding.BindingAdapter;
import com.squareup.picasso.Picasso;
//這個類就相當於ViewModel
public class User extends BaseObservable {
private String name;
private String password;
//新增的屬性,可以填到xml文件<ImageView/>標籤中
private String headImage;
public User(String name, String password, String headImage) {
this.name = name;
this.password = password;
this.headImage = headImage;
}
@Bindable
public String getHeadImage() {
return headImage;
}
public void setHeadImage(String headImage) {
this.headImage = headImage;
notifyPropertyChanged(BR.headImage);
}
@Bindable
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
//更新數據,該方法需要一個ID,這個ID是什麼呢?
//我們自己寫的ViewModel裏面的每個成員,會生成一個BR的文件,把ID註冊
//到裏面,類似我們的R文件
notifyPropertyChanged(BR.name);
}
@Bindable
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
//通知UI更新的動作
notifyPropertyChanged(BR.password);
}
//自定義屬性:提供一個靜態方法,注意必須是靜態方法,來加載headImage
//bind後面就是屬性的名字
@BindingAdapter("bind:headImage")
public static void getImaage(ImageView view, String url) {
//view就通過bind 綁定到了app:headImage = ""屬性,而@{abc.headImage}就相當於這裏的url
Picasso.with(view.getContext()).load(url).into(view);//加載圖片
}
}
第五部分
使用ListView
看下佈局和listitem的佈局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:id="@+id/listView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<!--與我們以前的佈局有些不一樣,這是我們MVVVM的佈局-->
<layout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
>
<data>
<!--此處定義該佈局要用到的數據的名字和類型-->
<!--name是自定義的,type是我們的bean類-->
<variable
name="abc"
type="com.example.testmvvm.User" />
</data>
<!--這裏定義我們的佈局-->
<LinearLayout
android:layout_width="match_parent"
android:orientation="horizontal"
android:layout_height="wrap_content">
<!--這裏需要自定義屬性,這裏要和User關聯-->
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:headImage = "@{abc.headImage}"
/>
<TextView
android:layout_width="wrap_content"
android:text="@{`姓名:`+abc.name}"
android:layout_height="wrap_content"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{`密碼`+abc.password}"/>
</LinearLayout>
</layout>
看下user類
package com.example.testmvvm;
import android.widget.ImageView;
import androidx.databinding.BaseObservable;
import androidx.databinding.Bindable;
import androidx.databinding.BindingAdapter;
import com.squareup.picasso.Picasso;
//這個類就相當於ViewModel
public class User extends BaseObservable {
private String name;
private String password;
//新增的屬性,可以填到xml文件<ImageView/>標籤中
private String headImage;
public User(String name, String password, String headImage) {
this.name = name;
this.password = password;
this.headImage = headImage;
}
@Bindable
public String getHeadImage() {
return headImage;
}
public void setHeadImage(String headImage) {
this.headImage = headImage;
notifyPropertyChanged(BR.headImage);
}
@Bindable
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
//更新數據,該方法需要一個ID,這個ID是什麼呢?
//我們自己寫的ViewModel裏面的每個成員,會生成一個BR的文件,把ID註冊
//到裏面,類似我們的R文件
notifyPropertyChanged(BR.name);
}
@Bindable
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
//通知UI更新的動作
notifyPropertyChanged(BR.password);
}
//自定義屬性:提供一個靜態方法,注意必須是靜態方法,來加載headImage
//bind後面就是屬性的名字
@BindingAdapter("bind:headImage")
public static void getImaage(ImageView view, String url) {
//view就通過bind 綁定到了app:headImage = ""屬性,而@{abc.headImage}就相當於這裏的url
Picasso.with(view.getContext()).load(url).into(view);//加載圖片
}
}
看下MainActivity類
package com.example.test;
import android.os.Bundle;
import android.widget.ListView;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import com.example.testmvvm.R;
import com.example.testmvvm.User;
import java.util.ArrayList;
import java.util.List;
/**
* @author writing
* @time 2019/11/14 15:09
* @note
*/
public class MainActivity extends AppCompatActivity {
ListView listView;
private List<User> users;
private String url = "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1573721346897&di=a236e053ff5f8b91f2c97964ccadb336&imgtype=0&src=http%3A%2F%2F5b0988e595225.cdn.sohucs.com%2Fq_70%2Cc_zoom%2Cw_640%2Fimages%2F20191017%2F8fd83a2e2b694ba2bcbeabdcf21f0510.jpeg";
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_layout);
users = new ArrayList<>();
//核心是適配器,ListView就直接使用findViewById了
listView = findViewById(R.id.listView);
users.add(new User( "zhang_xin1","123456",url));
users.add(new User( "zhang_xin2","123456",url));
users.add(new User( "zhang_xin3","123456",url));
users.add(new User( "zhang_xin4","123456",url));
listView.setAdapter(new CommonAdapter<User>(this,getLayoutInflater(),R.layout.item, com.example.testmvvm.BR.abc,users));
}
}
看下CommonAdapter類
package com.example.test;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import androidx.databinding.DataBindingUtil;
import androidx.databinding.ViewDataBinding;
import java.util.List;
/**
* 寫一個公共適配器,裏面放的數據用泛型
*/
public class CommonAdapter<T> extends BaseAdapter {
private int layoutId;//佈局ID
/**
* 與佈局中的這個屬性有關
* <variable
* name="abc"
* type="com.example.testmvvm.User" />
*/
private int variableId;//會自動生成
private Context context;
private LayoutInflater layoutInflater;
//需要添加的數據
private List<T> list;
public CommonAdapter(Context context, LayoutInflater inflater, int layoutId, int variableId, List<T> list) {
this.context = context;
this.layoutInflater = inflater;
this.layoutId = layoutId;
this.variableId = variableId;
this.list = list;
}
@Override
public int getCount() {
return list.size();
}
@Override
public Object getItem(int position) {
return list.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
//進行綁定動作
ViewDataBinding viewDataBinding;
if(convertView==null){
//解析文件
viewDataBinding = DataBindingUtil.inflate(layoutInflater,layoutId,parent,false);
}else{
//有數據就重用
viewDataBinding = DataBindingUtil.getBinding(convertView);
}
viewDataBinding.setVariable(variableId,list.get(position));
return viewDataBinding.getRoot().getRootView();
}
}
看下運行效果
第六部分增加點擊事件
修改User類增加點擊事件
public void click(View view) {
Toast.makeText(view.getContext(), "點擊了", Toast.LENGTH_LONG).show();
}
修改listitem的佈局中< ImageView />標籤
<ImageView
android:onClick="@{abc.click}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:headImage = "@{abc.headImage}"
/>