Android入门第60天-MVVM中的Databinding与ListView结合使用

Posted TGITCIC

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android入门第60天-MVVM中的Databinding与ListView结合使用相关的知识,希望对你有一定的参考价值。

开篇

        还记得我们进入Listview、GridView都是以一个layout+adapter组合在一起来实现的是吧?那么还记得我们的Adapter的写法么?

        在我们的Adapter里提供了一个bindView方法 。

        在调用时我们需要在Activity里把layout里的控件元素一个个传给这个Adapter。

         在我们的例子里我们的layout里只有两个对象

        那么如果我们现在假设需要实现以下这样的界面(笔者正在制作的基于spring cloud2.0微服务+android MVVM开源商城,我做完后会把前后端都开源出来。mao-sir.com是笔者拥有的实名域名中的一个)。

        我们可以看到标记1处有两处图片、5处文字。

        再来看标记2处,有一个图片+两个文字。

        如果按照我们之前这个Adapter的写法,岂不是要为这两个layout各写2个Adapter吗?那么我们假设在整个app开发过程中有100个不同的layout呢?那岂不是要变成了Adapter泛滥了吗?

        答案就是:不用这么多Adapter,我们可以使用Databinding来解决大多数问题!

        下面我们就来看如何应对这样的场景。

基于ListView的Databinding

        假设我们需要完成以下这样的一个ListView。

         我们可以看到这样的一个ListView有两个元素构成了它的基本的每一行的Layout。

  • 一个TextView;
  • 一个ImageView;

我们直接来看代码。

代码

先定义一个继承自BaseObservable的User的JavaBean

User.java

package com.mkyuan.android.demo.mvvm;

import android.widget.ImageView;

import androidx.databinding.BaseObservable;
import androidx.databinding.Bindable;
import androidx.databinding.BindingAdapter;

import com.bumptech.glide.Glide;

public class User extends BaseObservable 
    public User(String name, int header) 
        this.name = name;
        this.header = header;
    

    private String name = "";
    private int header;

    @Bindable
    public String getName() 
        return name;
    

    public void setName(String name) 
        this.name = name;
        notifyPropertyChanged(BR.name);
    

    @Bindable
    public int getHeader() 
        return header;
    

    public void setHeader(int header) 
        this.header = header;
        notifyPropertyChanged(BR.header);
    

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

        我们可以看到在这个JavaBean里我们依然使用了Glid工具把图片显示到界面上。

dependencies 
    implementation 'com.github.bumptech.glide:glide:4.9.0'
    annotationProcessor 'com.github.bumptech.glide:compiler:4.9.0'
    implementation 'androidx.appcompat:appcompat:1.4.1'
    implementation 'com.google.android.material:material:1.5.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
    testImplementation 'junit:junit:4.13.2'
    androidTestImplementation 'androidx.test.ext:junit:1.1.3'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'

定义一个显示在ListiView里每一行用的基本元素的Layout

list_item.xml

<?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中可以添加多个数据源 -->
    <data>

        <variable
            name="user"
            type="com.mkyuan.android.demo.mvvm.User" />
    </data>

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

        <TextView
            android:id="@+id/tv"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@`姓名:`+user.name" />

        <ImageView
            android:layout_width="60dp"
            android:layout_height="100dp"
            android:adjustViewBounds="true"
            app:headId="@user.header" />

    </LinearLayout>
</layout>

 在这个layout里我们使用了databinding。

定义一个基于Databinding的通用Adapter

MyListAdapter.java

package com.mkyuan.android.demo.mvvm;

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 MyListAdapter <T> extends BaseAdapter 
    private Context context;
    private LayoutInflater inflater;
    private int layoutId;
    private int variableId;
    private List<T> list;

    public MyListAdapter(Context context, int layoutId, List<T> list, int resId) 
        this.context = context;
        this.layoutId = layoutId;
        this.list = list;
        this.variableId = resId;
        inflater = LayoutInflater.from(context);
    


    @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 dataBinding;
        if (convertView == null) 
            dataBinding = DataBindingUtil.inflate(inflater, layoutId, parent, false);
         else 
            dataBinding = DataBindingUtil.getBinding(convertView);
        
        dataBinding.setVariable(variableId, list.get(position));
        return dataBinding.getRoot().getRootView();
    

         从代码上看,我们可以看出这个Adapter才是一个“级极Adapter”,所有代码里没有一点和控件元素、多少个控件有关的语句。说白了就是关你多少个控件。。。只要你把layout传进去都可以dataBinding起来。因此这才是一个通用的Adapter,如果没有什么特殊的情况这个Adapter在我们的项目里往往承担着超过90%左右的界面显示的那些活。

在Activity里如何结合layout和adapter

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <ListView
        android:id="@+id/listView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>

ActivityMain.java

package com.mkyuan.android.demo.mvvm;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.widget.ListView;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity 
    private static final String TAG = "DemoMVVMList";
    private ListView listView;

    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        listView = (ListView) findViewById(R.id.listView);
        List<User> list = new ArrayList<User>();
        list.add(new User("张三", R.drawable.icon_1_128));
        list.add(new User("李四", R.drawable.icon_2_128));
        list.add(new User("王五", R.drawable.icon_3_128));
        list.add(new User("赵六", R.drawable.icon_4_128));
        list.add(new User("孙七", R.drawable.icon_5_128));
        list.add(new User("周八", R.drawable.icon_6_128));
        list.add(new User("吴九", R.drawable.icon_7_128));
        MyListAdapter<User> adapter = new MyListAdapter<User>(MainActivity.this,
                R.layout.list_items, list,
                BR.user);
        listView.setAdapter(adapter);


    

运行工程

  1. 记得在build.gradle里需要有dataBinding enabled=true;
  2. 记得在build.gradle里引入Glide组件的依赖;
  3. 记得gradle.properties里需要:userAdroidX=true以及android.enableJetifier=true;
  4. 记得工程要Migrate成AndroidX工程;

        然后运行起来就得到了我们需要的效果

        不妨自己动一下手吧。

 

以上是关于Android入门第60天-MVVM中的Databinding与ListView结合使用的主要内容,如果未能解决你的问题,请参考以下文章

Android入门第59天-进入MVVM

Android入门第59天-进入MVVM

Android入门第64天-MVVM下瀑布流界面的完美实现-使用RecyclerView

Android入门第62天-Glide显示网络图片高版本的使用

Android入门第65天-mvvm模式下的retrofit2+okhttp3+rxjava

Android入门第41天-Android中的Service(bindService)