背景
之前我们通过Kotlin Android Extensions来访问布局文件中的元素,但是这个现在被废弃了,原因如下:
- 空安全:res下的任何id都可以被访问,有可能因访问了非当前Layout下的id而出错
- 兼容性:只能在kotlin中使用,java不能用
- 局限性:不能跨模块使用
访问布局中元素方法
- findViewById
- ButterKnife
- DataBinding
- Kotlin Android Extensions
- ViewBinding
ViewBinding
2019年Google I/O大会上公布的一款Android视图绑定工具:ViewBinding。使用方式类似DataBinding,但相比DataBinding,ViewBinding是一个更轻量级、更纯粹的findViewById的替代方案。它具有如下优点:
- 类型安全: ViewBinding会基于布局中的View生成类型正确的属性。比如,在布局中放入了一个 TextView ,视图绑定就会暴露出一个 TextView 类型的属性供开发中使用。
- 空安全:ViewBinding会检测某个视图是否只在某些配置下存在,并依据结果生成带有 @Nullable 注解的属性,所以即使在多种配置下定义的布局文件,视图绑定依然能够保证空安全。
- ViewBinding生成的绑定类是一个Java类,并且添加了Kotlin注解,可以很好地支持 Java 和 Kotlin 两种编程语言。
使用详解
- Activity中基础使用方式
- 修改build.gradle文件
在android节点下增加buildFeatures 信息,具体如下:
android {
...
buildFeatures {
viewBinding = true
}
}
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.textView1.text = "textView1"
}
}
注意:ActivityMainBinding是根据布局文件activity_main.xml来生成的。
- 代码变化点说明
之前的setContentView(R.layout.activity_main) 修改为了:
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
访问布局文件中元素时,使用:binding.textView1 这样的命名方式即可。 2. 布局中引用其他布局
<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/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="111111" />
<include
android:id="@+id/subView"
layout="@layout/view_sub" />
</LinearLayout>
注意:必须要给被引用布局设置id属性。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="222222" />
</LinearLayout>
binding.subView.textView2.text = "textView2"
- 引用merge布局
<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/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="111111" />
<include layout="@layout/view_merge" />
</LinearLayout>
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<TextView
android:id="@+id/textView3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="333333" />
</merge>
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private lateinit var viewMergeBinding: ViewMergeBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
viewMergeBinding = ViewMergeBinding.bind(binding.root)
viewMergeBinding.textView3.text = "textView3"
}
}
- 在Activity基类中使用
abstract class BaseActivity<T : ViewBinding> : AppCompatActivity() {
protected lateinit var binding: T
abstract fun initBinding(): T
abstract fun init()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
binding = initBinding()
setContentView(binding.root)
init()
}
}
class MainActivity : BaseActivity<ActivityMainBinding>() {
override fun initBinding() = ActivityMainBinding.inflate(layoutInflater)
override fun init() {
binding.textView1.text = "textView1"
}
}
- Fragment用法
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/holo_blue_light"
android:gravity="center">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="第一个Fragment" />
</LinearLayout>
class FirstFragment : Fragment() {
private var _binding: FragmentFirstBinding? = null
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding =
FragmentFirstBinding.inflate(LayoutInflater.from(container?.context), container, false)
return _binding?.root
}
override fun onDestroyView() {
_binding = null
super.onDestroyView()
}
}
- Fragment基类用法
class FirstFragment : BaseFragment<FragmentFirstBinding>() {
override fun initBinding() = FragmentFirstBinding.inflate(layoutInflater)
override fun init() {
_binding?.textView1?.text="textView1"
}
}
- RecyclerView中使用
篇幅有限,只列出有变化的Adapter类:
class RvAdapter : RecyclerView.Adapter<RvAdapter.MyViewHolder>() {
private var mDataList = mutableListOf<String>()
private lateinit var mContext: Context
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
mContext = parent.context
val itemBinding =
ItemLayoutBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return MyViewHolder(mContext, itemBinding)
}
override fun onBindViewHolder(viewHolder: MyViewHolder, position: Int) {
val data = mDataList[position]
viewHolder.bind(data)
}
fun setData(dataList: List<String>) {
mDataList.clear()
mDataList.addAll(dataList)
notifyDataSetChanged()
}
override fun getItemCount(): Int = mDataList.size
class MyViewHolder(private var context: Context, private var itemBinding: ItemLayoutBinding) :
RecyclerView.ViewHolder(itemBinding.root) {
fun bind(data: String) {
//更新UI上nameTv展示内容
itemBinding.nameTv.text = data
//设置点击事件
itemBinding.root.setOnClickListener {
Toast.makeText(context, data, Toast.LENGTH_SHORT).show()
}
}
}
}
关于我
厦门大学计算机专业|华为八年高级工程师 十年软件开发经验,5年编程培训教学经验 目前从事编程教学,软件开发指导,软件类毕业设计指导。
参考资料
https://blog.csdn.net/qq_20521573/article/details/110278319 https://weilu.blog.csdn.net/article/details/109557820 https://www.jianshu.com/p/f284dd13b953
|