自定义对话框问题中的约束布局

Posted

技术标签:

【中文标题】自定义对话框问题中的约束布局【英文标题】:Constraint layout in Custom Dialog problem 【发布时间】:2021-09-07 03:24:06 【问题描述】:

我有这样的自定义对话框的 XML 文件:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_
    android:layout_
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:paddingHorizontal="15dp"
    android:paddingVertical="10dp">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/cl_header"
        android:layout_
        android:layout_
        app:layout_constraintTop_toTopOf="parent"
        android:paddingBottom="15dp">

        <TextView
            style="@style/tv_big"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintStart_toStartOf="@id/cl_header"
            android:layout_
            android:layout_
            android:textStyle="bold"
            android:text="Filter"/>
        <com.google.android.material.button.MaterialButton
            android:id="@+id/bt_reset"
            android:textColor="@color/black"
            android:layout_
            android:layout_
            style="@style/Widget.MaterialComponents.Button.OutlinedButton"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintEnd_toEndOf="@id/cl_header"
            android:text="RESET FILTERS"/>
    </androidx.constraintlayout.widget.ConstraintLayout>


    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/cl_distance"
        app:layout_constraintTop_toBottomOf="@id/cl_header"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:layout_
        android:layout_>

        <TextView
            android:id="@+id/tv_distance_title"
            style="@style/tv_medium"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            android:layout_
            android:layout_
            android:text="Distance range"/>

        <TextView
            android:id="@+id/tv_distance_result"
            android:layout_
            android:layout_
            android:textAlignment="center"
            app:layout_constraintBottom_toBottomOf="@id/tv_distance_title"
            app:layout_constraintLeft_toRightOf="@id/tv_distance_title"
            app:layout_constraintRight_toRightOf="parent"
            android:background="#ddd"
            android:padding="3dp"
            android:textColor="#333"
            android:text="0 km - 500km"/>

        <com.google.android.material.slider.RangeSlider
            android:id="@+id/slider_distance"
            app:layout_constraintTop_toBottomOf="@id/tv_distance_title"
            android:layout_
            android:layout_
            app:labelBehavior="gone"
            android:stepSize="1.0" />
    </androidx.constraintlayout.widget.ConstraintLayout>


    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/cl_location"
        android:layout_
        android:layout_
        app:layout_constraintTop_toBottomOf="@id/cl_distance"
        app:layout_constraintStart_toStartOf="parent">

        <TextView
            android:id="@+id/tv_location_title"
            style="@style/tv_medium"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            android:layout_
            android:layout_
            android:text="Location"/>
        <EditText
            android:id="@+id/et_location"
            android:layout_
            android:layout_
            app:layout_constraintTop_toBottomOf="@id/tv_location_title"
            android:hint="@string/filter_location_et_hint" />
    </androidx.constraintlayout.widget.ConstraintLayout>

    <LinearLayout
        android:layout_
        android:layout_
        app:layout_constraintTop_toBottomOf="@id/cl_location"
        app:layout_constraintRight_toRightOf="parent"
        android:layout_marginTop="10dp"
        android:orientation="horizontal">

        <Button
            android:id="@+id/bt_cancel"
            android:backgroundTint="#ccc"
            android:textColor="@color/black"
            android:layout_
            android:layout_
            android:layout_marginStart="20dp"
            android:text="CANCEL"/>
        <Button
            android:id="@+id/bt_save"
            android:layout_
            android:layout_
            android:text="save"
            android:layout_marginHorizontal="20dp"/>
    </LinearLayout>


</androidx.constraintlayout.widget.ConstraintLayout>

Android Studio 是这样渲染的:

这就是我希望在我的对话框中拥有的内容。

但是:

在物理设备和模拟器上,它看起来与在 Android Studio 中不同:

我的问题是:如何在左侧放置“过滤器”标题,并使位置 EditText 与父级匹配(在工作应用程序中)?

【问题讨论】:

