Android开发View Binding的使用与解析

作者: android00 发布时间: 2022-10-13 浏览: 1105 次 编辑

1. 前言

  • 如何干掉模版代码是很多第三方框架的设计初衷,在Android开发中,findViewById()是必不可少的存在,这样的冗余代码在很久以前充斥在Android工程中,因此也出现了很多精简方案。
  • 在Android Studio3.6中加入了很多新特性——View Binding就是其中之一。目前已经在工程中使用并上线,未出现稳定性问题,因此做以下记录。
  • 在谈View Binding之前,我们先聊一下在此之前有哪些代表性方案做过这个事情,以及它们的优缺点分析。

2. View Binding

这就是今天的主角了,View Binding既不会像Butter Knife那样为View分配多余的对象,也不会像Data Binding那样使用Annotation Processor,它的实现既简单又强大。首先来看一下如何使用View Binding。

2.1 View Binding所需环境

  • 首先需要将Android Studio升级到3.6版本及以上
  • 升级Gradle plugin版本到3.6.1
  • 并在app模块中手动开启view binding开关(View Binding以模块为粒度进行开启/关闭)
buildscript {
 ...
    dependencies {
        classpath "com.android.tools.build:gradle:3.6.1"
    }
}


android {
    ...
    viewBinding {
        enabled = true
    }
}

这里升级gradle的时候遇到了一些小坑,即工程中使用的tinker版本过低,其使用了旧版gradle中的语法,因此会报错,将tinker版本升级到’1.9.14.6’后问题解决。

2.2 View Binding的使用

activity_main.xml的布局如下

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">


    <Button
        android:id="@+id/first_button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="First Button" />


    <include
        android:id="@+id/inner_layout"
        layout="@layout/inner_layout" />
</LinearLayout>

这里直接include了一个子布局,inner_layout.xml的布局如下:

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


    <Button
        android:id="@+id/inner_layout_second_button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Inner Button" />
</FrameLayout>
  • 默认情况下,每一个布局xml文件都会生成一个对应的Binding类,名字的生成规则同Data Binding。
  • 在该例子中,会自动为我们生成一个ActivityMainBinding.java,以及include的标签对应的InnerLayoutBinding,这里include必须指定一个id。
  • 当然,如果不需要为该xml生成Binding类,可以在xml的根布局中配置tools:viewBindingIgnore=“true”。

在Java中的使用如下所示:

public class MainActivity extends AppCompatActivity {

    private ActivityMainBinding binding;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = ActivityMainBinding.inflate(LayoutInflater.from(this));
        setContentView(binding.getRoot());

        binding.firstButton.setText("Text Change");
        binding.innerLayout.innerLayoutSecondButton.setText("Inner Layout Button Text Change");
    }
}

用法也很简单,直接通过binding点名字的方式就可以获取到控件实例,消除了findViewById的模版代码。

这里可以看到,setContentView的入参写法都变了,因为可通过XXXBinding类的getRoot函数获取到布局的根View,再通过setContentView添加到Activity。

2.3 View Binding的一些注意点

在2.2中我们演示了include的用法,只需要加一个id,我们就可以通过binding.innerLayout获取到该子布局。但是有个特殊情况需要处理,就是merge标签。

如果子布局inner_layout.xml中的布局如下,多了一个merge标签:

<?xml version="1.0" encoding="utf-8"?>
<merge>
    <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent">


        <Button
            android:id="@+id/inner_layout_second_button"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Inner Button" />
    </FrameLayout>
</merge>

那么在activity_main.xml的布局的include标签中就一定不要设置id了(这里就不贴activity_main.xml的代码了),否则会找不到View报空指针异常。这个情况,我们可以先初始化主布局,再初始带merge的布局,那么就需要用到View Binding的另一个构造方法了,处理如下:

public class MainActivity extends AppCompatActivity {
    private ActivityMainBinding binding;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = ActivityMainBinding.inflate(LayoutInflater.from(this));
        setContentView(binding.getRoot());

        //使用
        binding.firstButton.setText("Text Change");

        //merge标签处理
        InnerLayoutBinding subBinding = InnerLayoutBinding.bind(binding.getRoot());
        subBinding.innerLayoutSecondButton.setText("Inner Layout Button Text Change");
    }
}