尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

findViewById 所有可能的 null

findViewById 所有可能的 null
📅 发布时间:2026/6/18 0:16:02
findViewById 所有可能的 null

findViewById 所有可能的 null

情况1

MainActivity.kt

package io.github.helloxmlimport android.content.Context
import android.content.Intent
import android.os.Bundle
import android.util.Log
import android.view.View
import android.widget.FrameLayout
import android.widget.TextView
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.graphics.Insets
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompatprivate const val TAG: String = "MainActivity"internal class MainActivity internal constructor() : AppCompatActivity() {internal companion object {internal fun startActivity(context: Context) {context.startActivity(Intent(context, MainActivity::class.java))}}private val tvClick: TextView? by lazy {findViewById<TextView>(R.id.tv_click)}override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)enableEdgeToEdge()setContentView(R.layout.activity_main)ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v: View, insets: WindowInsetsCompat ->val systemBars: Insets = insets.getInsets(WindowInsetsCompat.Type.systemBars())v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)Log.i(TAG, "onCreate -> v: ${v.javaClass}")insets}initListener()}private fun initListener() {tvClick?.setOnClickListener {Log.i(TAG, "initListener container: ${findViewById<FrameLayout>(R.id.container)}")}}
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:id="@+id/main"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity"><TextViewandroid:id="@+id/tv_click"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Hello World!"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent" /><io.github.helloxml.MyFrameLayoutandroid:id="@+id/container"android:layout_width="match_parent"android:layout_height="200dp"app:layout_constraintTop_toBottomOf="@id/tv_click"/></androidx.constraintlayout.widget.ConstraintLayout>

MyFrameLayout.kt

package io.github.helloxmlimport android.content.Context
import android.util.AttributeSet
import android.widget.FrameLayoutinternal class MyFrameLayout internal constructor(context: Context, attributeSet: AttributeSet) : FrameLayout(context, attributeSet) // 1
internal class MyFrameLayout internal constructor(context: Context, attributeSet: AttributeSet) : FrameLayout(context) // 2

分别观察1和2看log是否返回MyFrameLayout,下面我们进行原理剖析。

打断点查看

AppCompatActivity.java

@NonNull
public AppCompatDelegate getDelegate() {if (mDelegate == null) {mDelegate = AppCompatDelegate.create(this, this);}return mDelegate;
}
@Override
public <T extends View> T findViewById(@IdRes int id) {return getDelegate().findViewById(id);
}

上面的findViewById返回值应该使用@Nullable标注,但是没有非常奇怪,新手可能会遇到空指针异常。
AppCompatDelegate.java

@Nullable
public abstract <T extends View> T findViewById(@IdRes int id);

查看ViewGroup.java