您必须使用RelativeLayout(使用android:layout_toLeftOf)或LinearLayout(将其方向设置为水平并设置权重)。 与此答案无关,但 ConstraintLayout 的全部意义在于删除您拥有的这些嵌套布局。如果您要嵌套多个 ConstraintLayouts,那么只需使用 LinearLayouts。您可以在一个 ConstraintLayout 中设计整个对话框。 对了,你为什么用了这么多嵌套?我相信只有一个约束布局就足够了。 这是一个link 以获得更好的布局代码,也许这可能会对您有所帮助。是的,请考虑根据您的选择调整一些边距和内容:) 【参考方案1】:

其他人说的都是正确的,保持布局平坦对性能和这些情况都有好处,您想快速找出为什么布局没有按照您想要的方式布局;)

试试这个:

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_
    android:layout_
    android:paddingHorizontal="15dp"
    android:paddingVertical="10dp">

    <TextView
        android:layout_
        android:layout_
        android:gravity="start"
        android:text="Filter"
        app:layout_constraintBottom_toBottomOf="@id/bt_reset"
        app:layout_constraintEnd_toStartOf="@id/bt_reset"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="@id/bt_reset" />

    <com.google.android.material.button.MaterialButton
        android:id="@+id/bt_reset"
        style="@style/Widget.MaterialComponents.Button.OutlinedButton"
        android:layout_
        android:layout_
        android:text="RESET FILTERS"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent" />


    <TextView
        android:id="@+id/tv_distance_title"
        android:layout_
        android:layout_
        android:text="Distance range"
        app:layout_constraintBottom_toBottomOf="@id/tv_distance_result"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="@id/tv_distance_result" />

    <TextView
        android:id="@+id/tv_distance_result"
        android:layout_
        android:layout_
        android:layout_marginTop="15dp"
        android:background="#ddd"
        android:gravity="center"
        android:paddingHorizontal="18dp"
        android:paddingVertical="3dp"
        android:text="0 km - 500km"
        android:textColor="#333"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toBottomOf="@id/bt_reset" />

    <com.google.android.material.slider.RangeSlider
        android:id="@+id/slider_distance"
        android:layout_
        android:layout_
        android:stepSize="1.0"
        app:labelBehavior="gone"
        app:layout_constraintTop_toBottomOf="@id/tv_distance_result" />

    <TextView
        android:id="@+id/tv_location_title"
        android:layout_
        android:layout_
        android:text="Location"
        app:layout_constraintTop_toBottomOf="@id/slider_distance" />

    <EditText
        android:id="@+id/et_location"
        android:layout_
        android:layout_
        android:hint="test"
        app:layout_constraintTop_toBottomOf="@id/tv_location_title" />

    <Button
        android:id="@+id/bt_cancel"
        android:layout_
        android:layout_
        android:layout_marginTop="10dp"
        android:layout_marginEnd="20dp"
        android:backgroundTint="#ccc"
        android:text="CANCEL"
        android:textColor="@color/colorBlack"
        app:layout_constraintEnd_toStartOf="@id/bt_save"
        app:layout_constraintTop_toBottomOf="@id/et_location" />

    <Button
        android:id="@+id/bt_save"
        android:layout_
        android:layout_
        android:layout_marginTop="10dp"
        android:layout_marginEnd="20dp"
        android:text="save"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toBottomOf="@id/et_location" />

</androidx.constraintlayout.widget.ConstraintLayout>

【讨论】:

您的解决方案几乎可以工作,但是我在回答中写到的 LinearLayout 存在一个问题(我同时找到了解决方案)【参考方案2】:

我将内部 ConstraintLayouts 更改为 LinearLayouts 并使用权重。我还使用this trick 填充例如之间的空间。 “过滤器”标题和“重置过滤器”按钮。

ll_location LinearLayout 也存在问题 - 它与父级不匹配。 我换了:

<LinearLayout
        android:id="@+id/ll_location"

        android:layout_

        android:layout_
        app:layout_constraintTop_toBottomOf="@id/ll_distance_ext"
        android:orientation="vertical">

        <!--  content  -->

</LinearLayout>

进入:

<LinearLayout
        android:id="@+id/ll_location"

        android:layout_
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"

        android:layout_
        app:layout_constraintTop_toBottomOf="@id/ll_distance_ext"
        android:orientation="vertical">

        <!--  content  -->

