JetPack DataBinding原理分析

208 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第8天,点击查看活动详情

一.简介

       DataBinding是谷歌发布的一个实现数据和UI绑定的框架,从字面意思来看即为数据绑定,是 MVVM 模式在Android上的一种实现,用于降低布局和逻辑的耦合性,使代码逻辑更加清晰。

       相对于 MVP,MVVM将Presenter层替换成了ViewModel层,关于MVC、MVP、MVVM架构比较,这里就不赘述了。

       DataBinding 能够省去我们一直以来的 findViewById() 步骤,大量减少 Activity 内的代码,数据能够单向或双向绑定到 layout 文件中。

       接下来本文通过一个例子来学习一下DataBinding的用法及原理分析。

二.实例分析

a.加入dataBinding支持

       使用DataBinding框架需要在对应Model的build.gradle文件Android{}内加入以下代码,同步后就能引入对 DataBinding的支持:

dataBinding {
    enabled = true
}

       AndroidStudio高版本引入方式如下:

buildFeatures {
    dataBinding = true
}
b.xml布局文件转化

       将常规的布局文件转化为对应的数据绑定布局文件,在布局文件的根布局上输入Alt+Enter后,选择Convert to data binding layout会将常规布局转换为以下布局文件形式:

<?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>
        //布局文件用到了View相关的方法,需要引入
        <import type="android.view.View" />
        <variable
            //任意取名字来作为type对应的引用可以通过guide代替类取可变变量
            //在view中通过setGuide()建立引用关系
            name="guide"
            //定义可变变量的类
            type="com.xxx.learn.ViewModel" />

    </data>

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <TextView
            android:id="@+id/name"
            android:layout_width="100dp"
            android:layout_height="50dp"
            android:layout_centerHorizontal="true"
            android:layout_marginTop="10dp"
            android:gravity="center"
            android:textColor="@android:color/holo_red_light"
            android:text="@{guide.rightImageName}" />

        <ImageView
            android:layout_width="200dp"
            android:layout_height="160dp"
            android:layout_below="@+id/name"
            android:layout_centerHorizontal="true"
            android:layout_marginTop="10dp"
            android:scaleType="fitXY"
            android:src="@{guide.rightImageRes}" />

        <TextView
            android:layout_width="300dp"
            android:layout_height="wrap_content"
            android:layout_below="@+id/name"
            android:layout_centerHorizontal="true"
            android:layout_marginTop="200dp"
            android:textColor="@color/colorAccent"
            android:text="@{guide.rightImageDescription}" />

        <TextView
            android:layout_width="100dp"
            android:layout_height="50dp"
            android:layout_below="@+id/name"
            android:layout_marginLeft="50dp"
            android:layout_marginTop="350dp"
            android:gravity="center"
            android:onClick="@{guide::upStep}"
            android:text="@string/up"
            android:textStyle="bold"
            android:textColor="@color/cardview_shadow_start_color"
            android:visibility="@{guide.step!=1?View.VISIBLE:View.INVISIBLE}" />

        <TextView
            android:layout_width="100dp"
            android:layout_height="50dp"
            android:layout_below="@+id/name"
            android:layout_marginLeft="230dp"
            android:layout_marginTop="350dp"
            android:gravity="center"
            android:onClick="@{guide::nextStep}"
            android:text="@string/next"
            android:textStyle="bold"
            android:visibility="@{guide.step!=3?View.VISIBLE:View.INVISIBLE}" />
    </RelativeLayout>
</layout>

      通过以上转化,已经将常规布局转化为数据绑定布局文件,那么下面就一步一步的来讲解实现。

c.View逻辑实现

      将之前常规的UI操作从view里面移到数据绑定布局xml文件中,可以大大减少view的代码量及相关的UI操作逻辑。
使用前,动态设置ImageView的显示,在view里面的操作如下:

ImageView mImg = findViewById(R.id.img);
mImg.setImageResource(R.drawable.rse);

      使用databinding后,可以在布局文件中将布局变量赋值以@{}形式给ImageView的src属性。当rightImageRes变化时,ImageView会动态加载对应的资源文件。

<ImageView
     android:layout_width="200dp"
     android:layout_height="160dp"
     android:layout_below="@+id/name"
     android:layout_centerHorizontal="true"
     android:layout_marginTop="10dp"
     android:scaleType="fitXY"
     android:src="@{guide.rightImageRes}" />

      使用数据绑定布局文件后,view对应的类变为:


import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import com.xxx.learn.ViewModel;
import com.xxx.learn.databinding.KeyboardLayoutBinding;

import com.xxx.learn.R;

import androidx.databinding.DataBindingUtil;

public class KeyboardFragment extends BaseFragment {

    private ViewModel mViewModel;

    @Override
    public int getLayoutId() {
        return R.layout.keyboard_layout;
    }

    @Override
    public void initData(View view) {

    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        //-------------------分析1------------------------------------
        KeyboardLayoutBinding binding = DataBindingUtil.inflate(inflater, R.layout.keyboard_layout, container, false);
        //获取布局对应的view
        //--------------------分析2-----------------------------------
        View view = binding.getRoot();
        //-------------------分析3----------------------------------
        mViewModel = new ViewModel(mContext);
        binding.setGuide(mViewModel);
        //-------------------分析4----------------------------------
        view.viewpager.setAdapter(xx);
        return view;
    }
}

      通过以上可以发现,主要的分析点如下:
分析1:通过DataBindingUtil来替代inflater来解析数据绑定布局文件,KeyboardLayoutBinding是在编译时根据keyboard_layout生成的(后面会有详细分析);如果为Activity,通过setContentView()方法来解析生成KeyboardLayoutBinding;
分析2:通过getRoot()获取布局文件对应的View;
分析3:创建ViewModel,并通过setGuide()(名字不是固定的)将View与ViewModel里面的变量建立关联;
分析4:直接通过view.idName来获取对应的view,然后进行操作;
使用了数据绑定布局后,view里面除了加载布局后,没有任何相关的findViewById操作,确实减少了不少UI处理逻辑。