Android Jectpack-ViewBinding 实践(kotlin实现)
Posted 匆忙拥挤repeat
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android Jectpack-ViewBinding 实践(kotlin实现)相关的知识,希望对你有一定的参考价值。
文章目录
I. 文档
id kotlin-android-extensions
插件过时了,官方推荐 ViewBinding
。
以下为官方文档中的一段话:
与 findViewById 的区别
与使用 findViewById 相比,视图绑定具有一些很显著的优点:
Null 安全:由于视图绑定会创建对视图的直接引用,因此不存在因视图 ID 无效而引发 Null 指针异常的风险。此外,如果视图仅出现在布局的某些配置中,则绑定类中包含其引用的字段会使用 @Nullable 标记。
类型安全:每个绑定类中的字段均具有与它们在 XML 文件中引用的视图相匹配的类型。这意味着不存在发生类转换异常的风险。
这些差异意味着布局和代码之间的不兼容将会导致构建在编译时(而非运行时)失败。
II. 配置及简要说明
[application Module]/build.gradle
android
//viewBinding
// enabled = true
// //本写法已在 stuido4.0过时 换成如下
buildFeatures
viewBinding = true
-
插件会将 ab_cd_ef.xml 生成一个 AbCdEfBinding 的类(即去掉下划线,并驼峰式)。
-
会对布局xml中的view id 生成 驼峰式类名。
源码位置:build/generated/data_binding_base_class_source_out/debug/out/[package name]/databinding/PrintDialogPrinterConnectBinding.java
-
对
<include id="@+id/includeLayout"../>
,要想访问include layout 内的子view,子view id为 “testTv”,只需:XxxBinding.IncludeLayout.testTv。
-
若不想生成 Binding 文件。在根视图中使用
tools:viewBindingIgnore="true"
III. 源码
public interface ViewBinding
/**
* Returns the outermost @link View in the associated layout file. If this binding is for a
* @code <merge> layout, this will return the first view inside of the merge tag.
*/
@NonNull
View getRoot();
所有生成的 Binding类都实现了该接口。
IV. 一个生成类的源码
public final class PrintDialogPrinterConnectBinding implements ViewBinding
@NonNull
public ConstraintLayout getRoot()
return rootView;
@NonNull
public static PrintDialogPrinterConnectBinding inflate(@NonNull LayoutInflater inflater
return inflate(inflater, null, false);
@NonNull
public static PrintDialogPrinterConnectBinding inflate(@NonNull LayoutInflater inflater,
@Nullable ViewGroup parent, boolean attachToParent)
View root = inflater.inflate(R.layout.print_dialog_printer_connect, parent, false);
if (attachToParent)
parent.addView(root);
return bind(root);
@NonNull
public static PrintDialogPrinterConnectBinding bind(@NonNull View rootView)
// The body of this method is generated in a way you would not otherwise write.
// This is done to optimize the compiled bytecode for size and performance.
int id;
missingId:
id = R.id.dialog_printer_connect_close;
ImageView dialogPrinterConnectClose = rootView.findViewById(id);
if (dialogPrinterConnectClose == null)
break missingId;
if ...
- getRoot() 获取根视图 view
- inflate(@NonNull LayoutInflater inflatere) ==> inflate(inflate, null, false)
- inflate(@NonNull LayoutInflater inflater, @Nullable ViewGroup parent, boolean attachToParent) ==> bind(root)
- bind(@NonNull View rootView)
外部使用时,通常用 inflate() 的两个函数。
最终都会调用 bind()。
所以可以 不使用Binding类的 inflate(),而通过其它方式 inflate 出 rootView, 再调用XxxBinding.bind(rootView) 。
V. 使用方法
通过 XxxBinding.getRoot() ,获取到根视图,然后 被 setContentView(root)、被Fragment#onCreateView() 用作返回值…
VI. 封装
基于ViewBind使用反射方式;
import android.app.Dialog
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.viewbinding.ViewBinding
/**
* AppCompatActivity#onCreate()使用
*/
inline fun <reified VB: ViewBinding> AppCompatActivity.inflate(): VB
return inflateBinding<VB>(layoutInflater).apply
setContentView(this.root)
/**
* Dialog#onCreate() 使用
*/
inline fun <reified VB: ViewBinding> Dialog.inflate(): VB
return inflateBinding<VB>(layoutInflater).apply
setContentView(this.root)
/**
* 继承自 ViewGroup 使用
*/
inline fun <reified VB: ViewBinding> ViewGroup.inflate(viewGroup: ViewGroup, attachToRoot: Boolean = true): VB
return inflateBinding(LayoutInflater.from(context), viewGroup, attachToRoot)
/**
* Recycler.Adapter#onCreateViewHolder() 使用
*/
inline fun <reified VB: ViewBinding> inflate(parent: ViewGroup): VB
return inflateBinding(LayoutInflater.from(parent.context), parent, false)
/**
* 这是一个基础方法。所有创建ViewBinding对象的地方都可以直接调用。
* 反射调用 ViewBinding.inflate(layoutInflater, viewGroup, attachToRoot) 。
* 对于 Fragment、DialogFragment 都直接使用本方法。
* 调用时,viewGroup可以不传,默认为null。
*/
@Suppress("UNCHECKED_CAST")
inline fun <reified VB: ViewBinding> inflateBinding(layoutInflater: LayoutInflater, viewGroup: ViewGroup? = null, attachToRoot: Boolean = false): VB
return VB::class.java.getMethod("inflate", LayoutInflater::class.java, ViewGroup::class.java, Boolean::class.java)
.invoke(null, layoutInflater, viewGroup, attachToRoot) as VB
VI.i. 一个DialogFragment例子
class TestDialogFragment : DialogFragment()
private lateinit var mViewBind: DialogFragmentTestBinding
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View
mViewBind = inflateBinding(inflater)
//mViewBind = inflateBinding(inflater, container)
return mViewBind.root
override fun onViewCreated(view: View, savedInstanceState: Bundle?)
super.onViewCreated(view, savedInstanceState)
mViewBind.tvTest.setOnClickListener
Toast.makeText(requireContext(), "test msg", Toast.LENGTH_SHORT).show()
//activity 中启动
TestDialogFragment().show(suuportFragmentManager, "tag-test")
//fragment 中启动
TestDialogFragment().show(childFragmentManager, "tag-test")
VI.ii. 强制Fragment子类实例化ViewBinding对象
abstract class BaseExtendFragment: Fragment()
private var mBinding: ViewBinding? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View?
mBinding = (getViewBind(inflater, container, savedInstanceState) as ViewBinding)
return mBinding?.root
abstract fun <VB: ViewBinding> getViewBind(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): VB
override fun onDestroyView()
super.onDestroyView()
mBinding = null
官方文档中示例是要在 onDestroyView() 中 置空 ViewBinding 对象的。
这样抽象,还是需要每个子Fragment , 声明自己的 XxxViewBinding 对象。
/**
* desc:
* author: stone
* email: aa86799@163.com
* time: 3/10/21 15:59
*/
class MeFragment : BaseExtendFragment()
private lateinit var mViewBind: FragmentMeBinding
@Suppress("UNCHECKED_CAST")
override fun <VB : ViewBinding> getViewBind(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): VB
mViewBind = inflateBinding(inflater, container)
return mViewBind as VB
尝试过 在 BaseExtendFragment<T: ViewBinding> 这样声明。是会精简一些重复代码,然而在多重Fragment抽象与继承,加 permisstion-diapster 框架时,无法通过编译。
开发者涨薪指南 48位大咖的思考法则、工作方式、逻辑体系
以上是关于Android Jectpack-ViewBinding 实践(kotlin实现)的主要内容,如果未能解决你的问题,请参考以下文章
Android 逆向Android 权限 ( Android 逆向中使用的 android.permission 权限 | Android 系统中的 Linux 用户权限 )