本文同步发表于我的微信公众号,扫一扫文章底部的二维码或在微信搜索 郭霖 即可关注,每个工作日都有文章更新。

kotlin-android-extensions插件可能算得上是我最喜欢的一个Kotlin在Android上的特性了。

这么说并不夸张,因为以前在使用Java开发Android程序时,我们总是要写一大堆的findViewById,枯燥又没什么意义。

虽然也有一些诸如ButterKnife之类的第三方库,专门用于对findViewById的用法进行简化,但是ButterKnife还是要通过注解来让控件与资源id之间进行绑定,并不算是非常方便。

而kotlin-android-extensions插件的出现则让这一情况完全发生了改变,我们可以不用再编写烦琐的findViewById代码,同时能用一种非常简便的写法进行替代。

比如说这里有一个布局文件activity_main.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><TextViewandroid:id="@+id/viewToShowText"android:layout_width="wrap_content"android:layout_height="wrap_content" /></LinearLayout>

非常简单,布局文件中只有一个TextView控件,它的id是viewToShowText。

那么,如果我想要在MainActivity中去设置TextView控件的内容,使用Java语言的话通常需要这样写:

public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);TextView viewToShowText = findViewById(R.id.viewToShowText);viewToShowText.setText("Hello");}}

可以看到,这里我们首先通过findViewById()函数获取到了TextView控件的实例,然后再调用setText()函数将其显示的内容设置成Hello。

这个findViewById()函数其实是很头疼的,这里我们只是获取了一个控件的实例,所以可能感受还不太明显。如果你要去获取10个甚至100个控件的实例,每个都要去findViewById一遍,你一定会抓狂的。

那么如果是使用Kotlin语言的话,这个问题要怎么解决呢?借助kotlin-android-extensions插件,我们可以使用如下代码来完成同样的功能:

class MainActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)viewToShowText.text = "Hello"}}

可以看到,这里我们不再需要调用findViewById()函数去获取控件的实例,而是直接调用该控件在xml中定义的id名称,就能够设置其显示的内容了。

而这个神奇的功能就是由kotlin-android-extensions插件自动完成的,这个插件能够帮助我们减少大量琐碎无意义的代码。

然而它被废弃了

其实早在几个月前,就有朋友在公众号上询问我,说自己升级了Android Studio 4.1之后,发现新建项目的时候Android Studio已经不会自动帮我们引入kotlin-android-extensions插件了,需要自己手动去添加才能使用,是不是Google不再推荐使用这个插件了?

当时我还说,不可能呀,这个插件这么好用,而且Kotlin也是Google未来主推的技术,可能只是Android Studio 4.1的bug吧。

然而,没过多久我就被打脸了。某天我将项目工程的Gradle版本升级到了最新,然后构建项目时发现了这样一个警告提示:

Google明确地告诉我们,kotlin-android-extensions插件已被废弃,现在推荐使用ViewBinding来进行替代。

对于Google的这种技术迭代频率我是有点生气的,如果kotlin-android-extensions插件是Google主推的技术,理应拥有更长的生命周期,不然的话就不该作为默认插件集成到Android Studio当中。要知道,去年我才刚刚出版的新书《第一行代码 第3版》里还大量使用了这个技术。

不过,好在ViewBinding并不复杂,从kotlin-android-extensions插件切换到ViewBinding也是比较容易的,那么本篇文章就作为《第一行代码 第3版》的另外一篇DLC,向大家介绍一下,如何使用ViewBinding来替代kotlin-android-extensions插件。

为什么会被废弃

在开始介绍ViewBinding之前,我还是想先讨论一下,为什么kotlin-android-extensions插件会被废弃。

虽说Google的技术迭代频率常常会让我们直呼学不动了,但是Google也绝对不会无缘无故去废弃一个之前主推的技术,说明kotlin-android-extensions插件肯定还是存在问题的。

那么到底存在什么问题呢?

