自定义View嵌套子View使用Databinding出现空指针异常如何解决
问题根因
你遇到的报错:
java.lang.NullPointerException: Attempt to invoke virtual method 'void androidx.appcompat.widget.AppCompatTextView.setTag(java.lang.Object)' on a null object reference
核心触发逻辑如下:
- 父Fragment的DataBinding实例生成时,会按照XML结构调用
CustomView.findViewById(R.id.nestedView)定位要绑定的TextView,为其设置绑定规则 - 你重写了
addView方法,将用户传入的子View转移到了CustomView内部的nestedViewContainer容器中,导致该TextView已经不在CustomView的直接子View树内 findViewById返回null后,DataBinding尝试对空对象调用setTag存储绑定信息,触发空指针,同时内部子View的绑定规则也无法生效
解决方案
提供两种适配方案,可根据业务场景选择:
方案1:重写findViewById方法(适配任意子View绑定,改动最小)
在CustomView类中新增findViewById重写逻辑,优先从内部容器中查找View,让父层DataBinding可以正常定位到转移后的子View:
class CustomView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 ) : ConstraintLayout(context, attrs, defStyleAttr) { var binding: CustomViewBinding = DataBindingUtil.inflate( LayoutInflater.from(context), R.layout.custom_view, this, true ) override fun addView(child: View, index: Int, params: ViewGroup.LayoutParams?) { if (child.id == R.id.static_view_container) { super.addView(child, index, params) } else { binding.nestedViewContainer.addView(child, index, params) } } // 新增重写方法 override fun <T : View?> findViewById(id: Int): T? { // 优先从嵌套子View容器查找 val nestedResult = binding.nestedViewContainer.findViewById<T>(id) if (nestedResult != null) return nestedResult // 其次从静态布局容器查找 val staticResult = binding.staticViewContainer.findViewById<T>(id) if (staticResult != null) return staticResult // 最后走系统默认查找逻辑 return super.findViewById(id) } }
该方案不需要修改XML代码,兼容任意嵌套在CustomView内部的子View的DataBinding规则。
方案2:透传绑定变量(适用于子View逻辑固定的场景)
如果CustomView内部嵌套的子View逻辑固定,你可以通过自定义属性将绑定变量透传给CustomView内部的Binding,由CustomView自己完成子View的绑定:
- 先在
res/values/attrs.xml中定义自定义属性:
<declare-styleable name="CustomView"> <attr name="nestedText" format="string"/> </declare-styleable>
- 修改CustomView类,添加变量接收和绑定逻辑:
var nestedText: String? = null set(value) { field = value binding.nestedView.text = value // 如果你用CustomView的Binding绑定,也可以直接给Binding变量赋值后调用executePendingBindings }
- 父层XML直接给CustomView设置属性即可,不需要给内部子View加绑定:
<CustomView android:id="@+id/customView" android:layout_width="match_parent" android:layout_height="wrap_content" android:visibility="@{viewModel.showView ? View.VISIBLE : View.GONE}" app:nestedText="@{viewModel.changableValue}"/>
内容的提问来源于stack exchange,提问作者Merlijn Claeys
相关产品推荐
相关产品推荐

