架构组件之ViewBinding解析

350 阅读3分钟

0、什么是ViewBinding视图绑定?

在模块中启用视图绑定之后,系统会为该模块中的每个 XML 布局文件生成一个绑定类。绑定类的实例包含对在相应布局中具有 ID 的所有视图的直接引用。通过视图绑定功能,可以替代findViewById,从而更轻松地编写可与视图交互的代码。

1、使用条件

视图绑定在 Android Studio 3.6 Canary 11 及更高版本中可用。

2、开启视图绑定

android {
 viewBinding {
  enabled = true
   }
}

3、忽略某个布局文件的视图绑定

<LinearLayout
 tools:viewBindingIgnore="true" >
</LinearLayout>

4、生成XML绑定类

为某个模块启用视图绑定功能后,系统会为该模块中包含的每个XML布局文件各生成一个绑定类。每个绑定类均包含对根视图以及具有ID的所有视图的引用。绑定类的名称为:XML文件的名称转换为驼峰式大小写+Binding。

XML文件: activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/tv_hello_world"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

自动生成绑定类文件:

ActivityMainBinding.java

// Generated by view binder compiler. Do not edit!
package androidstack.viewbinding.databinding;

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import androidstack.viewbinding.R;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.viewbinding.ViewBinding;

import java.lang.NullPointerException;
import java.lang.Override;
import java.lang.String;

public final class ActivityMainBinding implements ViewBinding {
    @NonNull
    private final ConstraintLayout rootView;

    @NonNull
    public final TextView tvHelloWorld;

    //实例化绑定类
    private ActivityMainBinding(@NonNull ConstraintLayout rootView, @NonNull TextView tvHelloWorld) {
        this.rootView = rootView;
        this.tvHelloWorld = tvHelloWorld;
    }

    //实现ViewBinding接口,返回绑定后的视图
    @Override
    @NonNull
    public ConstraintLayout getRoot() {
        return rootView;
    }

    @NonNull
    public static ActivityMainBinding inflate(@NonNull LayoutInflater inflater) {
        return inflate(inflater, null, false);
    }

    @NonNull
    public static ActivityMainBinding inflate(@NonNull LayoutInflater inflater,
                                              @Nullable ViewGroup parent, boolean attachToParent) {
        //解析XML布局
        View root = inflater.inflate(R.layout.activity_main, parent, false);
        if (attachToParent) {
            //添加到父布局中
            parent.addView(root);
        }
        //解析之后进行绑定操作
        return bind(root);
    }

    @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.
        String missingId;
        missingId:
        {
            //根据ID寻找XML中的View
            TextView tvHelloWorld = rootView.findViewById(R.id.tv_hello_world);
            if (tvHelloWorld == null) {
                //如果找不到View,退出missingId语句块,后抛空指针异常
                missingId = "tvHelloWorld";
                break missingId;
            }
            //实例化绑定类
            return new ActivityMainBinding((ConstraintLayout) rootView, tvHelloWorld);
        }
        throw new NullPointerException("Missing required view with ID: ".concat(missingId));
    }
}

该类实现了ViewBinding接口,: ViewBinding.java

/** A type which binds the views in a layout XML to fields. */
public interface ViewBinding {
    /**
     * Returns the outermost {@link View} in the associated layout file. If this binding is for a
     * {@code <merge>} layout, this will return the first view inside of the merge tag.
     */
    @NonNull
    View getRoot();
}

5、使用绑定类文件

MainActivity.kt

package androidstack.viewbinding

import android.os.Bundle
import androidstack.viewbinding.databinding.ActivityMainBinding
import androidx.appcompat.app.AppCompatActivity

class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        //获取绑定类实例
        binding = ActivityMainBinding.inflate(layoutInflater)
        //通过绑定类获取布局后,传给setContentView设置内容视图
        setContentView(binding.root)
        //通过绑定类获取对应的ID视图,进行操作
        binding.tvHelloWorld.text = "视图绑定"
    }
}

6、运行结果

7、demo地址

github.com/chaozhouzha…

推荐文章:

1. 通过学习Handler源码,手写子线程间的通信

2. 通过代码例子详解aidl跨进程通信的调用流程

3. Java实现十大排序算法(上)

技术交流,欢迎加我微信:HackerFeeling,拉你入技术交流群。
扫码关注公众号【Android技术堆栈】,一起学习Android技术。

喜欢就点个「在看」吧 ▽

本文使用 mdnice 排版