比较容易让人想到的一个缺点就是,kotlin-android-extensions插件只能支持Kotlin语言,而无法支持Java语言。当然这个我认为并不是主要原因,因为现在Google开发的各种新技术都在全面兼容Kotlin,而不再怎么去考虑Java了,如协程、Jetpack Compose等。

那么主要原因是什么呢?这可能就要从kotlin-android-extensions插件的实现原理去理解了。刚才我们已经看到过了使用kotlin-android-extensions插件后的代码,非常简单:

class MainActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)viewToShowText.text = "Hello"}}

那么这段代码为什么可以工作呢?

我们可以通过点击Android Studio顶部导航栏的Tools -> Kotlin -> Show Kotlin Bytecode来查看这段代码对应的Kotlin字节码,然后在弹出窗口中点击Decompile按钮将字节码反编译成Java代码。

为了方便阅读,我将反编译后的代码又做了些整理,大致如下所示:

public final class MainActivity extends AppCompatActivity {private HashMap _$_findViewCache;protected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);this.setContentView(1300023);TextView var10000 = (TextView)this._$_findCachedViewById(id.textView);var10000.setText((CharSequence)"Hello");}public View _$_findCachedViewById(int var1) {if (this._$_findViewCache == null) {this._$_findViewCache = new HashMap();}View var2 = (View)this._$_findViewCache.get(var1);if (var2 == null) {var2 = this.findViewById(var1);this._$_findViewCache.put(var1, var2);}return var2;}
}

可以看到,实际上kotlin-android-extensions插件会帮我们生成一个_$_findCachedViewById()函数(使用这种奇怪的命名方式是为了防止和开发者定义的函数名冲突)。在这个函数中首先会尝试从一个HashMap中获取传入的资源id参数所对应的控件实例缓存,如果还没有缓存的话,就调用findViewById()函数来查找控件实例,并写入HashMap缓存当中。这样当下次再获取相同控件实例的话,就可以直接从HashMap缓存中获取了。

这就是kotlin-android-extensions插件的实现原理,其实还是非常简单的。

然而这种实现原理同时也暴露出来了一些问题。

比如说每一个Activity都需要使用一个额外的HashMap数据结构来存储所有控件的实例,无形中增加了一些内存的开支。

还有,虽然HashMap是一种O(1)时间复杂度的数据结构,但这毕竟只是理论上的时间复杂度,实际调用肯定是没有直接访问控件实例快的,因此kotlin-android-extensions插件也在无形当中降低了程序的运行效率。

最重要的是,这些内容对于绝大部分开发者来说都是黑盒,使用kotlin-android-extensions插件的人可能并不知道这些隐藏的“坑”,这个问题在稍后介绍RecyclerView Adapter的时候会更加突显,我们待会儿再聊。

不管我上面分析的这些足不足以成为废弃kotlin-android-extensions插件的理由,总之这已经是事实了。那么接下来,我们的学习目标就变成了:如何使用ViewBinding来替代之前的kotlin-android-extensions插件。请放心,这并不是一件很难的事情。

什么是ViewBinding

ViewBinding总体来说其实非常简单,它的目的只有一个,就是为了避免编写findViewById,这和它另外一个非常复杂的兄弟DataBinding相比有明显的区别。

要想使用ViewBinding需要注意两件事。第一,确保你的Android Studio是3.6或更高的版本。第二,在你项目工程模块的build.gradle中加入以下配置:

android {...buildFeatures {viewBinding true}
}

这样准备工作就完成了。接下来我会从Activity、Fragment、Adapter、引入布局这4个方面,分别讨论ViewBinding的用法。

在Activity中使用ViewBinding

一旦启动了ViewBinding功能之后,Android Studio会自动为我们所编写的每一个布局文件都生成一个对应的Binding类。

Binding类的命名规则是将布局文件按驼峰方式重命名后,再加上Binding作为结尾。

比如说,前面我们定义了一个activity_main.xml布局,那么与它对应的Binding类就是ActivityMainBinding。

当然,如果有些布局文件你不希望为它生成对应的Binding类,可以在该布局文件的根元素位置加入如下声明:

