You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android自定义View重写addView()触发NullPointerException问题排查

问题

自定义View中重写addView(child: View?)后,使用View Binding加载布局时抛出NullPointerException,即使xml中TestView没有子View也会触发。尝试用findViewById替代Binding调用,错误情况一致,迁移到View Binding前代码正常运行。

相关代码

TestView.kt

package com.example.testandroid

import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.View
import android.widget.LinearLayout
import com.example.testandroid.databinding.ViewTestBinding

class TestView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
    LinearLayout(context, attrs) {

    var binding: ViewTestBinding

    init {
        binding = ViewTestBinding.inflate(LayoutInflater.from(context), this, true)
        binding.vTitle.text = "test title 2"
    }

    override fun addView(child: View?) {
        binding.vContainer.addView(child)
    }
}

view_test.xml

<?xml version="1.0" encoding="utf-8"?>
<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/vTitle"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="test title"/>

    <LinearLayout
        android:id="@+id/vContainer"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"/>

</LinearLayout>

FirstFragment.kt

package com.example.testandroid

import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.navigation.fragment.findNavController
import com.example.testandroid.databinding.FragmentFirstBinding

/**
 * A simple [Fragment] subclass as the default destination in the navigation.
 */
class FirstFragment : Fragment() {

    private var _binding: FragmentFirstBinding? = null
    private val binding get() = _binding!!

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        _binding = FragmentFirstBinding.inflate(inflater, container, false)
        return binding.root

    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
    }

    override fun onDestroyView() {
        super.onDestroyView()
        _binding = null
    }
}

fragment_first.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".FirstFragment">

    <com.example.testandroid.TestView
        android:id="@+id/vTest"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <TextView
            android:id="@+id/vText1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="text 1"/>

        <TextView
            android:id="@+id/vText2"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="text 2"/>

    </com.example.testandroid.TestView>
</LinearLayout>

错误信息

Caused by: java.lang.NullPointerException: Attempt to read from field 'android.widget.LinearLayout com.example.testandroid.databinding.ViewTestBinding.vContainer' on a null object reference

完整错误栈

Process: com.example.testandroid, PID: 5681
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.testandroid/com.example.testandroid.MainActivity}: android.view.InflateException: Binary XML file line #23 in com.example.testandroid:layout/activity_main: Binary XML file line #10 in com.example.testandroid:layout/fragment_first: Binary XML file line #10 in com.example.testandroid:layout/fragment_first: Error inflating class com.example.testandroid.TestView
    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3271)
    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3410)
    at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:83)
    at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
    at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2017)
    at android.os.Handler.dispatchMessage(Handler.java:107)
    at android.os.Looper.loop(Looper.java:214)
    at android.app.ActivityThread.main(ActivityThread.java:7397)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:935)
 Caused by: android.view.InflateException: Binary XML file line #23 in com.example.testandroid:layout/activity_main: Binary XML file line #10 in com.example.testandroid:layout/fragment_first: Binary XML file line #10 in com.example.testandroid:layout/fragment_first: Error inflating class com.example.testandroid.TestView
 Caused by: android.view.InflateException: Binary XML file line #10 in com.example.testandroid:layout/fragment_first: Binary XML file line #10 in com.example.testandroid:layout/fragment_first: Error inflating class com.example.testandroid.TestView
 Caused by: android.view.InflateException: Binary XML file line #10 in com.example.testandroid:layout/fragment_first: Error inflating class com.example.testandroid.TestView
 Caused by: java.lang.reflect.InvocationTargetException
    at java.lang.reflect.Constructor.newInstance0(Native Method)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:343)
    at android.view.LayoutInflater.createView(LayoutInflater.java:854)
    at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:1006)
    at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:961)
    at android.view.LayoutInflater.rInflate(LayoutInflater.java:1123)
    at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:1084)
    at android.view.LayoutInflater.inflate(LayoutInflater.java:682)
    at android.view.LayoutInflater.inflate(LayoutInflater.java:534)
    at com.example.testandroid.databinding.FragmentFirstBinding.inflate(FragmentFirstBinding.java:54)
    at com.example.testandroid.FirstFragment.onCreateView(FirstFragment.kt:23)
    at androidx.fragment.app.Fragment.performCreateView(Fragment.java:3104)
    at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:524)
    at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:261)
    at androidx.fragment.app.FragmentStore.moveToExpectedState(FragmentStore.java:113)
    at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1424)
    at androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:2968)
    at androidx.fragment.app.FragmentManager.dispatchViewCreated(FragmentManager.java:2879)
    at androidx.fragment.app.Fragment.performViewCreated(Fragment.java:3129)
    at androidx.fragment.app.FragmentStateManager.ensureInflatedView(FragmentStateManager.java:394)
    at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:260)
    at androidx.fragment.app.FragmentLayoutInflaterFactory.onCreateView(FragmentLayoutInflaterFactory.java:142)
