文章目录

  • mvvm简介
  • mvvm基本使用方式
  • 设置 ImageView
  • 使用 ListView


mvvm简介

  • MVVM是Model-View-ViewModel的简写。它本质上就是MVC 的改进版。MVVM 就是将其中的View 的状态和行为抽象化,让我们将视图 UI 和业务逻辑分开。当然这些事 ViewModel 已经帮我们做了,它可以取出 Model 的数据同时帮忙处理 View 中由于需要展示内容而涉及的业务逻辑。与MVC相比它减少了很多维护的成本,也大大提高的开发速度。但是由于mvvm由于生成大量的副本,且数据双向绑定同步需要开启线程,所以对内存的要求相比 mvc和mvp是要大很多的,不过随着手机内存的不断升高,消耗的性能基本会慢慢忽略不计。前端的一些框架比如vue等已经很完善的应用了mvvm架构。

mvvm基本使用方式

  • 在 app gradle 的 android{} 下开启 dataBinding 的使用
dataBinding{
	enabled = true
}
  • 在使用的布局中顶层使用 layout 标签,然后在内部设置 data 数据源,然后才是我们正常构建的布局。代码如下:
    其中 data 是设置数据源,<variable 标签里面设置的name是内部调用的方式,type对应相应的model类。我这里是新建了一个 User 类,里面有name 和 password 两个属性。具体用法看代码
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
	<!-- 设置数据源 data中可以添加多个数据源 -->
    <data>
        <variable
            name="user"
            type="com.example.myapplication.User" />
    </data>
	<!-- 我们自己的布局 -->
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
		<!-- 
         数据设置采用上面设置的name.属性的方式
         这里是 user.name 
         如果想拼接字符串使用的是键盘左上角数字1旁边的那个符号
         "@{`拼接字符串`+user.name}"
         -->
        <TextView
            android:id="@+id/tv"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@{`姓名:`+user.name}" /> 
		
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@{`密码:`+user.password}" />

        <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:onClick="click"
            android:text="click" />

    </LinearLayout>
</layout>
  • 在Activity中使用方式
public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";
    private TextView tv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 通过 DataBindingUtil 的 setContentView 方法替代 activity 的 setContentView 方法,返回 ActivityMainBinding 
        ActivityMainBinding activityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        tv = findViewById(R.id.tv);
        User user = new User("张三", "123456");
        // 通过 ActivityMainBinding 将数据源和view绑定
        activityMainBinding.setUser(user);
    }

    public void click(View view) {
        // empty
    }
    
}

运行效果就是 User 的信息正确展示到的布局上。

android 搭建mvvm框架java android从零搭建mvvm_User

  • 那么mvvm数据变化了怎么同步更新UI呢。需要在model中修改一下
/**
 * User 实体类
 * mvvm 绑定的步骤如下:
 * <p>
 * 1.User类继承被观察者的一个类 BaseObservable androidx.databinding包下面的一个类(我是使用的androidx)
 * 2.get方法添加  @Bindable 注解,这是一个运行时注解。
 * 3.在需要同步属性的set方法中设置对应的值 如setName中:notifyPropertyChanged(BR.name); 其中 BR是编译时生成的一个类,没有的话可以rebuild一下
 * 这样一个基本的数据绑定就结束了
 */
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;
        notifyPropertyChanged(BR.name);
    }

    @Bindable
    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
        notifyPropertyChanged(BR.password);
    }
}

此时我在点击事件重新设置User属性时,UI上的展示也随之变化。

设置 ImageView

  • 如何设置 ImageVIew 图片呢?
    首先在User中添加 header 属性代表图片地址。
public class User extends BaseObservable {

    private String header;
    
    public String getHeader() {
        return header;
    }

    public void setHeader(String header) {
        this.header = header;
    }

   
    //自定义属性  headUrl 是自定义的,在xml的imageView中引用
    @BindingAdapter("headUrl")
    public static void getImage(ImageView view, String url) {
        Glide.with(view.getContext()).load(url).into(view);
    }
}

xml中引用如下:
headUrl 为model中自己定义的属性 user.header为返回的数据

<ImageView
	android:layout_width="200dp"
	android:layout_height="500dp"
	app:headUrl="@{user.header}" />

使用 ListView

  • 下面是适配器代码
/**
 * 通用适配器
 * @param <T>
 */
public class MyListAdapter<T> extends BaseAdapter {
    private Context context;
    private List<T> lists;
    private LayoutInflater inflater;
    // 布局id
    private int layoutId;
    // 这是对应的 List的bean中生成的 variableId
    private int variableId;

    public MyListAdapter(Context context, List<T> lists, LayoutInflater inflater, int layoutId, int variableId) {
        this.context = context;
        this.lists = lists;
        this.inflater = inflater;
        this.layoutId = layoutId;
        this.variableId = variableId;
    }

    @Override
    public int getCount() {
        return lists.size();
    }

    @Override
    public Object getItem(int position) {
        return lists.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        ViewDataBinding dataBinding;
        if (convertView == null) {
            dataBinding = DataBindingUtil.inflate(inflater, layoutId, parent, false);
        } else {
            dataBinding = DataBindingUtil.getBinding(convertView);
        }
        dataBinding.setVariable(variableId, lists.get(position));
        return dataBinding.getRoot().getRootView();
    }
}
  • 下面item代码
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <data>

        <variable
            name="item"
            type="com.example.myapplication.User" />
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <ImageView
            android:layout_width="80dp"
            android:layout_height="160dp"
            app:headUrl="@{item.header}" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            android:layout_marginStart="10dp"
            android:text="@{item.name}"
            android:textSize="18sp" />

    </LinearLayout>
</layout>

下面是使用:

List<User> list = new ArrayList<>();
        list.add(new User("张三", "123456", "图片地址"));
        list.add(new User("李四", "123456", "图片地址"));
        list.add(new User("王五", "123456", "图片地址"));
        list.add(new User("赵六", "123456", "图片地址"));
        listView.setAdapter(new MyListAdapter<User>(this, list, getLayoutInflater(), R.layout.item, BR.item));
  • 点击事件添加方式如下:方式有几种 我这种只是其中一种
    在User中添加
// 	getiName就是User中的一个get方法。
    public void click(View view) {
        Toast.makeText(view.getContext(), getName(), Toast.LENGTH_SHORT).show();
    }

在item的xml对应控件的点击事件中添加,我是添加到了 ImageVIew中

<ImageView
            android:layout_width="80dp"
            android:layout_height="160dp"
            android:onClick="@{item.click}"
            app:headUrl="@{item.header}" />
  • 这样就可以展示出来ListView列表了。这么做ListView适配器可以使用一个,因为适配器中不关心 bean和view的关系。所以其他Listview可以直接引用。