<LinearLayoutxmlns:tools="http://schemas.android.com/tools"...tools:viewBindingIgnore="true">...
</LinearLayout>

接下来我们看一下如何使用ViewBinding来实现在MainActivity中去设置TextView内容的功能,代码如下所示:

class MainActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)val binding = ActivityMainBinding.inflate(layoutInflater)setContentView(binding.root)binding.textView.text = "Hello"}}

ViewBinding的用法可以说就是这么简单。首先我们要调用activity_main.xml布局文件对应的Binding类,也就是ActivityMainBinding的inflate()函数去加载该布局,inflate()函数接收一个LayoutInflater参数,在Activity中是可以直接获取到的。

接下来就更加简单了,调用Binding类的getRoot()函数可以得到activity_main.xml中根元素的实例,调用getTextView()函数可以获得id为textView的元素实例。

那么很明显,我们应该把根元素的实例传入到setContentView()函数当中,这样Activity就可以成功显示activity_main.xml这个布局的内容了。然后获取TextView控件的实例,并给它设置要显示的文字即可。

当然,如果你需要在onCreate()函数之外的地方对控件进行操作,那么就得将binding变量声明成全局变量,写法如下:

class MainActivity : AppCompatActivity() {private lateinit var binding: ActivityMainBindingoverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)binding = ActivityMainBinding.inflate(layoutInflater)setContentView(binding.root)binding.textView.text = "Hello"}}

注意,Kotlin声明的变量都必须在声明的同时对其进行初始化。而这里我们显然无法在声明全局binding变量的同时对它进行初始化,所以这里又使用了lateinit关键字对binding变量进行了延迟初始化。

虽然这里我举的例子非常简单,但实际上ViewBinding用法的套路都是如此,掌握了这一套规则之后基本上你就可以举一反三了。

在Fragment中使用ViewBinding

下面我们学习一下,如何在Fragment中使用ViewBinding。这部分内容同样非常简单,因为在Fragment中使用ViewBinding和在Activity基本是一样的。

这里我还是通过代码的方式进行演示,顺便介绍一下Fragment与Activity中ViewBinding用法的异同。

假设我们有一个布局文件叫fragment_main.xml,那么启用ViewBinding功能之后,则必然会生成一个与其对应的FragmentMainBinding类。

如果我们想要在MainFragment中去显示这个布局,就可以这样写:

class MainFragment : Fragment() {private var _binding: FragmentMainBinding? = nullprivate val binding get() = _binding!!override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {_binding = FragmentMainBinding.inflate(inflater, container, false)return binding.root}override fun onDestroyView() {super.onDestroyView()_binding = null}}

这段代码的实际逻辑远没有看上去得复杂。

首先最核心的逻辑仍然是调用FragmentMainBinding的inflate()函数去加载fragment_main.xml布局文件,但由于这是在Fragment当中,所以使用了3个参数的inflate()函数重载,这和我们平时在Fragment中去加载布局文件的方式如出一辙。

接下来不一样的地方在于,由于我们是在onCreateView()函数中加载的布局,那么理应在与其对应的onDestroyView()函数中对binding变量置空,从而保证binding变量的有效生命周期是在onCreateView()函数和onDestroyView()函数之间。

但由于Kotlin空类型系统的存在,导致为了实现这一简单的功能,需要额外编写一些看上去很奇怪的代码,上述代码就是如此。关于Kotlin空类型系统这里我就不展开介绍了,还不了解的朋友可以去参考《第一行代码 第3版》第2章的内容。

好吧,这是我少有承认Java要比Kotlin更简洁的地方,因为使用Java代码去实现同样的功能只需要这样写:

