DataBinding

半腔热情 提交于 2020-02-12 07:55:31

一、DataBinding

1.1 在Module的build.gradle android模块中添加如下配置

代码地址 https://github.com/MichealPan9999/DataBinding-MVVM

android {
 dataBinding {
    enabled = true
 }
}
  Android Studio中是依靠gradle来管理项目的,在创建一个项目时,从开始创建一直到创建完毕,整个过程是需要执行很多个gradle task的,这些task有很多是系统预先帮我们定义好的,比如build task,clean task等,DataBinding相关的task也是系统预先帮我们定义好的,但是默认情况下,DataBinding相关的task在task列表中是没有的,因为我们没有开启dataBinding,但是一旦我们通过 dataBinding{enabled = true}的方式开启DataBinding之后,DataBinding相关的task就会出现在task列表中,每当我们执行编译之类的操作时,就会执行这些dataBinding Task, 这些task的作用就是检查并生成相关dataBinding代码,比如dataBindingExportBuildInfoDebug这个task就是用来导出debug模式下的build信息的。

1.2 创建一个简单的JavaBean对象

public class UserBean {
    private String name; //姓名
    private int age; //年龄

    public UserBean(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

1.3 使用了DataBinding之后的Activity的布局文件activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>

        <variable
            name="user"
            type="com.zx.databindingdemo.bean.UserBean" />
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{user.name}" />

        <!--注意:这里age是int类型,必须转化为String,否则会运行时异常-->
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{String.valueOf(user.age)}" />
    </LinearLayout>
</layout>

1.4 MainActivity 

public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        UserBean userBean = new UserBean ("张三", "25");
        binding.setUser(userBean );
    }
}
  这个activity很简洁,没有了控件的初始化的findViewById或者butterknife的那一堆注解,也没有了TextView的setText(),也就2行代码而已。大家应该已经看见了,这里用DataBindingUtil.setContentView代替了setContentView,然后创建一个 UserBean 对象,通过 binding.setUser(userBean) 与 variable 进行绑定。注意:这个ActivityMainBinding 是如何生成的呢?他是继承ViewDataBinding,这个类的生成是有规则的,它是根据对应的布局文件的名字生成的,比如:activity_main-->ActivityMainBinding 、fragment-->FragmentBinding即:第一个单词首字母大写,第二个单词首字母大写,最后都会拼上Binding就是生成的Binding类。
  ActivityMainBinding这个类其实是系统帮我们自动生成的。
  但是如果你在实际编写代码的过程中,你会发现并没有执行编译、运行之类等操作,ActivityMainBinding这个类就直接能用了,竟然还有这种操作?其实是Android Studio 这个IDE自动帮我们做了这一步,在默认情况下,系统会使用Android Studio为我们自动生成databinding相关的代码,但是这种方式生成的代码不能调试,如果你想通过点击ActivityMainBinding跳转到它的源码中,你会发现并不能如你所愿,而是会跳转到对应的布局文件中。那么如果我们确实要查看ActivityMainBinding的源码并且还想调试,我们就需要通过另外一种方式:手动编译代码。这两种方式可以通过Android Studio的设置面板修改。

1.5 引入一些高级变量variable

1.5.1 import导包及xml中添加运算

<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <data>

        <import type="java.util.List" />

        <import type="java.util.Map" />

        <import type="com.example.panzq.mvvm.bean.UserBean" />
        <import type="android.view.View" />

        <!--泛型的支持会在编译时期报红线,但是是可以直接运行的
       但是需要通过转义字符才行,如:<号用&lt表示;>号用&gt表示;-->
        <variable
            name="list"
            type="List&lt;String&gt;" />

        <variable
            name="map"
            type="Map&lt;String,Object&gt;" />

        <variable
            name="array"
            type="String[]" />

        <variable
            name="muser"
            type="UserBean" />
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="15dp"
        android:orientation="vertical">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{list[0]}" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{list.get(1)}" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:text="@{map[`key0`]}" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{map.get(`key1`)}" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:text="@{array[0]}" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{array[1]}" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{muser.name}"
            android:visibility="@{((muser.age > 18) ? View.GONE:View.VISIBLE)}" />

    </LinearLayout>
</layout>
ActivityMain2Binding binding = DataBindingUtil.setContentView(this, R.layout.activity_main2);
        List<String> list = new ArrayList<>();
        list.add("List1");
        list.add("List2");
        binding.setList(list);
        HashMap<String, Object> map = new HashMap<>();
        map.put("key0", "map_value0");
        map.put("key1", "map_value1");
        binding.setMap(map);

        String[] array = {"字符串1", "字符串2"};
        binding.setArray(array);

        UserBean userBean = new UserBean("张三",18);
        binding.setMuser(userBean);

 

list和map这里我没有用List<String>和Map<String,Object>,而是用的List<String>和Map<String,Object>原因是在data中,有些字符是必须用转义字符才能编译通过,上面把<>换成转义字符的写法虽然会在编译时是红色的,但是不用担心,会编译通过的,下面给出常用的转义字符。

附:常用的转义字符

显示结果描述转义字符十进制
  空格 &nbsp; &#160;
