使用ViewBinding修改内部布局TextView为何UI未更新?
我需要修改嵌套在内部ConstraintLayout中的TextView文本,使用binding.lyricViewTitle.text = it.title时,调试显示binding.lyricViewTitle的文本已经更改,但UI并未更新;而改用binding.lyricView.findViewById<TextView>(R.id.lyric_view_title).text = it.title时,文本和UI均能正常更新。想知道二者的区别,以及ViewBinding的内部逻辑是什么?
对应的布局文件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/constraint_layout" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <androidx.media3.ui.PlayerControlView android:id="@+id/player_view" android:layout_width="match_parent" android:layout_height="match_parent" app:controller_layout_id="@layout/custom_exo_player_control_view" app:hide_on_touch="false" app:repeat_toggle_modes="one" app:show_shuffle_button="true" app:show_timeout="0" /> <androidx.constraintlayout.widget.ConstraintLayout android:id="@+id/lyric_view" android:layout_width="0dp" android:animateLayoutChanges="true" android:layout_height="0dp" android:background="@color/white" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/player_view"> <TextView android:id="@+id/lyric_view_title" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_margin="15dp" android:ellipsize="end" android:maxLines="1" android:textSize="24sp" android:textStyle="bold" app:layout_constraintEnd_toStartOf="@id/lyric_view_close" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" tools:text="Love wins all Love wins all Love wins all " /> <!-- 省略其他布局代码 --> </androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
二者的核心区别
binding.lyricViewTitle是ViewBinding在编译时生成绑定类时,缓存的第一个匹配该id的View实例,后续调用都是直接使用这个缓存的对象。binding.lyricView.findViewById<TextView>(R.id.lyric_view_title)是在运行时,从lyric_view这个指定布局范围内,实时查找匹配id的View实例,只会返回该布局内部的目标View。
问题根源与ViewBinding内部逻辑
id冲突是关键
你的布局中PlayerControlView通过app:controller_layout_id加载了自定义控制栏布局custom_exo_player_control_view,如果这个自定义布局里也存在一个id为lyric_view_title的TextView,就会出现id重复的情况。ViewBinding的缓存规则
ViewBinding在编译时会遍历整个布局(包括控件动态加载的子布局)里所有带id的View,按照XML中View的声明顺序缓存实例。如果遇到相同id的View,后面的实例会被前面的覆盖,绑定对象里只会保留第一个被遍历到的那个——也就是自定义控制栏里的TextView,而非你要修改的、嵌套在lyric_view里的那个。
这就导致你修改binding.lyricViewTitle时,实际修改的是控制栏里的TextView(可能处于不可见状态),所以UI没变化。findViewById的范围查找特性
findViewById是从调用它的View(这里是lyric_view)的子布局范围内查找,只会返回该布局内部的lyric_view_title,所以修改后能正常更新UI。
内容的提问来源于stack exchange,提问作者4rigener