public class MainFragment extends Fragment {private FragmentMainBinding binding;@Overridepublic View onCreateView(@NotNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {binding = FragmentMainBinding.inflate(inflater, container, false);return binding.getRoot();}@Overridepublic void onDestroyView() {super.onDestroyView();binding = null;}
}

这两段代码实现的功能以及所想表达的意思是完全相同的,但是明显Java版本要更加好理解一点。

在Adapter中使用ViewBinding

接下来,让我们再来探讨一下在Adapter中使用ViewBinding的场景,这个场景会相对比较有趣,同时也是之前kotlin-android-extensions插件被误用比较多的地方。

相信每一位Android开发者都使用过RecyclerView,也都写过Adapter,Adapter其实是很能考验一个开发者功底的地方。

我在很早之前面试的时候被问到过,为什么我们要在ListView的Adapter当中去写ViewHolder(那个时候还没有RecyclerView)。答案就是,为了不用在列表滚动的时候频繁调用findViewById(),从而减少了一些没必要的性能消耗。

而RecyclerView把ListView中的这个普遍应用的最佳实践直接作为默认实现集成了进去,所以只要我们使用RecyclerView,就一定要写ViewHolder。

然而有些朋友在这里却存在一些误用的情况,这里我还是通过一个具体的示例进行说明。

假设我们定义了fruit_item.xml来作为RecyclerView子项的布局:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_margin="5dp"><ImageViewandroid:id="@+id/fruitImage"android:layout_width="40dp"android:layout_height="40dp"android:layout_gravity="center_horizontal"android:layout_marginTop="10dp" /><TextViewandroid:id="@+id/fruitName"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="left"android:layout_marginTop="10dp" /></LinearLayout>

然后编写如下RecyclerView Adapter来加载和显示这个子项布局:

class FruitAdapter(val fruitList: List<Fruit>) : RecyclerView.Adapter<FruitAdapter.ViewHolder>() {inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {val fruitImage: ImageView = view.findViewById(R.id.fruitImage)val fruitName: TextView = view.findViewById(R.id.fruitName)}override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {val view = LayoutInflater.from(parent.context).inflate(R.layout.fruit_item, parent, false)return ViewHolder(view)}override fun onBindViewHolder(holder: ViewHolder, position: Int) {val fruit = fruitList[position]holder.fruitImage.setImageResource(fruit.imageId)holder.fruitName.text = fruit.name}override fun getItemCount() = fruitList.size}

这是比较标准和传统的一种写法,并且可以说没有任何问题,《第一行代码 第3版》中关于RecyclerView这部分讲解也是使用的这种写法。

然而有些读者朋友跟我反馈,说这种写法还要在ViewHolder当中声明控件变量,还要编写findViewById(),实在是太复杂了。自己找到了一种更简单的写法,只需要借助kotlin-android-extensions插件,就可以这样写:

class FruitAdapter(val fruitList: List<Fruit>) : RecyclerView.Adapter<FruitAdapter.ViewHolder>() {inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view)override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {val view = LayoutInflater.from(parent.context).inflate(R.layout.fruit_item, parent, false)return ViewHolder(view)}override fun onBindViewHolder(holder: ViewHolder, position: Int) {val fruit = fruitList[position]holder.itemView.fruitImage.setImageResource(fruit.imageId)holder.itemView.fruitName.text = fruit.name}override fun getItemCount() = fruitList.size}

可以看到,这里ViewHolder中没有进行任何控件声明,相当于只是定义了一个空的ViewHolder。然后在onBindViewHolder()函数当中,直接调用holder.itemView再接上控件id的名称就可以使用它了。

这种写法确实简化了不少代码,但是这种写法对吗?

如果你的评判标准只是这段代码能不能正常工作,那么答案是肯定的,这样写确实可以正常工作。但是这种写法我可以说是完全不正确的,为什么呢?我们只需要使用刚才的手法把这段代码反编译一下,看看它对应的Java代码是什么样的就知道了。

同样为了方便阅读,我还是对代码进行了简化,只保留了关键部分,如下所示:

public final class FruitAdapter extends Adapter {...public final class ViewHolder extends androidx.recyclerview.widget.RecyclerView.ViewHolder {public ViewHolder(@NotNull View view) {super(view);}}public void onBindViewHolder(@NotNull FruitAdapter.ViewHolder holder, int position) {Fruit fruit = (Fruit)this.fruitList.get(position);View var10000 = holder.itemView;((ImageView)var10000.findViewById(id.fruitImage)).setImageResource(fruit.getImageId());var10000 = holder.itemView;TextView var4 = (TextView)var10000.findViewById(id.fruitName);var4.setText((CharSequence)fruit.getName());}}

不知道你有没有发现问题,现在onBindViewHolder()函数当中,每次都是调用了findViewById()来获取控件实例,这样就导致ViewHolder的作用完全失效了。

所以,上面这种写法就是kotlin-android-extensions插件在Adapter当中一种比较典型的误用方式。同时也算是一个隐藏的“坑”,因为如果你不去将Kotlin代码进行反编译,可能都不知道自己的ViewHolder其实根本就没有起到任何作用。

讲完了kotlin-android-extensions插件的“坑”,接下来我们还是看一下如何在Adapter中使用ViewBinding,别忘了我们的目标始终是不写findViewById。

其实如果你已经熟练掌握了ViewBinding在Activity和Fragment中的用法,那么现在应该可以举一反三了,因为在Adapter中使用ViewBinding基本也是同样的套路。

我们还是先来看一下代码,然后我再稍微做下简单的讲解:

class FruitAdapter(val fruitList: List<Fruit>) : RecyclerView.Adapter<FruitAdapter.ViewHolder>() {inner class ViewHolder(binding: FruitItemBinding) : RecyclerView.ViewHolder(binding.root) {val fruitImage: ImageView = binding.fruitImageval fruitName: TextView = binding.fruitName}override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {val binding = FruitItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)return ViewHolder(binding)}override fun onBindViewHolder(holder: ViewHolder, position: Int) {val fruit = fruitList[position]holder.fruitImage.setImageResource(fruit.imageId)holder.fruitName.text = fruit.name}override fun getItemCount() = fruitList.size}

这段代码的核心基本都在onCreateViewHolder()函数和ViewHolder当中。

首先,我们在onCreateViewHolder()函数中调用FruitItemBinding的inflate()函数去加载fruit_item.xml布局文件,这和ViewBinding在Fragment中的用法是一模一样的。

接下来需要改造ViewHolder,让其构造函数接收FruitItemBinding这个参数。但是注意,ViewHolder的父类RecyclerView.ViewHolder它只会接收View类型的参数,因此我们需要调用binding.root获得fruit_item.xml中根元素的实例传给RecyclerView.ViewHolder。

这样,我们就不需要再使用findViewById()函数来查找控件实例了,而是调用binding.fruitImage和binding.fruitName就可以直接引用到相应控件的实例。

这就是ViewBinding在Adapter中的用法。

对引入布局使用ViewBinding

关于ViewBinding的使用其实还有另外一种比较特殊的场景,那就是如何对引入布局使用ViewBinding。

引入布局一般有两种方式,include和merge。关于这两种方式的用法和区别,我在 Android最佳性能实践(四)——布局优化技巧 这篇文章中有比较详细的讲解,还不了解的朋友可以去参考一下。

接下来我们开始分别学习如何在include和merge的布局中使用ViewBinding。

先来看include,这个情况比较简单。假设我们有如下titlebar.xml布局,是希望作为一个通用布局引入到各布局当中的:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent" ><Buttonandroid:id="@+id/back"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentLeft="true"android:layout_centerVertical="true"android:text="Back" /><TextViewandroid:id="@+id/title"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerInParent="true"android:text="Title"android:textSize="20sp" /><Buttonandroid:id="@+id/done"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentRight="true"android:layout_centerVertical="true"android:text="Done" /></RelativeLayout>

那么如果我想要在activity_main.xml中引入这个布局,只需要这样写:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><include layout="@layout/titlebar" />...
</LinearLayout>

这种写法虽然的确可以将titlebar.xml引入到activity_main.xml布局当中,但问题是,你会发现ViewBinding是关联不到titlebar.xml中的控件的。