< 小于号 &lt; &#60;
> 大于号 &gt; &#62;
& 与号 &amp; &#38;
" 引号 &quot; &#34;
撇号 &apos; &#39;
× 乘号 &times; &#215;
÷ 除号 &divide; &#247;

1.5.2 别名

  如果我们import了两个不同路径,但名称相同的类,可以借助于别名来解决,别名借助alias字段来标识。

<import type="com.example.panzq.mvvm.bean.UserBean" />
<import type="com.example.panzq.mvvm.bean.UserBean" alias="UserBean2"/>
...
<variable
    name="muser"
    type="UserBean" />
<variable
    name="muser2"
    type="UserBean2" />
...
<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@{muser.name}"
    android:visibility="@{((muser.age > 18) ? View.GONE:View.VISIBLE)}" />
<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@{muser2.name}"
    android:visibility="@{((muser2.age > 18) ? View.GONE:View.VISIBLE)}" />
UserBean userBean = new UserBean("张三",18);
binding.setMuser(userBean);
UserBean userBean2 = new UserBean("李四",17);
binding.setMuser2(userBean2);

1.5.3 android:onClick事件处理

下面给出几种实现方式:

  • 布局中引入OnClickListener的变量
  • 方法调用
<variable
            name="clickListener"
            type="android.view.View.OnClickListener" />
...
<Button
            android:id="@+id/click_btn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="@{clickListener}"
            android:text="button" />
binding.setClickListener(this);
...
@Override
    public void onClick(View view) {

        switch (view.getId()) {
            case R.id.click_btn:
                Toast.makeText(Main2Activity.this, "点击了按钮", Toast.LENGTH_SHORT).show();
                break;
        }
    }

二 DataBinding & RecyclerView

2.1 处理找不到符号  RecyclerView问题 

 import android.support.v7.widget.RecyclerView;时提示找不到符号RecyclerView

2.1.1 配置aar方法

1. 找到sdk目录下的recyclerview-v7-****.aar文件,如:

D:\Program Files\androidstudio3\sdk2\extras\android\m2repository\com\android\support\recyclerview-v7\24.0.0\recyclerview-v7-24.0.0.aar

2. 将aar文件拷贝到项目的libs目录下build.gradle中加载aar文件

