王学岗移动架构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}"
            />
发布了208 篇原创文章 · 获赞 15 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/qczg_wxg/article/details/103058696