Jetpack之ViewBinding的使用

ViewBinding介绍

        ViewBinding是android jetpack的一个特性,ViewBinding总体来说其实非常简单,它的目的只有一个,就是为了避免编写findViewById。因为他会为每个 XML 布局文件生成一个绑定类。绑定类的实例包含在相应布局xml文件中具有 ID 的所有视图的直接引用。

先看看有啥优点心知肚明咱们再学

        1、提高编译速度 因为不需要在编译期处理大量的annotation(注释的解析)的逻辑。之前android 大名鼎鼎的bufferknife 也是为了解决代码中大量的findviewbyid的问题,但是缺点是增加了编译速度,因为bufferknife 编译时需要处理大量的annotation的逻辑。连butterknife 的作者已经宣布不维护Butter Knife,推荐使用ViewBinding了。
        2、就是为了避免编写findViewById(直接通过bind引用即可)。
        3、减少控件类型转换带来的错误(防止你用比如text的id给Button对象带来的运行期错误)。

知道了优点就学学咋用吧

1、准备工作

要想使用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。因为他生成的是类,所以我们就可以去直接去引用使用。

2、真实场景使用

a、Activity使用

假设有一个布局是 activity_main.xml

<LinearLayout
    xmlns:tools="http://schemas.android.com/tools"
    ...>
    <Button
         android:text="这是按钮"
         android:id="@+id/test_view_binding"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"/>
</LinearLayout>

在Activity如何引用

 class MainActivity : Activity() {//首先声明全局binding变量
     private lateinit var binding: ActivityMainBinding
     
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
         //再通过生成的binding去加载该布局
         binding = ActivityMainBinding.inflate(layoutInflater)
         //调用Binding类的getRoot()函数可以得到activity_main.xml中根元素的实例
         val view = binding.root
         //将根视图传递到 setContentView(),使其成为屏幕上的活动视图
         setContentView(view)
         //使用binding实例获取到控件
         binding.testViewBinding.text = "button" //更改id为testViewBinding的内容}
 }

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

b、Fragment中使用

在Fragment中使用ViewBinding和在Activity基本是一样的,但是也有一些不同。主要是因为他们的生命周期就是不同的。(这里布局文件省略,就叫fragment_main.xml吧)

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
    }
}

         这里Fragment加载布局就是用的inflate去加载的。和正常Fragment加载布局如出一辙。
         大家可能发现了为什么不用lateinit关键字对binding变量进行延迟初始化。
         这是因为:binding变量只有在onCreateView与onDestroyView才是可用的。因为我们fragment的生命周期和activity的不同,fragment 可以超出其视图的生命周期,如果不将这里置为空,有可能引起内存泄漏。所以我们要在onCreateView中创建,onDestroyView置空

c、Adapter中使用

RecycleView的子项布局(fruit_item.xml)

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    .....>

    <ImageView
        android:id="@+id/fruitImage"
        ..... />

    <TextView
        ..... />
</LinearLayout>

Adapter中如何去使用

class FruitAdapter(val fruitList: List<Fruit>) : RecyclerView.Adapter<FruitAdapter.ViewHolder>() {

    //Myholder接受FruitItemBinding参数,RecyclerView.ViewHolder接受的是一个View,通过这个binding.root返回一个root
    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 {
        //首先调用FruitItemBinding的inflate函数去加载fruit_item.xml的布局
        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直接使用它自己的成员变量
        holder.fruitImage.setImageResource(fruit.imageId)
        holder.fruitName.text = fruit.name
    }

    override fun getItemCount() = fruitList.size

}
d、引入布局(include)的使用

引入布局一般有两种方式,include和merge。假设我们有如下引用title_bar.xml布局,主布局activity_main.xml。
被引入布局title_bar.xml

 <?xml version="1.0" encoding="utf-8"?>
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     .....>
     <Button
         android:text="include"
         android:id="@+id/test_include"
         ....../></LinearLayout>

主布局activity_main.xml

 <?xml version="1.0" encoding="utf-8"?>
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     .....>
     
     <!--在include标签中添加id属性-->
     <include
         android:id="@+id/title_bar"  
         //注意要写id不然ViewBinding是关联不到title_bar.xml中的控件的。
         layout="@layout/title_bar"/>
     ...
 </LinearLayout>

Activity中我们怎么去引用呢

 class MainActivity : Activity() {
     //首先声明变量
     private lateinit var binding: ActivityMainBinding
 ​
     override fun onCreate(savedInstanceState: Bundle?) {
         binding = ActivityMainBinding.inflate(layoutInflater)
         //调用Binding类的getRoot()函数可以得到activity_main.xml中根元素的实例
         val view = binding.root
         //将根视图传递到 setContentView(),使其成为屏幕上的活动视图
         setContentView(view)
         //直接使用include标签的id,然后再根据include的id引用include布局里面的id
         binding.titleBar.testInclude.text = "hello"
     }
 }

e、引入布局(include)使用merge标签

         merge:使用merge标签引入的布局在某些情况下可以减少一层布局的嵌套,而更少的布局嵌套通常就意味着更高的效率。具体merge用处请看:Android布局优化技巧
好了先看跟普通引入有什么区别吧:
         首先我们在include的时候不可以再给他Id了,因为merge标签并不是一个布局,所以我们无法像刚才那样在include的时候给它指定一个id。不然会报错崩溃。
merge标签布局title_bar.xml

 <merge xmlns:android="http://schemas.android.com/apk/res/android">
 
     <Button
         android:text="include"
         android:id="@+id/test_include"
         ....../>
 </merge>

引入布局activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     .....>
     
     <!--在include标签中添加id属性-->
     <include
         //android:id="@+id/title_bar"  //因为merge不能用id
         layout="@layout/title_bar"/>
     ...
 </LinearLayout>

那么问题来了没有id了,ViewBinding怎么去绑定呢,一起来看看吧。
Activity利用ViewBinding绑定

 private lateinit var binding: ActivityMainBinding
 //声明变量
 private lateinit var titleBarBinding: TitleBarBinding
 override fun onCreate(savedInstanceState: Bundle?) {
     super.onCreate(savedInstanceState)
     binding = ActivityMainBinding.inflate(layoutInflater)//调用TitleBarBind的bind函数让title_bar.xml和我们的activity_main.xml关联起来
     titleBarBinding = TitleBarBinding.bind(binding.root)
     val view = binding.root
     setContentView(view)
     //直接使用titlrBarBinding变量引用控件
     titleBarBinding.testInclude.text = "button"}

         在onCreate()函数中,我们调用TitlebarBinding.bind()函数,让titlebar.xml布局和activity_main.xml布局能够关联起来。
接下来的事情就很简单了,直接使用titlebarBinding变量就可以引用到titlebar.xml中定义的各个控件了。

结尾

好了,这大概就是关于ViewBinding的所有内容了。下一篇文章我会讲解DataBinding的使用。
最后呢我还会把ViewBinding(视图绑定)和DataBinding(数据绑定)的区别讲解给大家。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值