你还在用 findViewById 吗?

4,675 阅读3分钟

本文微信公众号「AndroidTraveler」首发。

定位

本篇文章的定位者主要是初学者、对于 ButterKnife、DataBinding、ViewBinding 没听过或没用过的开发者以及任何感兴趣的读者。

背景

在 Android 里面要操作组件,需要获取到组件,本篇主要说明组件获取的各种方式。

1. 官方的 API findViewById

我们先放上布局文件:

<?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_center"
        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>

很简单,就是居中显示了一个文本。

我们在代码里面如果要改变文本内容,不再显示这里指定的 Hello World!,需要怎么处理呢?

其实也很简单,我们找到对应的组件,然后再更新就可以了。

代码如下:

TextView tvCenter = findViewById(R.id.tv_center);
tvCenter.setText("Text Change");

这里查找对应组件就是使用 Android SDK 自带的 API findViewById,传入我们在 xml 里面对应组件的 id 就可以了。

代码:github.com/nesger/Andr…

2. 开源库 ButterKnife

方式一使用 findViewById 看起来似乎没有什么问题,但是当你组件比较多的时候,你要 ctrl+c、ctrl+v 或者使用 AndroidStudio 的快捷键做拷贝操作。

结果就是出现一大堆的 findViewById。

比如下面这样:

TextView tv1;
TextView tv2;
TextView tv3;
TextView tv4;
TextView tv5;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    tv1 = findViewById(R.id.tv1);
    tv2 = findViewById(R.id.tv2);
    tv3 = findViewById(R.id.tv3);
    tv4 = findViewById(R.id.tv4);
    tv5 = findViewById(R.id.tv5);
}

后续如果有新增的话,你也要去写类似的代码。

但是使用开源库 ButterKnife 则不需要,只需要在对应的组件上面注解就可以了。

开源库 ButterKnife 地址:github.com/JakeWharton…

首先需要对 module 所在的 build.gradle 做更改,修改配置如下:

android {
  ...
  // Butterknife requires Java 8.
  compileOptions {
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
  }
}

dependencies {
  implementation 'com.jakewharton:butterknife:10.2.1'
  annotationProcessor 'com.jakewharton:butterknife-compiler:10.2.1'
}

这个也可以在 ButterKnife 的 README 文件上面查看获知最新配置。

配置完成之后,我们对应方式一修改代码如下:

@BindView(R.id.tv_center)
TextView tvCenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    ButterKnife.bind(this);
    tvCenter.setText("Text Change");
}

可以看到我们新增了一行代码:

ButterKnife.bind(this);

然后通过注解 id(@BindView(resId)) 到对应组件的方式取代 findViewById。

这样后续新增组件,只需要在对应组件上面增加注解就可以了。

代码:github.com/nesger/Andr…

3. DataBinding

首先需要对 module 所在的 build.gradle 做更改,修改配置如下:

android {
    ...
    dataBinding {
        enabled = true
    }
}

接着在布局外面套一层 layout,如下:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <androidx.constraintlayout.widget.ConstraintLayout
        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_center"
            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>
</layout>

然后主界面代码逻辑修改如下:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
    binding.tvCenter.setText("Text Change");
}

是不是感觉更精简了?

核心代码是:

ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);

拿到 binding 之后就可以结合具体组件 id 来做设置了。

代码:github.com/nesger/Andr…

4. ViewBinding

最低环境要求:

Android Studio:3.6
Android Gradle Plugin 3.6.0

首先需要对 module 所在的 build.gradle 做更改,修改配置如下:

// 需要 Android Gradle Plugin 3.6.0
android {
    ...
    viewBinding {
        enabled = true
    }
}

如果提示

Could not find method viewBinding() for arguments

说明环境不对,可能是 Gradle 版本问题,请对比上面的最低环境要求。

配置好之后代码修改如下:

ActivityMainBinding mBinding;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    mBinding = ActivityMainBinding.inflate(getLayoutInflater());
    setContentView(mBinding.getRoot());
    mBinding.tvCenter.setText("Text Change");
}

可以看到最关键的部分就是要获取到 ActivityMainBinding,然后就可以对具体组件做操作了。

对比 DataBinding 的好处是不需要去修改 xml 文件,不用套一层 layout。

更多用法可以到官网查看:view-binding

代码:github.com/nesger/Andr…

个人使用

我这边之前用的比较多的还是方式一和方式二。

方式一主要是针对一些历史遗留项目或者简单写些 sample,就没必要单独依赖一个三方库。
方式二主要是针对新的项目。