赞
踩
id kotlin-android-extensions
插件过时了,官方推荐 ViewBinding
。
以下为官方文档中的一段话:
与 findViewById 的区别
与使用 findViewById 相比,视图绑定具有一些很显著的优点:
Null 安全:由于视图绑定会创建对视图的直接引用,因此不存在因视图 ID 无效而引发 Null 指针异常的风险。此外,如果视图仅出现在布局的某些配置中,则绑定类中包含其引用的字段会使用 @Nullable 标记。
类型安全:每个绑定类中的字段均具有与它们在 XML 文件中引用的视图相匹配的类型。这意味着不存在发生类转换异常的风险。
这些差异意味着布局和代码之间的不兼容将会导致构建在编译时(而非运行时)失败。
[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"
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类都实现了该接口。
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 ... } } }
外部使用时,通常用 inflate() 的两个函数。
最终都会调用 bind()。
所以可以 不使用Binding类的 inflate(),而通过其它方式 inflate 出 rootView, 再调用XxxBinding.bind(rootView) 。
通过 XxxBinding.getRoot() ,获取到根视图,然后 被 setContentView(root)、被Fragment#onCreateView() 用作返回值…
基于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 }
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")
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 框架时,无法通过编译。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。