Android自定义View嵌套后宽度异常收缩问题求助
我有三个自定义View:SmallBannerView、LargeBannerView,以及作为二者容器的BannerView。单独展示前两个View时显示正常,但嵌套到BannerView后,二者会向左侧收缩,调整外层宽度参数只能增大视图框架,内部布局完全没有变化。我尝试移除maxLines、ellipsize等参数,问题依然存在。
各View布局代码
SmallBannerView布局
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="88dp" android:layout_height="88dp" android:id="@+id/small_banner_view" xmlns:tools="http://schemas.android.com/tools" xmlns:app="http://schemas.android.com/apk/res-auto" android:background="@drawable/small_banner_card"> <ImageView app:layout_constraintTop_toTopOf="parent" app:layout_constraintStart_toStartOf="parent" android:layout_margin="4dp" android:id="@+id/image" android:layout_width="35dp" android:layout_height="35dp" tools:src="@drawable/bonus_badge"/> <TextView android:id="@+id/right_title" android:layout_marginTop="4dp" android:layout_marginHorizontal="6dp" app:layout_constraintTop_toBottomOf="@id/image" android:layout_width="match_parent" android:layout_height="14dp" android:textColor="@color/black" tools:text="Акции" android:textSize="12sp" android:fontFamily="sans-serif-medium" android:gravity="end" android:includeFontPadding="false" android:maxLines="1" android:ellipsize="end"/> <TextView android:id="@+id/left_title" android:layout_marginTop="6dp" android:layout_marginHorizontal="10dp" app:layout_constraintTop_toBottomOf="@id/right_title" android:textStyle="bold" android:layout_width="match_parent" android:layout_height="20dp" android:textColor="@color/black" tools:text="2 в 1" android:textSize="16sp" android:fontFamily="sans-serif-medium" android:gravity="start" android:includeFontPadding="false" android:maxLines="1" android:ellipsize="end"/> </androidx.constraintlayout.widget.ConstraintLayout>
预期显示:
LargeBannerView布局
<?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/large_banner_view" android:layout_width="match_parent" android:layout_height="88dp" android:background="@drawable/large_banner_card"> <ImageView android:id="@+id/image" android:layout_width="190dp" android:layout_height="40dp" app:layout_constraintTop_toTopOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" tools:src="@drawable/banner_1_img" /> <TextView android:layout_marginTop="4dp" android:layout_marginHorizontal="12dp" android:id="@+id/title" android:gravity="center" android:layout_width="match_parent" android:layout_height="18dp" android:textSize="16sp" android:textStyle="bold" android:textColor="@color/black" tools:text="Мангоооооо 100% бонусов" app:layout_constraintTop_toBottomOf="@id/image" android:includeFontPadding="false" android:maxLines="1" android:ellipsize="end"/> <TextView android:layout_marginBottom="4dp" android:layout_marginHorizontal="32dp" android:layout_width="match_parent" android:layout_height="20dp" android:textSize="8sp" android:id="@+id/description" android:textColor="@color/black" app:layout_constraintTop_toBottomOf="@+id/title" tools:text="Покупайте что-то по что-то и что-то что-то по что-то и что-точто-то " android:includeFontPadding="false" android:maxLines="2" android:ellipsize="end"/> </androidx.constraintlayout.widget.ConstraintLayout>
预期显示:
BannerView容器布局
<?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="88dp" android:layout_marginVertical="10dp" android:layout_marginHorizontal="20dp"> <com.prod.solution.impl.ui.banner.LargeBannerView android:id="@+id/large" android:layout_width="300dp" android:layout_height="match_parent" app:layout_constraintStart_toStartOf="parent" tools:layout_editor_absoluteY="0dp" /> <com.prod.solution.impl.ui.banner.SmallBannerView android:id="@+id/small" android:layout_width="88dp" android:layout_height="match_parent" app:layout_constraintStart_toEndOf="@+id/large" tools:layout_editor_absoluteY="0dp" /> </androidx.constraintlayout.widget.ConstraintLayout>
实际显示:
自定义View的Kotlin实现
class BannerView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0, ) : RelativeLayout(context, attrs, defStyleAttr) { private var binding: BannerViewBinding init { binding = BannerViewBinding.inflate(LayoutInflater.from(context)) addView(binding.root) } fun setupBanner(model: BannerUiModel) { binding.large.setUpLarge(model.largeBanner) binding.small.setUpSmall(model.smallBanner) } } class LargeBannerView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0, ) : RelativeLayout(context, attrs, defStyleAttr) { private var binding: LargeBannerViewBinding init { binding = LargeBannerViewBinding.inflate(LayoutInflater.from(context)) addView(binding.root) } fun setUpLarge(model: LargeBannerUiModel) { binding.title.text = model.title binding.description.text = model.description binding.image.setImageDrawable(AppCompatResources.getDrawable(context, model.imageResId)) } } class SmallBannerView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0, ) : RelativeLayout(context, attrs, defStyleAttr) { private var binding: SmallBannerViewBinding init { binding = SmallBannerViewBinding.inflate(LayoutInflater.from(context)) addView(binding.root) } fun setUpSmall(model: SmallBannerUiModel?) { binding.rightTitle.text = model?.rightLabel binding.leftTitle.text = model?.leftLabel val imageId = model?.imageResId if (imageId != null) binding.image.setImageDrawable(AppCompatResources.getDrawable(context, imageId)) } }
预期整体显示:
解决方案
问题根源是自定义View初始化时,加载布局没有传入当前View作为父容器,导致布局根节点的match_parent等参数无法正确继承父View的约束,内部布局只会按自身wrap_content尺寸渲染。
1. 修改自定义View的初始化逻辑
在inflate布局时,传入当前View作为父容器,并设置attachToRoot = true:
修改后的BannerView
class BannerView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0, ) : RelativeLayout(context, attrs, defStyleAttr) { private var binding: BannerViewBinding init { binding = BannerViewBinding.inflate(LayoutInflater.from(context), this, true) } fun setupBanner(model: BannerUiModel) { binding.large.setUpLarge(model.largeBanner) binding.small.setUpSmall(model.smallBanner) } }
修改后的LargeBannerView
class LargeBannerView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0, ) : RelativeLayout(context, attrs, defStyleAttr) { private var binding: LargeBannerViewBinding init { binding = LargeBannerViewBinding.inflate(LayoutInflater.from(context), this, true) } fun setUpLarge(model: LargeBannerUiModel) { binding.title.text = model.title binding.description.text = model.description binding.image.setImageDrawable(AppCompatResources.getDrawable(context, model.imageResId)) } }
修改后的SmallBannerView
class SmallBannerView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0, ) : RelativeLayout(context, attrs, defStyleAttr) { private var binding: SmallBannerViewBinding init { binding = SmallBannerViewBinding.inflate(LayoutInflater.from(context), this, true) } fun setUpSmall(model: SmallBannerUiModel?) { binding.rightTitle.text = model?.rightLabel binding.leftTitle.text = model?.leftLabel val imageId = model?.imageResId if (imageId != null) binding.image.setImageDrawable(AppCompatResources.getDrawable(context, imageId)) } }
2. 优化BannerView容器的约束(可选)
如果希望LargeBannerView自动填充父容器剩余空间,将其宽度改为0dp并添加右侧约束:
<com.prod.solution.impl.ui.banner.LargeBannerView android:id="@+id/large" android:layout_width="0dp" android:layout_height="match_parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toStartOf="@+id/small" />
修改后,内部布局会正确继承父View的尺寸,不会再出现收缩问题。
内容的提问来源于stack exchange,提问作者A1pha
相关产品推荐
相关产品推荐

