Android自定义TravelContactDetailsWidget添加到布局后不显示,求排查
我开发了一个继承自ConstraintLayout的自定义View——TravelContactDetailsWidget,代码实现如下:
class TravelContactDetailsWidget : ConstraintLayout { private lateinit var binding : TravelContactDetailsWidgetBinding constructor(context: Context) : super(context) { initView() } constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { initView() } constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle) { initView() } private fun initView() { val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater binding = TravelContactDetailsWidgetBinding.inflate(inflater) } }
对应的布局文件内容:
<?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"> <androidx.appcompat.widget.LinearLayoutCompat android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:id="@+id/tvContactDetails" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:layout_marginTop="16dp" android:fontFamily="sans-serif-medium" android:text="@string/label_countries" android:textColor="@color/white_label_primary_color" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> </androidx.appcompat.widget.LinearLayoutCompat> </layout>
我将该自定义Widget添加到Fragment布局中:
<androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="match_parent"> <androidx.appcompat.widget.SwitchCompat android:id="@+id/switch1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginEnd="16dp" app:layout_constraintBottom_toBottomOf="@+id/tvUseCardAbroad" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.2" app:layout_constraintStart_toEndOf="@+id/tvUseCardAbroad" app:layout_constraintTop_toTopOf="@+id/tvUseCardAbroad" android:theme="@style/SBGSwitch" /> <TextView android:id="@+id/tvUseCardAbroad" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:layout_marginTop="16dp" android:layout_marginEnd="8dp" android:text="TextView" app:layout_constraintEnd_toStartOf="@+id/switch1" app:layout_constraintHorizontal_bias="0.8" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <TextView android:id="@+id/tvUseCardAbroadDisclaimerText" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginTop="24dp" android:layout_marginEnd="16dp" android:text="@string/label_activate_card_note" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="@+id/tvUseCardAbroad" app:layout_constraintTop_toBottomOf="@+id/tvUseCardAbroad" tools:visibility="invisible" /> <com.myapp.mobile.android.travel.TravelDateRangePickerWidget android:id="@+id/travelDateRangePickerWidget" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="8dp" app:layout_constraintTop_toBottomOf="@+id/tvUseCardAbroadDisclaimerText" android:visibility="visible"/> <com.myapp.mobile.android.travel.TravelCountrySelectorWidget android:id="@+id/travelCountrySelectorWidget" android:layout_width="match_parent" android:layout_height="200dp" android:layout_marginTop="8dp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/travelDateRangePickerWidget"/> <com.myapp.mobile.android.travel.TravelContactDetailsWidget android:layout_width="match_parent" android:layout_height="120dp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/travelCountrySelectorWidget" /> </androidx.constraintlayout.widget.ConstraintLayout>
但运行应用后,该自定义Widget所在区域为空,无法显示。请问我可能遗漏了什么配置或代码?
核心问题:布局未附加到自定义View
代码中initView()方法调用TravelContactDetailsWidgetBinding.inflate(inflater)时未指定父容器,导致inflate生成的布局并未添加到TravelContactDetailsWidget实例中。有两种修复方式:- inflate时直接传入当前View作为父容器并设置
attachToRoot=true:private fun initView() { val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater binding = TravelContactDetailsWidgetBinding.inflate(inflater, this, true) } - inflate后手动将布局根View添加到自定义View:
private fun initView() { val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater binding = TravelContactDetailsWidgetBinding.inflate(inflater) addView(binding.root) }
- inflate时直接传入当前View作为父容器并设置
布局属性无效且结构冗余
自定义布局的根容器是LinearLayoutCompat,但内部TextView使用了app:layout_constraintXXX系列属性——这类属性仅对ConstraintLayout的子View生效,在LinearLayoutCompat下完全无效,建议删除。同时根容器layout_height设为match_parent,但自定义View在Fragment布局中是固定120dp高度,可能导致内容显示异常,建议改为wrap_content:<?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android"> <androidx.appcompat.widget.LinearLayoutCompat android:layout_width="match_parent" android:layout_height="wrap_content"> <TextView android:id="@+id/tvContactDetails" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:layout_marginTop="16dp" android:fontFamily="sans-serif-medium" android:text="@string/label_countries" android:textColor="@color/white_label_primary_color" /> </androidx.appcompat.widget.LinearLayoutCompat> </layout>可选优化:对齐继承结构
自定义View继承自ConstraintLayout,但布局根容器是LinearLayoutCompat,若无需特殊布局逻辑,可将布局根容器改为ConstraintLayout,减少结构冗余并充分利用继承的布局特性。
内容的提问来源于stack exchange,提问作者George