那么如何解决这个问题呢?很简单,我们只需要在include的时候给被引入的布局添加一个id,如下所示:

<?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"><include android:id="@+id/titleBar"layout="@layout/titlebar" />...
</LinearLayout>

然后,在MainActivity中,我们即可通过如下的写法引用到titlebar.xml中定义的控件:

class MainActivity : AppCompatActivity() {private lateinit var binding: ActivityMainBindingoverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)binding = ActivityMainBinding.inflate(layoutInflater)setContentView(binding.root)binding.titleBar.title.text = "Title"binding.titleBar.back.setOnClickListener {}binding.titleBar.done.setOnClickListener {}}}

接下来我们再来看一下merge。merge和include最大的区别在于,使用merge标签引入的布局在某些情况下可以减少一层布局的嵌套,而更少的布局嵌套通常就意味着更高的效率。

比如说我们对titlebar.xml进行如下修改:

<merge xmlns:android="http://schemas.android.com/apk/res/android"><Buttonandroid:id="@+id/back"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentLeft="true"android:layout_centerVertical="true"android:text="Back" /><TextViewandroid:id="@+id/title"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerInParent="true"android:text="Title"android:textSize="20sp" /><Buttonandroid:id="@+id/done"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentRight="true"android:layout_centerVertical="true"android:text="Done" /></merge>

可以看到,这里最外层的布局使用了merge标签,这就表示当有任何一个地方去include这个布局时,会将merge标签内包含的内容直接填充到include的位置,不会再添加任何额外的布局结构。

但是很遗憾,如果使用这种写法的话,运行程序将会直接崩溃。因为merge标签并不是一个布局,所以我们无法像刚才那样在include的时候给它指定一个id。

那么这种情况下应该怎么使用ViewBinding呢?首先为了避免崩溃,我们应该将activity_main.xml中引入布局时指定的id移除,如下所示:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><includelayout="@layout/titlebar" /></LinearLayout>

然后修改MainActivity中的代码,如下所示:

class MainActivity : AppCompatActivity() {private lateinit var binding: ActivityMainBindingprivate lateinit var titlebarBinding: TitlebarBindingoverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)binding = ActivityMainBinding.inflate(layoutInflater)titlebarBinding = TitlebarBinding.bind(binding.root)setContentView(binding.root)titlebarBinding.title.text = "Title"titlebarBinding.back.setOnClickListener {}titlebarBinding.done.setOnClickListener {}}}

可以看到,这里我们又定义了一个titlebarBinding变量。很明显,TitlebarBinding就是Android Studio根据我们的titlebar.xml布局文件自动生成的Binding类。

在onCreate()函数中,我们调用TitlebarBinding.bind()函数,让titlebar.xml布局和activity_main.xml布局能够关联起来。

接下来的事情就很简单了,直接使用titlebarBinding变量就可以引用到titlebar.xml中定义的各个控件了。

好了,这大概就是关于ViewBinding的所有内容了,至少我已经想不出还有什么更多的用法,相信本篇文章也足以将你工作中可能遇到的ViewBinding相关的问题全部覆盖到。

另外,如果想要学习Kotlin和最新的Android知识,可以参考我的新书 《第一行代码 第3版》,点击此处查看详情。

关注我的技术公众号,每个工作日都有优质技术文章推送。

微信扫一扫下方二维码即可关注:

kotlin-android-extensions插件也被废弃了?扶我起来相关推荐

  1. 【译】迁移被废弃的Kotlin Android Extensions插件

    原文:Migrating the deprecated Kotlin Android Extensions compiler plugin 作者:Ahmad El-Melegy 在Kotlin 1.4 ...

  2. 迁移废弃的Kotlin Android Extensions插件

    1.视图绑定 删除: apply plugin: 'kotlin-android-extensions' 使用: android {...buildFeatures {viewBinding true ...

  3. kotlin学习笔记——Kotlin Android Extensions

    Kotlin Android Extensions是另外一个团队开发的,它是一个插件所以不需要依赖别的库.当前仅仅包含view的绑定,会自动创建很多属性让我们直接访问xml中的view,我们就不需要明 ...

  4. Kotlin Android Extensions使用指南

    Kotlin Android Extensions是Kotlin团队开发的一个插件,目的是让我们在开发过程中更少的编写代码.目前包括了视图绑定的功能. 几种绑定视图方式对比** xml文件如下 < ...

  5. kotlin android 4,Kotlin Android Extensions: 与 findViewById 说再见 (KAD 04)

    时间:Aug 16, 2017 原文链接:https://antonioleiva.com/kotlin-android-extensions/ 在 Kotlin1.1.4版本 发布后,原作者依据 K ...

  6. Android KTX与Kotlin Android Extensions

    Android KTX Android KTX是Google官方推荐的一套便利的Android API扩展函数库.因还处于beta阶段,相关API并不丰富,但既然是出自JakeWharton大神之手, ...

  7. KTX 和 Kotlin android extension 都到底是个啥?

    1. KTX是什么 Android官网对于KTX的介绍:https://developer.android.com/kotlin/ktx KTX 是被称为Android之光的 JakeWharton  ...

  8. Android KTX 和 Kotlin android extension对比

    参考: KTX 和 Kotlin android extension 都到底是个啥? Android KTX KTX 是被称为Android之光的 JakeWharton 写的 Android KTX ...

  9. android studio json插件_热门Android Studio 插件,这里是Top 20

    Android Studio是Google基于IntelliJ开发的一款功能强大的开发工具,它具有构建出色Android应用所需要的一切.借助基于IntelliJ IDEA的强大的功能,插件非常丰富. ...

最新文章

  1. python 数据类笔试题_一道 Python 类的笔试题详解
  2. 压力测试即将开始,超前思维的128MB区块未必可行
  3. 如何将本地文件通过终端上传到linux服务器 /服务器/阿里云
  4. html中js添加或删除activex,JS:操作样式表2 :用JS实现添加和删除一个类名的功能(addClass()和removeClass())...
  5. 【拔刀吧少年】之条件语句
  6. 所有计算机的字数都是8位,计算机(全部笔记).doc
  7. 利用Console来调试JS程序、Console用法总结
  8. 计算机软考中集成系统,软考中级系统集成项目管理工程师有哪些作用?
  9. android 8.1.0怎么截屏,vivo Z1i怎么截屏?4种vivo Z1i截图方法
  10. dr. unarchiver_1F 新店开业男士一生仅能定制一枚的钻戒,DR 教你用一生爱一人
  11. 阶段3 2.Spring_07.银行转账案例_5 编写业务层和持久层事务控制代码并配置spring的ioc...
  12. 金税盘专、普红字发票开具步骤及(税盘注销方法)
  13. php怎么更换图片背景颜色,照片换底色红色变白色怎么变 怎么换照片底色
  14. 计算机主机usb端口使用不了,电脑usb接口不能用怎么办
  15. 常用单片机模块(一)
  16. 劳动节 | 耕耘美好,创造不凡
  17. 一个电脑接两个显示器(win10)
  18. 明厨亮灶监控系统解决方案,看得见的食品安全
  19. 小程序选择手机图片后 压缩图片 转码base64
  20. Ubutnu18.04解决:Starting Wait until snapd is fully seeded...

热门文章

  1. Python+scrapy爬取36氪网
  2. PaddleOCR实践之飞桨常规赛:中文场景文字识别
  3. 阿里云Aliplayer视频播放(web播放器)
  4. 华师大数据科学考研_华东师范大学数据科学与工程学院硕士研究生录取名单公示...
  5. CNS可增值积分区块链应用未来趋势如何
  6. Linux文件类型之 管道
  7. 西安电子科技大学833真题_21西安电子科技大学计算机/软工考研经验分享
  8. 云服务器怎么安装虚拟显卡,云服务器怎么弄图形界面
  9. 2007年。。。。。
  10. 计算机主板用塑料做的好吗,旧电脑零件千万别扔 重新利用竟还能做出这么多新设备...