</LinearLayout>

然后它起作用了。

我不想使用 flat 布局(为什么我使用嵌套布局)的原因是恕我直言,flat 布局使代码更难维护。 将视图拆分为组后,无论组中有多少视图,都可以轻松替换它们。

我的解决方案:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_
    android:layout_
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:paddingHorizontal="15dp"
    android:paddingVertical="10dp">

    <LinearLayout
        android:id="@+id/ll_header"
        android:layout_
        android:layout_
        app:layout_constraintTop_toTopOf="parent"
        android:paddingBottom="15dp">

        <TextView
            style="@style/tv_big"
            android:layout_
            android:layout_
            android:textStyle="bold"
            android:text="Filter"/>
        <View
            android:layout_
            android:layout_
            android:layout_weight="1" />

        <com.google.android.material.button.MaterialButton
            android:id="@+id/bt_reset"
            android:textColor="@color/black"
            android:layout_
            android:layout_
            style="@style/Widget.MaterialComponents.Button.OutlinedButton"
            android:text="reset filters"/>
    </LinearLayout>

    <LinearLayout
        android:id="@+id/ll_distance_ext"
        app:layout_constraintTop_toBottomOf="@id/ll_header"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:layout_
        android:layout_
        android:orientation="vertical">

        <LinearLayout
            android:layout_
            android:layout_
            android:orientation="horizontal">

            <TextView
                android:id="@+id/tv_distance_title"
                style="@style/tv_medium"
                android:layout_
                android:layout_
                android:text="Distance range"/>

            <View
                android:layout_
                android:layout_
                android:layout_weight="1" />

            <TextView
                android:id="@+id/tv_distance_result"
                android:layout_
                android:layout_
                android:textAlignment="center"
                android:background="#ddd"
                android:padding="3dp"
                android:textColor="#333"
                android:text="0 km - 500km"/>

        </LinearLayout>

        <com.google.android.material.slider.RangeSlider
            android:id="@+id/slider_distance"
            android:layout_
            android:layout_
            app:labelBehavior="gone"
            android:stepSize="1.0" />
    </LinearLayout>

    <LinearLayout
        android:id="@+id/ll_location"
        android:layout_
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:layout_
        app:layout_constraintTop_toBottomOf="@id/ll_distance_ext"
        android:orientation="vertical">

        <TextView
            android:id="@+id/tv_location_title"
            style="@style/tv_medium"
            android:layout_
            android:layout_
            android:text="Location"/>
        <EditText
            android:id="@+id/et_location"
            android:layout_
            android:layout_
            android:hint="@string/filter_location_et_hint" />
    </LinearLayout>

    <LinearLayout
        android:layout_
        android:layout_
        app:layout_constraintTop_toBottomOf="@id/ll_location"
        app:layout_constraintRight_toRightOf="parent"
        android:layout_marginTop="10dp"
        android:orientation="horizontal">

        <Button
            android:id="@+id/bt_cancel"
            android:backgroundTint="#ccc"
            android:textColor="@color/black"
            android:layout_
            android:layout_
            android:layout_marginStart="20dp"
            android:text="cancel"/>
        <Button
            android:id="@+id/bt_save"
            android:layout_
            android:layout_
            android:text="save"
            android:layout_marginHorizontal="20dp"/>
    </LinearLayout>


</androidx.constraintlayout.widget.ConstraintLayout>

【讨论】:

我不知道维护的难度,但它对性能有明显的影响。不仅嵌套的视图层次结构和太多的 ViewGroup,而且还有一些额外的成本,比如水平 LinearLayout 导致double layout taxation。

以上是关于自定义对话框问题中的约束布局的主要内容,如果未能解决你的问题,请参考以下文章

自定义 UITableViewCell 的自动布局约束在运行时失败

自定义布局未使用约束布局在列表视图中居中

自定义布局中约束更改的动画 UICollectionView 项目

约束布局中的 ViewPager 选项卡式活动在底部被剪切以用于 recyclerview

在 UICollectionViewCell 中使用 Storyboard 中的自动布局约束

自定义 UITtableViewCell 的布局约束不起作用