ConstraintLayout中锚点约束移除时如何动态调整视图边距
ConstraintLayout 条件边距适配问题
现有布局
现有卖家信息模块使用ConstraintLayout实现,完整布局代码如下:
<androidx.constraintlayout.widget.ConstraintLayout android:id="@+id/SellerProfileContainer" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="16dp" android:layout_marginBottom="16dp" android:layout_marginEnd="16dp" android:visibility="@{sellerFullName != null ? View.VISIBLE : View.GONE}" app:layout_constraintTop_toBottomOf="@id/Title" app:layout_constraintStart_toStartOf="@id/Title" app:layout_goneMarginTop="24dp"> <ImageView android:id="@+id/SellerProfileImage" android:layout_width="60dp" android:layout_height="60dp" android:layout_marginTop="16dp" android:layout_marginBottom="16dp" android:layout_marginEnd="16dp" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toBottomOf="parent" android:contentDescription="@{sellerProfileImageAccessibilityText}" android:scaleType="centerCrop" android:visibility="@{sellerProfileImage != null ? View.VISIBLE : View.GONE}" android:src="@{sellerProfileImage}" /> <TextView android:id="@+id/SellerProfile" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:layout_marginTop="16dp" android:contentDescription="@{sellerFullName}" android:text="@{sellerFullName.text}" android:visibility="@{sellerFullName != null ? View.VISIBLE : View.GONE}" app:layout_constraintTop_toTopOf="@id/SellerProfileImage" app:layout_constraintBottom_toBottomOf="@id/SellerProfileImage" app:layout_constraintStart_toEndOf="@id/SellerProfileImage" app:layout_constraintEnd_toEndOf="parent" tools:text="Seller Profile Name" /> <TextView android:id="@+id/SellerRatings" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{sellerFeedbackPercent}" android:contentDescription="@{sellerFeedbackPercent}" android:textColor="@color/style_guide_neutral6" android:visibility="@{sellerFeedbackPercent != null ? View.VISIBLE : View.GONE}" app:layout_constraintStart_toStartOf="@id/SellerProfile" app:layout_constraintTop_toBottomOf="@id/SellerProfile" tools:text = "100% positive rating"/> </androidx.constraintlayout.widget.ConstraintLayout>
布局参考效果:
待实现规则
- 当
sellerProfileImage不为空时,卖家头像控件SellerProfileImage和卖家名称控件SellerProfile之间保留16dp间距 - 当
sellerProfileImage为空时,卖家名称控件SellerProfile与父容器ConstraintLayout的起始边距为0
尝试的方案与报错
最初尝试通过DataBinding直接绑定android:layout_marginStart属性实现逻辑:
android:layout_marginStart="@{uxContent.sellerProfileImage != null ? @dimen/margin_16dp : @dimen/margin_0dp}"
其中margin_16dp对应16dp尺寸、margin_0dp对应0dp尺寸,编译时抛出错误:
Cannot find a setter for <android.widget.TextView android:layout_marginStart> that accepts parameter type 'float' If a binding adapter provides the setter, check that the adapter is annotated correctly and that the parameter type matches.
解决方案
方案1:利用ConstraintLayout原生GoneMargin特性(推荐,零额外代码)
现有代码已经给SellerProfileImage配置了头像为空时自动GONE的逻辑,不需要写自定义适配器,直接用ConstraintLayout的GoneMargin机制即可满足需求,修改步骤:
- 删除
SellerProfile上写死的android:layout_marginStart="16dp"属性 - 保留
SellerProfile原有约束app:layout_constraintStart_toEndOf="@id/SellerProfileImage"不变 - 给
SellerProfile新增属性app:layout_goneMarginStart="0dp" - 保留
SellerProfileImage上已有的android:layout_marginEnd="16dp"属性即可
实现逻辑:
- 头像存在时,
SellerProfileImage为可见状态,它自身的16dp右外边距会自动作为两个控件之间的间距,满足第一条规则 - 头像不存在时,
SellerProfileImage为GONE状态,ConstraintLayout会自动忽略GONE控件的边距,转而使用SellerProfile配置的goneMarginStart=0dp,此时卖家名称直接对齐父容器起始位置,满足第二条规则
方案2:自定义BindingAdapter(适合复杂边距场景)
如果有更复杂的动态边距逻辑,不适合用GoneMargin实现,可以自定义BindingAdapter补全margin属性的DataBinding支持,Kotlin代码示例:
import android.view.View import android.view.ViewGroup import androidx.databinding.BindingAdapter object ViewBindingAdapters { @JvmStatic @BindingAdapter("android:layout_marginStart") fun setStartMargin(view: View, marginPx: Float) { val params = view.layoutParams as ViewGroup.MarginLayoutParams params.marginStart = marginPx.toInt() view.layoutParams = params } }
添加该适配器后,之前写的DataBinding边距绑定表达式即可正常编译运行。
内容的提问来源于stack exchange,提问作者Kartik
相关产品推荐
相关产品推荐

