Android开发:能否跨XML布局引用组件作为约束?
问题:跨布局引用RecyclerView子项组件作为约束失效
我正在开发一款练手用的账单拆分APP,这是我的第一个Android Studio项目。我想让activity_main布局里的person文本和sum文本,与RecyclerView子项内的CheckBox对齐,但直接在activity_main的XML中使用app:layout_constraintBaseline_toBaselineOf="@+id/checkBox_1"完全无效。
下面是我当前的布局代码:
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:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <Button android:id="@+id/chooseFileBtn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:layout_marginTop="16dp" android:onClick="chooseFile" android:text="@string/btn_filechoose" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <TextView android:id="@+id/person1" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:layout_marginTop="8dp" android:text="@string/txt_finn_sum" app:layout_constraintEnd_toStartOf="@+id/person2" app:layout_constraintStart_toEndOf="@+id/chooseFileBtn" app:layout_constraintTop_toTopOf="@+id/chooseFileBtn" app:layout_constraintBaseline_toBaselineOf="@+id/checkBox_1"/><!-- 此处无效 --> <TextView android:id="@+id/person2" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:layout_marginTop="8dp" android:text="@string/txt_basti_sum" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toEndOf="@+id/person1" app:layout_constraintTop_toTopOf="@+id/chooseFileBtn" /> <androidx.recyclerview.widget.RecyclerView android:id="@+id/recyclerView" android:layout_width="0dp" android:layout_height="0dp" android:layout_marginStart="16dp" android:layout_marginEnd="16dp" app:layout_constraintBottom_toTopOf="@+id/sumTotal" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/chooseFileBtn" /> <TextView android:id="@+id/sumTotal" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:layout_marginBottom="64dp" android:text="@string/txt_sumTotal" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent" /> <TextView android:id="@+id/sum1" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:layout_marginEnd="16dp" android:text="@string/txt_sum1" app:layout_constraintBaseline_toBaselineOf="@+id/sumTotal" app:layout_constraintEnd_toStartOf="@+id/sum2" app:layout_constraintStart_toEndOf="@+id/chooseFileBtn" /> <TextView android:id="@+id/sum2" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:text="@string/txt_sum2" app:layout_constraintBaseline_toBaselineOf="@+id/sumTotal" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toEndOf="@+id/person1" /> </androidx.constraintlayout.widget.ConstraintLayout>
RecyclerView子项布局(item_layout.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="wrap_content" android:orientation="horizontal"> <TextView android:id="@+id/item_name" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="6" android:text="@string/txt_item_name" android:gravity="start" /> <TextView android:id="@+id/item_price" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:text="@string/txt_item_price" android:gravity="start" android:layout_marginStart="16dp"/> <CheckBox android:id="@+id/checkBox_1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:layout_marginStart="16dp" android:gravity="center" /> <CheckBox android:id="@+id/checkBox_2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:gravity="center" android:layout_marginStart="16dp"/> </LinearLayout>
解决方法
直接跨布局引用RecyclerView子项的组件是行不通的——RecyclerView的子项是动态生成的,在activity_main布局加载时,这些子项组件还未创建,系统找不到对应的ID。推荐两种简单可行的对齐方案:
方案1:统一布局结构与权重分配
这是最适合新手的方案,通过让顶部person栏、底部sum栏与RecyclerView子项使用完全一致的布局结构和权重分配,实现天然对齐。
修改RecyclerView子项布局
把子项里的wrap_content宽度改成0dp,让权重分配更精准:
<?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="wrap_content" android:orientation="horizontal" android:paddingVertical="8dp"> <TextView android:id="@+id/item_name" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="6" android:text="@string/txt_item_name" android:gravity="center_vertical"/> <TextView android:id="@+id/item_price" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="@string/txt_item_price" android:gravity="center_vertical" android:paddingStart="16dp"/> <CheckBox android:id="@+id/checkBox_1" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:gravity="center" android:paddingStart="16dp"/> <CheckBox android:id="@+id/checkBox_2" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:gravity="center" android:paddingStart="16dp"/> </LinearLayout>
修改activity_main里的person栏
替换原来的两个TextView为同结构的LinearLayout,用占位View对应子项的item_name和item_price:
<!-- 替换原person1、person2 TextView --> <LinearLayout android:layout_width="0dp" android:layout_height="wrap_content" android:orientation="horizontal" app:layout_constraintStart_toEndOf="@+id/chooseFileBtn" app:layout_constraintTop_toTopOf="@+id/chooseFileBtn" android:layout_marginStart="16dp"> <!-- 占位,对应子项的item_name权重 --> <View android:layout_width="0dp" android:layout_height="0dp" android:layout_weight="6"/> <!-- 占位,对应子项的item_price权重 --> <View android:layout_width="0dp" android:layout_height="0dp" android:layout_weight="1"/> <TextView android:id="@+id/person1" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="@string/txt_finn_sum" android:gravity="center" android:paddingStart="16dp"/> <TextView android:id="@+id/person2" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="@string/txt_basti_sum" android:gravity="center" android:paddingStart="16dp"/> </LinearLayout>
修改activity_main里的sum栏
同样用LinearLayout对齐:
<!-- 替换原sum1、sum2 TextView --> <LinearLayout android:layout_width="0dp" android:layout_height="wrap_content" android:orientation="horizontal" app:layout_constraintStart_toEndOf="@+id/chooseFileBtn" app:layout_constraintBaseline_toBaselineOf="@+id/sumTotal" android:layout_marginStart="16dp"> <View android:layout_width="0dp" android:layout_height="0dp" android:layout_weight="6"/> <View android:layout_width="0dp" android:layout_height="0dp" android:layout_weight="1"/> <TextView android:id="@+id/sum1" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="@string/txt_sum1" android:gravity="center" android:paddingStart="16dp"/> <TextView android:id="@+id/sum2" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="@string/txt_sum2" android:gravity="center" android:paddingStart="16dp"/> </LinearLayout>
方案2:动态计算对齐位置(不推荐新手)
在RecyclerView完成加载后,获取第一个子项中CheckBox的屏幕坐标,再动态设置activity_main中文本的margin或约束。但这种方法需要处理RecyclerView滚动、布局重新测量等情况,实现复杂,容易出现适配问题。
内容的提问来源于stack exchange,提问作者Bastian
相关产品推荐
相关产品推荐