/*** {@hide}*/
@Override
protected <T extends View> T findViewTraversal(@IdRes int id) {if (id == mID) { // 1return (T) this;}final View[] where = mChildren;final int len = mChildrenCount;for (int i = 0; i < len; i++) {View v = where[i];if ((v.mPrivateFlags & PFLAG_IS_ROOT_NAMESPACE) == 0) {v = v.findViewById(id);if (v != null) {return (T) v;}}}return null;
}

情况1和情况2的唯一区别是mID一个为-1,一个不为-1;
接下来查看View.java

public View(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {this(context);mSourceLayoutId = Resources.getAttributeSetSourceResId(attrs);// ...final int N = a.getIndexCount();for (int i = 0; i < N; i++) {int attr = a.getIndex(i);switch (attr) {// ...case com.android.internal.R.styleable.View_id:mID = a.getResourceId(attr, NO_ID);break;// ...}}
}

因为internal class MyFrameLayout internal constructor(context: Context, attributeSet: AttributeSet) : FrameLayout(context),没有走到com.android.internal.R.styleable.View_id:分支,所有findViewById为null.
解决了为什么findViewBy为null的原因1.

情况2 比较常见

错误引用另一个布局文件的id
假设存在另一个activity_main1.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:id="@+id/main"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity"><TextViewandroid:id="@+id/tv_click"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Hello World!"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent" /><io.github.helloxml.MyFrameLayoutandroid:id="@+id/container1"android:layout_width="match_parent"android:layout_height="200dp"app:layout_constraintTop_toBottomOf="@id/tv_click"/></androidx.constraintlayout.widget.ConstraintLayout>

MainActivity.java

...
Log.i(TAG, "initListener container: ${findViewById<FrameLayout>(R.id.container1)}")
...

结果一定为null.

解决方法

可以使用viewBinding
配置如下

android {buildFeatures { viewBinding = true}
}

这样几乎不会出现viewId为空的任何问题.

当然标准的自定义View一般像下面那样写。

package io.github.helloxmlimport android.content.Context
import android.util.AttributeSet
import android.widget.FrameLayoutinternal class MyFrameLayout @JvmOverloads internal constructor(context: Context,attributeSet: AttributeSet? = null,defStyleAttr: Int = 0
) : FrameLayout(context, attributeSet, defStyleAttr)

双向数据绑定

有些公司使用数据绑定dataBinding做双向数据绑定
例如

android {buildFeatures {viewBinding = truedataBinding = true}
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<layout><data><variablename="name"type="String" /></data><androidx.constraintlayout.widget.ConstraintLayout 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:id="@+id/main"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity"><TextViewandroid:id="@+id/tv_click"android:text="@={name}"android:layout_width="wrap_content"android:layout_height="wrap_content"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent" /><io.github.helloxml.MyFrameLayoutandroid:id="@+id/container1"android:layout_width="match_parent"android:layout_height="200dp"app:layout_constraintTop_toBottomOf="@id/tv_click"/></androidx.constraintlayout.widget.ConstraintLayout>
</layout>

MainActivity

package io.github.helloxmlimport android.content.Context
import android.content.Intent
import android.os.Bundle
import android.util.Log
import android.view.View
import android.widget.FrameLayout
import android.widget.TextView
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.graphics.Insets
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import io.github.helloxml.databinding.ActivityMain1Binding
import io.github.helloxml.databinding.ActivityMainBinding
import kotlin.random.Randomprivate const val TAG: String = "MainActivity"internal class MainActivity internal constructor() : AppCompatActivity() {internal companion object {internal fun startActivity(context: Context) {context.startActivity(Intent(context, MainActivity::class.java))}}private val binding: ActivityMain1Binding by lazy {ActivityMain1Binding.inflate(layoutInflater)}override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)enableEdgeToEdge()setContentView(binding.root)ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v: View, insets: WindowInsetsCompat ->val systemBars: Insets = insets.getInsets(WindowInsetsCompat.Type.systemBars())v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)Log.i(TAG, "onCreate -> v: ${v.javaClass}")insets}initListener()}private fun initListener() {binding.name = "Hello World"binding.tvClick.setOnClickListener {Log.i(TAG, "initListener container: ${binding.container1}")binding.name = Random.nextInt().toString()}}
}

还有一种使用的Kotlin synthetics

个人感觉还不如findViewById,因为各种空问题都解决不了还容易导入其他布局的id.
迁移文档

相关新闻

  • pbootcms如何实现留言内容自动发送到QQ邮箱(PbootCMS留言自动发送至QQ邮箱的实现方法)
  • 网站打开提示“No input file specified.”
  • VMware vSAN 9.0.1.0 - 数据中心存储虚拟化

最新新闻

  • 2026淮南中考300多分的孩子去哪?这所公办中职让你5年拿大专,毕业还包就业 - 我叫小周
  • # 2026年内蒙古AI搜索优化公司实力排行榜:呼和浩特市包头市技术成熟服务专业,基于AI搜索优化的5大权威推荐榜单 - 十大品牌榜
  • 温州汽车贴膜怎么选?行业盘点、门店横向对比与完整避坑攻略 - 国麟测评
  • 从用户态到内核态:系统调用原理、实现与性能优化深度解析
  • YOLOv8轻量化实战:从模型压缩到边缘部署全流程解析
  • 别被低价票务系统带偏,真正该看的是经营闭环能力 - FaiscoJeff

日新闻

  • 2026年不锈钢卷板厂家推荐排行榜:冷轧热轧/304/201不锈钢卷板,高颜值耐腐蚀源头厂家实力精选 - 企业推荐官【官方】
  • FLUX.1-dev FP8模型实战指南:24GB以下显卡高效部署方案
  • 2026佛山长途搬家价目表:跨省跨市搬家费用完整计算指南 - 从来都是英雄出少年

周新闻

  • 3步解锁iOS设备:applera1n激活锁绕过完全指南
  • 39 2026 人工智能证书终极盘点,普通人选 AI 证书可以从这些方向入手
  • Redis 暴露公网有多危险?从端口检查到补救步骤

月新闻

  • 【总结】入门篇:50句话让你记住架构核心概念
  • WeChatMsg技术方案解析:实现Mac微信数据自主管理的完整解决方案
  • WeChatMsg:革新性微信数据备份方案,打造你的专属数字记忆库

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号