• 「Android」浅析viewBinding和DataBinding


    viewBinding

    优点

    当一个页面布局出现多个控件时,使用findViewById去进行控件绑定,过于冗长,且存在NULL指针异常风险。viewBinding直接创建对视图的引用,不存在因控件ID不存在而引发的NULL指针异常。并且在绑定类中对控件添加@NonNull注解

    findViewByIdviewBinding
    冗长简短
    NULLNULL安全

    配置

    3.6之前的版本在build.gradle文件中声明如下定义

     viewBinding {
                enabled = true
            }
    
    • 1
    • 2
    • 3

    4.0以上的版本在build.gradle文件中声明如下定义

     buildFeatures {
                viewBinding = true
            }
    
    • 1
    • 2
    • 3

    声明如上定义之后,点击同步(Sync Now)按钮,系统会自动生成viewBinding类,例如MainActivity会生成名为ActivityMainBinding的类,ReceiveActivity会生成名为ActivityReceiveBinding的类,以此类推;
    以上viewBinding类会生成在如下路径文件中

    build//generated//data_binding_base_class_source_out//debug//out//com.你的包名//databinding
    
    • 1

    使用

    使用步骤很简单,需要被调用的控件声明id就行,然后声明viewBinding类对象

    private ActivityMainBinding binding;
    
    • 1

    绑定视图

    binding = ActivityMainBinding.inflate(getLayoutInflater());
    setContentView(binding.getRoot());
    
    • 1
    • 2

    控件引用

    binding.postMes.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    startActivity(new Intent(MainActivity.this,ReceiverActivity.class));
                }
            });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    源码解析

    如上所示,我们使用了ActivityMainBinding.inflate()方法进行视图绑定和binding.getRoot()方法获取视图。
    首先我们在外部通过调用ActivityMainBinding.inflate()方法。

      @NonNull
      public static ActivityMainBinding inflate(@NonNull LayoutInflater inflater) {
        return inflate(inflater, null, false);
      }
    
    • 1
    • 2
    • 3
    • 4

    然后内部进行重载,添加我们的Avcivity的布局文件,并调研bind(root)方法

      @NonNull
      public static ActivityMainBinding inflate(@NonNull LayoutInflater inflater,
          @Nullable ViewGroup parent, boolean attachToParent) {
        View root = inflater.inflate(R.layout.activity_main, parent, false);
        if (attachToParent) {
          parent.addView(root);
        }
        return bind(root);
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    在bind方法中进行控件绑定,通过其findChildViewById()方法

    @NonNull
      public static ActivityMainBinding bind(@NonNull View rootView) {
        // The body of this method is generated in a way you would not otherwise write.
        // This is done to optimize the compiled bytecode for size and performance.
        int id;
        missingId: {
          id = R.id.content;
          TextView content = ViewBindings.findChildViewById(rootView, id);
          if (content == null) {
            break missingId;
          }
    
          id = R.id.postMes;
          Button postMes = ViewBindings.findChildViewById(rootView, id);
          if (postMes == null) {
            break missingId;
          }
    
          return new ActivityMainBinding((LinearLayout) rootView, content, postMes);
        }
        String missingId = rootView.getResources().getResourceName(id);
        throw new NullPointerException("Missing required view with ID: ".concat(missingId));
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    然后在findChildViewById()方法中最终也使用到了findViewById()方法,但差距在于跳过视图本身

     /**
         * Like `findViewById` but skips the view itself.
         *
         * @hide
         */
        @Nullable
        public static <T extends View> T findChildViewById(View rootView, @IdRes int id) {
            if (!(rootView instanceof ViewGroup)) {
                return null;
            }
            final ViewGroup rootViewGroup = (ViewGroup) rootView;
            final int childCount = rootViewGroup.getChildCount();
            for (int i = 0; i < childCount; i++) {
                final T view = rootViewGroup.getChildAt(i).findViewById(id);
                if (view != null) {
                    return view;
                }
            }
            return null;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    最后通过将获取到的控件定义与内部定义的字段进行缝合,以暴露给外部使用

      @NonNull
      private final LinearLayout rootView;
    
      @NonNull
      public final TextView content;
    
      @NonNull
      public final Button postMes;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
      private ActivityMainBinding(@NonNull LinearLayout rootView, @NonNull TextView content,
          @NonNull Button postMes) {
        this.rootView = rootView;
        this.content = content;
        this.postMes = postMes;
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    DataBinding

    配置

    依旧在build.gradle文件中配置如下定义

     dataBinding {
                enabled = true
            }
    
    • 1
    • 2
    • 3

    创建实体类

    实体类通过继承BaseObservable类,而BaseObservable又实现了Observable,从而获取添加和移除监听的机制。
    在get()方法中使用@Bindable注解,会自动生成BR类,此类中将添加@Bindable的字段声明成常量,然后在set()方法使用notifyPropertyChanged()配合使用,当数据发生变化时,dataBinding会自动修改该字段的值。

    public class EventMessage extends BaseObservable {
        public String title;
        public EventMessage(){
    
        }
        public EventMessage(String title){
            this.title = title;
        }
        @Bindable
        public String getTitle() {
            return title;
        }
        /**
         * @param title*/
        public void setTitle(String title) {
            this.title = title;
            notifyPropertyChanged(BR.title);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    创建布局

    创建layout标签布局才会生成ActivityMainBinding(以及布局文件名而定)
    EditText通过使用如下定义进行绑定,

     android:text="@={viewModel.message.title}"
    
    • 1

    Button通过如下定义进行点击事件监听

     android:onClick="@{viewModel.setText}"
    
    • 1

    以上两者的存在差距,EditText多了一个=,而Button没有,并且Button绑定监听事件,不需要加()

    <?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"
        xmlns:tools="http://schemas.android.com/tools">
    
        <data>
            <variable
                name="viewModel"
                type="com.franzliszt.databinding.ViewModel" />
    
        </data>
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            tools:context=".MainActivity"
            android:orientation="vertical"
            android:gravity="center">
            <EditText
                android:id="@+id/inputText"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:hint="default"
                android:text="@={viewModel.message.title}"/>
            <TextView
                android:id="@+id/ShowText"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text=""/>
            <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="update"
                android:onClick="@{viewModel.setText}"/>
        </LinearLayout>
    </layout>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    创建viewModel

    通过监听Button点击事件,在其中监听EditText输入事件,并将其输入的字符串显示在TextView中

    public class ViewModel {
        private ActivityMainBinding binding;
        public EventMessage message;
        public ViewModel(ActivityMainBinding binding, EventMessage message){
            this.binding = binding;
            this.message = message;
        }
        public void setText(View view){
            String str = message.getTitle();
            binding.ShowText.setText(str);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    dataBinding绑定

     private ActivityMainBinding binding;
    
    • 1
    binding = DataBindingUtil.setContentView( this,R.layout.activity_main );
    binding.setViewModel(new ViewModel(binding,new EventMessage()));
    
    • 1
    • 2
  • 相关阅读:
    一种离散化的编程策略
    10.8队列安排,最少找字典次数,表达式转换与计算模拟(栈、队列)
    Clusterpedia 入围 “全球开源软件产品评选活动” 优胜奖
    四级英语黄金词组及常用15个句型
    图机器学习(GML)&图神经网络(GNN)原理和代码实现(前置学习系列二)
    【剑指Offer】31.栈的压入、弹出序列
    laravel系列(三) Dcat admin框架工具表单以及普通表单的使用
    50springboot私人健身与教练预约管理系统
    土壤类型、土壤质地、土壤养分空间分布
    天龙八部科举答题问题和答案(全4/8)
  • 原文地址:https://blog.csdn.net/News53231323/article/details/125002668