Android DrawerLayout头部无法通过View Binding访问userDataHeader问题
年初开发的旧项目中可直接在MainActivity代码中设置drawerHeader的内容,当前重构项目时,无法在MainActivity中通过binding访问抽屉头部控件。
布局采用常规结构:Activity下嵌套DrawerLayout,DrawerLayout内包含NavigationView,NavigationView通过app:headerLayout属性引入drawer_header布局。原先可通过binding.layoutDrawer.userDataHeader直接访问头部控件,现在无论如何都无法识别userDataHeader。
相关代码
主Activity布局
<?xml version="1.0" encoding="utf-8"?> <layout 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"> <data> <variable name="mainActivity" type="com.kerubyte.engagecommerce.presentation.ui.activity.MainActivity" /> </data> <androidx.drawerlayout.widget.DrawerLayout android:id="@+id/layout_drawer" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".presentation.ui.activity.MainActivity"> <androidx.constraintlayout.widget.ConstraintLayout android:id="@+id/layout_activity_main_root" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".presentation.ui.activity.MainActivity"> <androidx.constraintlayout.widget.ConstraintLayout android:id="@+id/layout_actionbar_root" android:layout_width="match_parent" android:layout_height="?actionBarSize" android:background="@color/primaryColor" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent"> <ImageView android:id="@+id/image_open_drawer_action" android:layout_width="32dp" android:layout_height="32dp" android:layout_marginStart="10dp" android:onClick="@{() -> mainActivity.openDrawerMenu()}" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:srcCompat="@drawable/ic_hamburger" /> <ImageView android:id="@+id/image_open_profile_action" android:layout_width="32dp" android:layout_height="32dp" android:layout_marginEnd="24dp" android:onClick="@{() -> mainActivity.onProfileClick()}" app:layout_constraintBottom_toBottomOf="@+id/image_open_cart_action" app:layout_constraintEnd_toStartOf="@+id/image_open_cart_action" app:layout_constraintTop_toTopOf="@+id/image_open_cart_action" app:srcCompat="@drawable/ic_person" /> <ImageView android:id="@+id/image_open_cart_action" android:layout_width="32dp" android:layout_height="32dp" android:layout_marginEnd="12dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent" app:srcCompat="@drawable/ic_cart" /> <TextView android:id="@+id/text_user_cart_items_number" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="10dp" android:text="0" android:textSize="16sp" app:layout_constraintBottom_toBottomOf="@+id/image_open_cart_action" app:layout_constraintEnd_toEndOf="@+id/image_open_cart_action" app:layout_constraintHorizontal_bias="0.55" app:layout_constraintStart_toStartOf="@+id/image_open_cart_action" app:layout_constraintTop_toTopOf="@+id/image_open_cart_action" /> </androidx.constraintlayout.widget.ConstraintLayout> <fragment android:id="@+id/nav_host_fragment" android:name="androidx.navigation.fragment.NavHostFragment" android:layout_width="match_parent" android:layout_height="0dp" app:defaultNavHost="true" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="1.0" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/layout_actionbar_root" app:layout_constraintVertical_bias="1.0" app:navGraph="@navigation/navigation" /> </androidx.constraintlayout.widget.ConstraintLayout> <com.google.android.material.navigation.NavigationView android:id="@+id/layout_navigation_menu" app:headerLayout="@layout/drawer_header" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_gravity="start" android:maxWidth="300dp" app:menu="@menu/drawer_menu" /> </androidx.drawerlayout.widget.DrawerLayout> </layout>
抽屉头布局
<?xml version="1.0" encoding="utf-8"?> <layout 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"> <androidx.constraintlayout.widget.ConstraintLayout android:id="@+id/user_data_header" android:layout_width="match_parent" android:layout_height="wrap_content"> <com.makeramen.roundedimageview.RoundedImageView android:id="@+id/image_user_avatar" android:layout_width="80dp" android:layout_height="80dp" android:layout_marginTop="16dp" android:scaleType="centerCrop" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:riv_oval="true"/> <View android:id="@+id/view_avatar_support" android:layout_width="1dp" android:layout_height="1dp" app:layout_constraintBottom_toBottomOf="@+id/image_user_avatar" app:layout_constraintEnd_toEndOf="@+id/image_user_avatar" app:layout_constraintStart_toStartOf="@+id/image_user_avatar" app:layout_constraintTop_toTopOf="@+id/image_user_avatar" /> <TextView android:id="@+id/text_user_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="8dp" android:layout_marginBottom="8dp" android:text="TextView" app:layout_constraintBottom_toTopOf="@+id/text_user_email" app:layout_constraintStart_toEndOf="@+id/image_user_avatar" /> <TextView android:id="@+id/text_user_email" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="8dp" android:layout_marginBottom="8dp" android:text="TextView" app:layout_constraintBottom_toBottomOf="@+id/image_user_avatar" app:layout_constraintStart_toEndOf="@+id/image_user_avatar" /> </androidx.constraintlayout.widget.ConstraintLayout> </layout>
MainActivity代码
@AndroidEntryPoint class MainActivity : AppCompatActivity() { private val mainViewModel: ActivityMainViewModel by viewModels() lateinit var binding: ActivityMainBinding private lateinit var navigationView: NavigationView private lateinit var navController: NavController override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = DataBindingUtil .setContentView( this, R.layout.activity_main ) navigationView = binding.layoutNavigationMenu navController = Navigation.findNavController( this, R.id.nav_host_fragment ) NavigationUI.setupWithNavController(navigationView, navController) setBindings() setupObserver() } private fun updateDrawerHeader(user: User) { binding.layoutDrawer.userDataHeader.text_first_name_value_header.text = user.firstName binding.layoutDrawer.userDataHeader.text_last_name_value_header.text = user.lastName binding.layoutDrawer.userDataHeader.text_email_value_header.text = user.email } }
Data Binding只会为当前布局文件中直接声明的控件生成绑定关系,通过app:headerLayout属性给NavigationView动态引入的头部布局,不属于主布局的直接子层级,不会被包含到ActivityMainBinding的生成规则中,因此无法通过主布局的binding对象直接访问头部控件。
另外你原有代码中引用的text_first_name_value_header、text_last_name_value_header这两个id在头布局中不存在,实际头布局里对应的控件id是text_user_name、text_user_email,需要对应修改。
手动绑定NavigationView的头部布局获取Binding实例即可:
- 声明抽屉头布局的Binding变量
- 在onCreate中从NavigationView取出头部View,绑定到对应的Binding类
- 修改更新头部的方法,直接通过头部Binding访问控件
修改后的完整代码如下:
@AndroidEntryPoint class MainActivity : AppCompatActivity() { private val mainViewModel: ActivityMainViewModel by viewModels() lateinit var binding: ActivityMainBinding private lateinit var navigationView: NavigationView private lateinit var navController: NavController // 声明头部Binding变量 private lateinit var headerBinding: DrawerHeaderBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = DataBindingUtil .setContentView( this, R.layout.activity_main ) navigationView = binding.layoutNavigationMenu // 绑定头部布局,注意如果有多个头布局,修改getHeaderView的索引即可 headerBinding = DrawerHeaderBinding.bind(navigationView.getHeaderView(0)) navController = Navigation.findNavController( this, R.id.nav_host_fragment ) NavigationUI.setupWithNavController(navigationView, navController) setBindings() setupObserver() } private fun updateDrawerHeader(user: User) { // 直接通过头部Binding访问控件 headerBinding.textUserName.text = "${user.firstName} ${user.lastName}" headerBinding.textUserEmail.text = user.email // 如需设置头像也可以直接访问 // headerBinding.imageUserAvatar.setImageURI(...) } }
如果修改后仍然提示Binding类不存在,先执行clean project再rebuild项目,确保所有Binding类正确生成即可。
内容的提问来源于stack exchange,提问作者Kerubyte