apply plugin: 'com.android.application'android {    compileSdkVersion 28    dataBinding {        enabled = true    }    defaultConfig {        applicationId "com.example.recyclerview"        minSdkVersion 22        targetSdkVersion 28        versionCode 1        versionName "1.0"        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"    }    buildTypes {        release {            minifyEnabled false            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'        }    }}repositories {    flatDir {        dirs 'libs'    }}dependencies {    implementation fileTree(dir: 'libs', include: ['*.jar'])    implementation 'com.android.support:appcompat-v7:28.+'    implementation 'com.android.support.constraint:constraint-layout:1.0.2'    testImplementation 'junit:junit:4.12'    androidTestImplementation 'com.android.support.test:runner:1.0.1'    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'    compile(name: 'recyclerview-v7-24.0.0', ext: 'aar')}

执行完Sync Now以后便可以正常导包

import android.support.v7.widget.RecyclerView;

2.2 Adapter 配置

BaseBindRecyclerViewAdapter

public abstract class BaseBindRecyclerViewAdapter<T> extends RecyclerView.Adapter {
    public List<T> mList; //数据源
    public LayoutInflater inflater;

    public BaseBindRecyclerViewAdapter(Context context, List<T> mList) {
        this.mList = mList;
        inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
        return onCreateMyViewHolder(viewGroup, i);
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int i) {
        onBindMyViewHolder(viewHolder, i);
    }

    @Override
    public int getItemCount() {
        return mList.size();
    }

    //获取Item布局
    public abstract RecyclerView.ViewHolder onCreateMyViewHolder(ViewGroup parent, int viewType);

    //绑定数据
    public abstract void onBindMyViewHolder(RecyclerView.ViewHolder holder, int position);
}

MutiItemAdapter

public class MultiItemAdapter extends BaseBindRecyclerViewAdapter<IBaseBindingAdapterItem> {
    public MultiItemAdapter(Context context, List<IBaseBindingAdapterItem> mList) {
        super(context, mList);
    }

    @Override
    public int getItemViewType(int position) {
        return mList.get(position).getItemViewType();
    }

    @Override
    public RecyclerView.ViewHolder onCreateMyViewHolder(ViewGroup parent, int viewType) {
        switch (viewType) {
            case R.layout.item_fruit:
                ItemFruitBinding itemFruitBinding = DataBindingUtil.inflate(inflater, R.layout.item_fruit, parent, false);
                return new FruitViewHolder(itemFruitBinding);
            case R.layout.item_text:
                ItemTextBinding itemTextBinding = DataBindingUtil.inflate(inflater, R.layout.item_text, parent, false);
                return new TextViewHolder(itemTextBinding);
            default:
                ItemFruitBinding binding = DataBindingUtil.inflate(inflater, R.layout.item_fruit, parent, false);
                return new FruitViewHolder(binding);
        }
    }

    @Override
    public void onBindMyViewHolder(RecyclerView.ViewHolder holder, int position) {
        if (holder instanceof FruitViewHolder) {
            FruitItem fruitBean = (FruitItem) mList.get(position);
            ((FruitViewHolder) holder).getBinding().setItem(fruitBean);
            ((FruitViewHolder) holder).getBinding().executePendingBindings(); //解决databinding闪烁问题
        } else if (holder instanceof TextViewHolder) {
            TextItem textBean = (TextItem) mList.get(position);
            ((TextViewHolder) holder).getBinding().setItem(textBean);
            ((TextViewHolder) holder).getBinding().executePendingBindings(); //解决databinding闪烁问题
        }
    }


}

但是用了DataBinding以后,主要有3个地方发生了变化,ViewHolder, onCreateViewHolder,onBindMyViewHolder。

2.2.1 编写ViewHolder

这里我的RecyclerView有两种布局,并且布局全部是用databinding编写的
class FruitViewHolder extends RecyclerView.ViewHolder {
    private ItemFruitBinding binding;

    public ItemFruitBinding getBinding() {
        return binding;
    }

    public FruitViewHolder(ItemFruitBinding binding) {
        super(binding.getRoot());
        this.binding = binding;
    }
}
...
class TextViewHolder extends RecyclerView.ViewHolder {
    private ItemTextBinding binding;

    public ItemTextBinding getBinding() {
        return binding;
    }

    public TextViewHolder(ItemTextBinding binding) {
        super(binding.getRoot());
        this.binding = binding;
    }
}
这里发生了一点变化,1.构造函数的参数不再是你item布局对应的View对象了,而是变成了你item布局对应的ViewDataBinding对象,这个名字是和你item布局的名字相关的,我这里的item是item_fruit.xml。2.里面写了一个getBinding的方法,方便获取binding对象。ViewHolder里面不需要其他操作,以前的那些findViewById去获取控件对象都不用了,因为item布局里面已经和数据绑定了。
 

2.2.2 重写onCreateViewHodler(ViewGroup parent,int viewType)

这里根据返回的viewType布局类型来创建不同的ViewHolder,viewType为getItemViewType(int position)方法返回的值:
@Override
    public RecyclerView.ViewHolder onCreateMyViewHolder(ViewGroup parent, int viewType) {
        switch (viewType) {
            case R.layout.item_fruit:
                ItemFruitBinding itemFruitBinding = DataBindingUtil.inflate(inflater, R.layout.item_fruit, parent, false);
                return new FruitViewHolder(itemFruitBinding);
            case R.layout.item_text:
                ItemTextBinding itemTextBinding = DataBindingUtil.inflate(inflater, R.layout.item_text, parent, false);
                return new TextViewHolder(itemTextBinding);
            default:
                ItemFruitBinding binding = DataBindingUtil.inflate(inflater, R.layout.item_fruit, parent, false);
                return new FruitViewHolder(binding);
        }
    }

2.2.3.onBindViewHolder(RecyclerView.ViewHolder holder,int position)方法

@Override
    public void onBindMyViewHolder(RecyclerView.ViewHolder holder, int position) {//绑定数据
        if (holder instanceof FruitViewHolder) {
            FruitItem fruitBean = (FruitItem) mList.get(position);
            ((FruitViewHolder) holder).getBinding().setItem(fruitBean);
            ((FruitViewHolder) holder).getBinding().executePendingBindings(); //解决databinding闪烁问题
        } else if (holder instanceof TextViewHolder) {
            TextItem textBean = (TextItem) mList.get(position);
            ((TextViewHolder) holder).getBinding().setItem(textBean);
            ((TextViewHolder) holder).getBinding().executePendingBindings(); //解决databinding闪烁问题
        }
    }

2.3 MainAcitivity 填充数据

public class MainActivity extends AppCompatActivity {

    private MultiItemAdapter multiItemAdapter;
    private List<IBaseBindingAdapterItem> mList = new ArrayList<>(); //数据源
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //setContentView(R.layout.activity_main);
        ActivityMainBinding binding = DataBindingUtil.setContentView(this,R.layout.activity_main);
        initData();
        multiItemAdapter = new MultiItemAdapter(this,mList);//获取填充的数据
        LinearLayoutManager layoutManager = new LinearLayoutManager(this, OrientationHelper.VERTICAL,false);
        binding.recyclerView.setLayoutManager(layoutManager);
        binding.recyclerView.setAdapter(multiItemAdapter);//填充数据到R.id.recyclerView
    }
    private void initData() {
        mList.add(new TextItem("标题1"));
        mList.add(new FruitItem(R.mipmap.fruit, "苹果"));
        mList.add(new FruitItem(R.mipmap.fruit, "香蕉"));
        mList.add(new TextItem("标题2"));
        mList.add(new TextItem("标题3"));
        mList.add(new FruitItem(R.mipmap.fruit, "桃子"));
        mList.add(new TextItem("标题4"));
        mList.add(new FruitItem(R.mipmap.fruit, "梨"));
        mList.add(new TextItem("标题5"));
    }
}

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!