安卓疑难布局问题解决:仿微信对话框中,如何控制对话框的文本自适应且各组件不超出父组件

情景描述

在安卓组件 XML 布局中,有时候会遇到较复杂的约束关系。比方说,对于下面这个消息对话框中的消息文本,希望它可以文本框大小自适应,且在各组件先后位置确定的情况下,各组件都不超出父组件。限定只能使用 XML 来完成这一功能。

如下图所示,当文本只有一行时,文本框的宽度自动调整至正好包裹文本。

在这里插入图片描述

如下图所示,当文本有多行时,文本框的宽度存在一个最大值,且不会将其它组件挤出屏幕外。文本框的高度则伸长至正好包裹文本。

在这里插入图片描述

有些读者可能很敏感,没错,这就是高仿微信的消息对话框。当然,实际上的对话框,其左边的背景是白色的,这里为了方便演示,设置成了绿色的。

问题建模

上面的情景可以抽象为以下问题,且为了便于说明,笔者作了一张图如下。


已知父组件 P 中有子组件 a、b、c。其中,a、c 的尺寸是固定的,b 的尺寸不是固定的。需要实现的是:

  1. 如图所示,保持 a、b、c 之间的顺序不变,且保持以下横向间距不变:
    1. a 左侧与父组件 P 的左侧的间距
    2. a、b、c 之间的间距
  2. b 的尺寸不固定,它的尺寸会自动根据 b 中的内容作调整,但它的最大横向尺寸会受如下约束:
    1. a、c 均不能超出父组件 P
    2. c 右侧与父组件 P 的右侧的间距不能小于某个给定的值
  3. 限定只能使用 XML 来实现本功能。

在这里插入图片描述

扫描二维码关注公众号,回复: 17094833 查看本文章

问题解决

下面给出了实现上述情形中的代码。


【注意】

以下的实现方案是错误的:

  1. 方案 1:父组件 P 使用 LinearLayout 布局,然后在 b 中设置 android:layout_weight="1" 来自动调整 b 的尺寸,其它组件不设置此项。

    错误理由:这会导致 b 的尺寸固定为 b 的最大尺寸,而上述情景希望当 b 的原始尺寸没有达到最大尺寸时是不固定的。

  2. 方案 2:父组件 P 使用 RelativeLayout 布局,然后在 b 中设置 android:layout_width="0dp" 来自动调整 b 的尺寸,其它组件不设置此项。

    错误理由:同方案 1。

  3. 方案 3:父组件使用 ConstraintLayout 布局,然后简单设置父组件 P 内各组件的约束关系。

    错误理由:这会导致 a、b、c 分散,中间会留很大的空隙。而上述情景希望 a、b、c 之间的间距是固定的。


<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/dialog_bar_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
    <androidx.constraintlayout.widget.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

        <!--
        app:layout_constraintHorizontal_chainStyle="packed" 是为了保证以本组件开头的后续的组件可以贴在一起,
        不然它们就会分散开,中间会留很大的空隙。此代码只能在第一个组件上加
         -->
        <RelativeLayout
                android:id="@+id/avatar_layout"
                android:layout_width="36dp"
                android:layout_height="36dp"
                android:layout_marginStart="8dp"
                android:layout_marginEnd="8dp"
                android:layout_marginTop="0dp"
                android:layout_marginBottom="0dp"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintEnd_toStartOf="@+id/msg_layout"
                app:layout_constraintHorizontal_bias="0"
                app:layout_constraintTop_toTopOf="parent"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintVertical_bias="0"
                app:layout_constraintHorizontal_chainStyle="packed">

            <ImageView
                    android:id="@+id/avatar"
                    android:layout_width="36dp"
                    android:layout_height="36dp"
                    android:layout_alignParentStart="true"
                    android:layout_alignParentTop="true"
                    android:scaleType="centerInside"
                    android:src="@drawable/my_default_avatar" />

        </RelativeLayout>

        <!--
        app:layout_constrainedWidth="true" 是为了保证文本框在扩大时,所有组件都不会超出屏幕之外。
        如果不加本代码,在 app:layout_constraintHorizontal_chainStyle="packed" 下,
        就算有其它代码来约束边界,一样会超出屏幕之外。此代码只能在面积会变化的组件上加
        -->
        <RelativeLayout
                android:id="@+id/msg_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="0dp"
                android:layout_marginEnd="12dp"
                app:layout_constraintStart_toEndOf="@+id/avatar_layout"
                app:layout_constraintEnd_toStartOf="@+id/msg_status_layout"
                app:layout_constraintHorizontal_bias="0"
                app:layout_constraintTop_toTopOf="parent"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintVertical_bias="0.5"
                app:layout_constrainedWidth="true"
                android:background="@drawable/msg_box_me_bg">

            <TextView
                    android:id="@+id/msg"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginStart="8dp"
                    android:layout_marginEnd="8dp"
                    android:layout_marginTop="8dp"
                    android:layout_marginBottom="8dp"
                    android:text="测试文本"
                    android:textColor="@color/text_color_black"
                    android:textSize="16sp"
                    android:textIsSelectable="true"
                    android:longClickable="true"
                    android:layout_centerInParent="true" />

        </RelativeLayout>

        <RelativeLayout
                android:id="@+id/msg_status_layout"
                android:layout_width="18dp"
                android:layout_height="18dp"
                android:layout_marginTop="0dp"
                android:layout_marginStart="0dp"
                android:layout_marginEnd="20dp"
                app:layout_constraintStart_toEndOf="@+id/msg_layout"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintHorizontal_bias="0"
                app:layout_constraintTop_toTopOf="parent"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintVertical_bias="0"
                android:clickable="true">

            <ImageView
                    android:id="@+id/error_icon"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:layout_centerVertical="true"
                    android:scaleType="centerInside"
                    android:src="@drawable/error_icon" />
        </RelativeLayout>

    </androidx.constraintlayout.widget.ConstraintLayout>

</RelativeLayout>

猜你喜欢

转载自blog.csdn.net/wangpaiblog/article/details/132659320