王學崗移動架構43MVVM設計模式

隨着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}"
            />
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章