本文同步发表于个人微信公众号,扫一扫文章底部的二维码或在微信搜索 郭霖 便可关注,每一个工做日都有文章更新。java
kotlin-android-extensions插件可能算得上是我最喜欢的一个Kotlin在Android上的特性了。android
这么说并不夸张,由于之前在使用Java开发Android程序时,咱们老是要写一大堆的findViewById,枯燥又没什么意义。面试
虽然也有一些诸如ButterKnife之类的第三方库,专门用于对findViewById的用法进行简化,可是ButterKnife仍是要经过注解来让控件与资源id之间进行绑定,并不算是很是方便。缓存
而kotlin-android-extensions插件的出现则让这一状况彻底发生了改变,咱们能够不用再编写烦琐的findViewById代码,同时能用一种很是简便的写法进行替代。微信
好比说这里有一个布局文件activity_main.xml:markdown
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical">
<TextView android:id="@+id/viewToShowText" android:layout_width="wrap_content" android:layout_height="wrap_content" />
</LinearLayout>
复制代码
很是简单,布局文件中只有一个TextView控件,它的id是viewToShowText。数据结构
那么,若是我想要在MainActivity中去设置TextView控件的内容,使用Java语言的话一般须要这样写:ide
public class MainActivity extends AppCompatActivity {
@Override
protected 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一遍,你必定会抓狂的。oop
那么若是是使用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整体来讲其实很是简单,它的目的只有一个,就是为了不编写findViewById,这和它另一个很是复杂的兄弟DataBinding相比有明显的区别。
要想使用ViewBinding须要注意两件事。第一,确保你的Android Studio是3.6或更高的版本。第二,在你项目工程模块的build.gradle中加入如下配置:
android {
...
buildFeatures {
viewBinding true
}
}
复制代码
这样准备工做就完成了。接下来我会从Activity、Fragment、Adapter、引入布局这4个方面,分别讨论ViewBinding的用法。
一旦启动了ViewBinding功能以后,Android Studio会自动为咱们所编写的每个布局文件都生成一个对应的Binding类。
Binding类的命名规则是将布局文件按驼峰方式重命名后,再加上Binding做为结尾。
好比说,前面咱们定义了一个activity_main.xml布局,那么与它对应的Binding类就是ActivityMainBinding。
固然,若是有些布局文件你不但愿为它生成对应的Binding类,能够在该布局文件的根元素位置加入以下声明:
<LinearLayout xmlns: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: ActivityMainBinding
override 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和在Activity基本是同样的。
这里我仍是经过代码的方式进行演示,顺便介绍一下Fragment与Activity中ViewBinding用法的异同。
假设咱们有一个布局文件叫fragment_main.xml,那么启用ViewBinding功能以后,则必然会生成一个与其对应的FragmentMainBinding类。
若是咱们想要在MainFragment中去显示这个布局,就能够这样写:
class MainFragment : Fragment() {
private var _binding: FragmentMainBinding? = null
private 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;
@Override
public View onCreateView(@NotNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
binding = FragmentMainBinding.inflate(inflater, container, false);
return binding.getRoot();
}
@Override
public void onDestroyView() {
super.onDestroyView();
binding = null;
}
}
复制代码
这两段代码实现的功能以及所想表达的意思是彻底相同的,可是明显Java版本要更加好理解一点。
接下来,让咱们再来探讨一下在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">
<ImageView android:id="@+id/fruitImage" android:layout_width="40dp" android:layout_height="40dp" android:layout_gravity="center_horizontal" android:layout_marginTop="10dp" />
<TextView android: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.fruitImage
val 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。
引入布局通常有两种方式,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" >
<Button android:id="@+id/back" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_centerVertical="true" android:text="Back" />
<TextView android:id="@+id/title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:text="Title" android:textSize="20sp" />
<Button android: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: ActivityMainBinding
override 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">
<Button android:id="@+id/back" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_centerVertical="true" android:text="Back" />
<TextView android:id="@+id/title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:text="Title" android:textSize="20sp" />
<Button android: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">
<include layout="@layout/titlebar" />
</LinearLayout>
复制代码
而后修改MainActivity中的代码,以下所示:
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private lateinit var titlebarBinding: TitlebarBinding
override 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版》,点击此处查看详情。
关注个人技术公众号“郭霖”,每一个工做日都有优质技术文章推送。