2023-08-09 13:44:26.820 5681-5681/com.example.testandroid E/AndroidRuntime:     at androidx.fragment.app.FragmentController.onCreateView(FragmentController.java:136)
    at androidx.fragment.app.FragmentActivity.dispatchFragmentsOnCreateView(FragmentActivity.java:248)
    at androidx.fragment.app.FragmentActivity.onCreateView(FragmentActivity.java:227)
    at android.view.LayoutInflater.tryCreateView(LayoutInflater.java:1069)
    at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:997)
    at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:961)
    at android.view.LayoutInflater.rInflate(LayoutInflater.java:1123)
    at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:1084)
    at android.view.LayoutInflater.parseInclude(LayoutInflater.java:1263)
    at android.view.LayoutInflater.rInflate(LayoutInflater.java:1119)
    at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:1084)
    at android.view.LayoutInflater.inflate(LayoutInflater.java:682)
    at android.view.LayoutInflater.inflate(LayoutInflater.java:534)
    at com.example.testandroid.databinding.ActivityMainBinding.inflate(ActivityMainBinding.java:50)
    at com.example.testandroid.databinding.ActivityMainBinding.inflate(ActivityMainBinding.java:44)
    at com.example.testandroid.MainActivity.onCreate(MainActivity.kt:22)
    at android.app.Activity.performCreate(Activity.java:7825)
    at android.app.Activity.performCreate(Activity.java:7814)
    at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1307)
    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3246)
    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3410)
    at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:83)
    at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
    at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2017)
    at android.os.Handler.dispatchMessage(Handler.java:107)
    at android.os.Looper.loop(Looper.java:214)
    at android.app.ActivityThread.main(ActivityThread.java:7397)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:935)
 Caused by: java.lang.NullPointerException: Attempt to read from field 'android.widget.LinearLayout com.example.testandroid.databinding.ViewTestBinding.vContainer' on a null object reference
    at com.example.testandroid.TestView.addView(TestView.kt:21)
    at com.example.testandroid.databinding.ViewTestBinding.inflate(ViewTestBinding.java:51)
    at com.example.testandroid.TestView.<init>(TestView.kt:16)
        ... 53 more
原因分析

从错误栈可以明确问题根源:

  1. 调用ViewTestBinding.inflate(..., this, true)时,第三个参数attachToRoot为true,会触发inflate内部立即将布局根View添加到TestView实例,也就是调用addView方法。
  2. 此时binding变量还未完成赋值(binding = ...代码未执行完毕),重写的addView中访问binding.vContainer时,binding仍为null,直接抛出NPE。
  3. 用findViewById替代也无效,因为布局还未完成attach,vContainer尚未加入View树,findViewById会返回null。
解决方案

方案一:延迟attach,手动添加布局

修改ViewTestBinding.inflate的attachToRoot参数为false,手动将布局根View添加到TestView中,避开inflate过程中调用重写的addView:

init {
    binding = ViewTestBinding.inflate(LayoutInflater.from(context), this, false)
    addView(binding.root) // 手动添加布局根View到当前View
    binding.vTitle.text = "test title 2"
}

方案二:区分初始化阶段的addView调用

在重写的addView中判断,如果是添加布局根View则调用父类逻辑,否则再添加到vContainer:

private lateinit var binding: ViewTestBinding

init {
    binding = ViewTestBinding.inflate(LayoutInflater.from(context), this, true)
    binding.vTitle.text = "test title 2"
}

override fun addView(child: View?) {
    // 初始化时添加的是布局根View,调用父类逻辑
    if (child == binding.root) {
        super.addView(child)
    } else {
        // 加空安全调用避免意外
        binding.vContainer?.addView(child)
    }
}

方案三:放弃重写addView,提供自定义方法

去掉重写的addView,给TestView添加公开方法来添加子View到vContainer,避免和系统布局初始化流程冲突:

// 移除override fun addView(child: View?)

fun addContainerChild(child: View?) {
    binding.vContainer.addView(child)
}

之后需要添加子View时,调用该方法即可,不再依赖XML中直接嵌套子View的方式。


内容的提问来源于stack exchange,提问作者coreorb

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 20:09:50