一、前言
最近放寒假了,终于有空做项目了,想着把之前的一些项目重构一下,碰巧重构到 view Binding 这块,之前都是用 kotlin 的那个扩展,其实刚开始做项目的时候这个就已经废弃了,由于当时自己有点懒,没去学习这个新的代替方案,所以就成为一个历史遗留的问题,参考官方文档
view Binding 官方文档,这里只是针对目前使用的几个场景进行重构,其他未涉及的场景可以同理得出。
二、环境的配置
- Android Studio 的版本至少需要3.6及以上
在build.gradle 配置文件下添加以下语句,不过现在的版本貌似是默认启用view Binding
android { ... viewBinding { enabled = true } }
由于绑定类是创建布局文件即生成的,故想要忽略某个布局需要在根布局添加以下内容
<LinearLayout ... tools:viewBindingIgnore="true" > ... </LinearLayout>
三、view Binding 基本用法
首先利用布局绑定的类进行创建一个对象,并对其进行初始化,然后就获得一个viewBinding的对象,用此对象进行获取布局所对应的view以及布局下的相关控件,首先说明一下自动生成的命名规则,自动去掉_空格等非字母字符,其他单词采取首字母大写的命名法则最后加个Binding结尾,举个例子
//布局名字
fragment_name.xml
//生成类名
FragmentNameBinding
binding自动调用布局里面的id也发生了一些变化,变成驼峰的命名法,同样会忽略掉非字母字符,举个例子
//布局里面某个控件的id
android:id="@+id/item_name"
//生成的变量名
FragmentNameBinding.itemName
1. Activity 中的用法
首先创建一个延迟初始化的binding变量,然后在创建activity时进行初始化,设置当前的view,只需用binding.root即可获取
private lateinit var binding:FragmentNameBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = FragmentNameBinding.inflate(layoutInflater)
setContentView(binding.root)
...
}
2. Fragment 中的用法
这个和Activity差不多,就是初始化不一样,这边采用一种比较安全的策略使得binding在fragment内部不能被修改,binding的get方法为_binding的,外部可以改变binding的值,内部不行
private var _binding: ResultProfileBinding? = null
//断言binding非空
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = ResultProfileBinding.inflate(inflater, container, false)
return binding.root
}
3. Adapter 中的用法
这个稍微麻烦一点,不过原理还是一样的,下面这个例子即可说明,针对那些使用findViewById进行了一些优化,这里涉及item的复用,注意变量的作用域范围,考虑binding的声明位置
class TestAdapter(private val fragment:Fragment,private val testList: List<Test>):RecyclerView.Adapter<TestAdapter.ViewHolder>(){
//对于holder参数进行了改造
inner class ViewHolder(binding: TestItemBinding):RecyclerView.ViewHolder(binding.root){
val fundName = binding.name
}
//在这里进行binding的初始化,这里对bingding的赋值和fragment类似
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder{
val binding = TestItemBinding.inflate(LayoutInflater.from(parent.context),parent,false)
binding.root.setOnClickListener {
val name = binding.name
}
return ViewHolder(binding)
}
//这里其实基本不需要改动,但却是用binding的用法进行的
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.name = "test"
}
override fun getItemCount() = testList.size
}
4.使用include包含布局的嵌套布局
这个刚开始也有点懵,官方文档好像没写,后来想想,包含布局不是也可以当成一个控件使用,所以最后采用在包含控件前添加id实现了调用
<ScrollView
...
<LinearLayout
...
<include
android:id="@+id/basic"
layout="@layout/basic"/>
</LinearLayout>
...
</ScrollView>
//如果上述布局名字是fragment_basic.xml,则采用下述方式获得layout的binding
val basic = FragmentBasicBinding.inflate(layoutInflater)
basic.basic
//这个即为layout basic的binding
当然还有更简单的想法,直接再定义一个layout所对应的binding,如果实在不会获取的话,跟上面几种情况类似就不过多说明了
四、总结
总体来讲这次重构是比较成功的,以前使用kotlin扩展虽然方便但是还是比较容易出错,尤其是不同布局采用相同的id名时经常弄错,用了view binding之后就不需要担心这个了,与视图相绑定,安全性大大提高,同时也简化了传统的获取id的方法,是一次比较愉快的重构。