ConstraintLayout 相对于 ImageView 尺寸

ConstraintLayout relative to ImageView dimensions

我目前正在开发一个小型 Android 应用程序并使用新的 ConstraintLayout。

我有一个 ImageView,其中包含一个矢量图形图像,该图像在纵横比方面应该采用最大可用 space。我用下面的代码让它工作:

<ImageView
    android:id="@+id/imageView"
    android:layout_width="0dp"
    android:layout_height="0dp"
    android:layout_margin="@dimen/margin"
    android:src="@drawable/image"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    />

现在,我想在准确的位置放置多个自定义视图(按钮)。使用约束指南,我想出了以下内容:

<android.support.constraint.Guideline
    android:id="@+id/guideline_x"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    app:layout_constraintGuide_percent="0.20"
    tools:layout_editor_absoluteX="..."
    tools:layout_editor_absoluteY="..."
    />

<android.support.constraint.Guideline
    android:id="@+id/guideline_y"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    app:layout_constraintGuide_percent="0.20"
    tools:layout_editor_absoluteX="..."
    tools:layout_editor_absoluteY="..."
    />

<com.example.android.myCustomView
    android:id="@+id/myCustomView"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:onClick="doSomething"
    app:layout_constraintLeft_toLeftOf="@+id/guideline_x"
    app:layout_constraintTop_toTopOf="@+id/guideline_y"
    />

这对于我最初测试过的特定设备非常有效。但是:一旦设备尺寸发生变化,自定义视图就会放置在错误的位置。

我正在寻找一种方法来放置自定义视图 n% 相对于图像视图的 x 坐标。我尝试将 app:layout_constraintLeft_toLeftOf="@+id/imageView" 添加到指南中,但这没有任何改变。您有什么想法可以解决这个问题吗?非常感谢!

编辑: 这里有 2 张图片可以说明这个问题。

三星 Galaxy S8:

Google 像素 XL:

红色小方块应始终位于相对于 Android 图标的完全相同的位置。

如果您想在另一个视图中放置一个视图,请查看 ConstraintLayout 中的 Centering positioning and bias

Bias

The default when encountering such opposite constraints is to center the widget; but you can tweak the positioning to favor one side over another using the bias attributes:

这里有一个 TextView,位于距离图像左侧 20% 和顶部 70% 的位置。对于不同的屏幕尺寸,定位将保持不变,但您必须确保纵横比保持不变;否则,TextView 会漂移。

这是上图的 XML:

<android.support.constraint.ConstraintLayout 
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_margin="16dp"
        android:src="@drawable/ic_android_green_24dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="8dp"
        android:layout_marginEnd="8dp"
        android:layout_marginTop="8dp"
        android:text="TextView here"
        android:textSize="24sp"
        app:layout_constraintBottom_toBottomOf="@id/imageView"
        app:layout_constraintEnd_toEndOf="@id/imageView"
        app:layout_constraintHorizontal_bias="0.2"
        app:layout_constraintStart_toStartOf="@id/imageView"
        app:layout_constraintTop_toTopOf="@id/imageView"
        app:layout_constraintVertical_bias="0.7" />
</android.support.constraint.ConstraintLayout>

另一种在提供更精确定位的视图中处理定位的方法是定义 Space 视图的水平和垂直链,这些视图使用权重来划分容器视图。这是与上面相同的布局,但两个 Space 视图之间的垂直边界位于 ImageView 宽度的 20% 处。另外两个 Space 视图在容器视图的 70% 处有一个水平边界。请参阅 ConstraintLayout 的文档中的 Chains

这里是 XML:

<android.support.constraint.ConstraintLayout 
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_margin="16dp"
        android:src="@drawable/ic_android_green_24dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Space
        android:id="@+id/space1"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:background="@android:color/holo_blue_light"
        app:layout_constraintBottom_toBottomOf="@id/imageView"
        app:layout_constraintEnd_toStartOf="@id/space2"
        app:layout_constraintHorizontal_chainStyle="packed"
        app:layout_constraintHorizontal_weight="20"
        app:layout_constraintStart_toStartOf="@id/imageView"
        app:layout_constraintTop_toTopOf="@id/imageView" />

    <Space
        android:id="@+id/space2"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:background="@android:color/holo_red_light"
        app:layout_constraintBottom_toBottomOf="@id/imageView"
        app:layout_constraintEnd_toEndOf="@id/imageView"
        app:layout_constraintHorizontal_weight="80"
        app:layout_constraintStart_toEndOf="@id/space1"
        app:layout_constraintTop_toTopOf="@id/imageView" />

    <Space
        android:id="@+id/space3"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:background="@android:color/holo_blue_light"
        app:layout_constraintBottom_toTopOf="@id/space4"
        app:layout_constraintEnd_toEndOf="@id/imageView"
        app:layout_constraintStart_toStartOf="@id/imageView"
        app:layout_constraintTop_toTopOf="@id/imageView"
        app:layout_constraintVertical_chainStyle="packed"
        app:layout_constraintVertical_weight="70" />

    <Space
        android:id="@+id/space4"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:background="@android:color/holo_red_light"
        app:layout_constraintBottom_toBottomOf="@id/imageView"
        app:layout_constraintEnd_toEndOf="@id/imageView"
        app:layout_constraintStart_toStartOf="@id/imageView"
        app:layout_constraintTop_toBottomOf="@id/space3"
        app:layout_constraintVertical_weight="30" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="TextView here"
        android:textSize="24sp"
        app:layout_constraintStart_toEndOf="@id/space1"
        app:layout_constraintTop_toBottomOf="@id/space3" />
</android.support.constraint.ConstraintLayout>

这是一种更简单的方法,可以将小部件精确地放置在 ImageView 或任何其他视图上,无论 ImageView 的大小发生任何变化,只要纵横比不变,它都会保留其位置保持不变。在我的另一个答案的第一个建议解决方案中,我建议使用 bias 将小部件放置在 ImageView 内。不幸的是,将小部件的相对大小更改为 ImageView 的大小会导致小部件在图像上移动,这是不可取的。

为了解决这个问题,我建议使用偏差在 ImageView 上放置一个 1x1 像素 Space 视图。由于 Space 视图的大小仅为 1x1 像素,因此可以使用偏置将其放置在 ImageView 中的任何位置。它的相对位置将保持不变,因为 ImageView 由于在单个设备上旋转或因为应用程序托管在具有不同屏幕尺寸的设备上而改变大小。无论屏幕大小如何,图像的纵横比保持不变是很重要的。

放置 1x1 像素视图后,可以通过约束附加小部件。在下面的示例中,我选择通过将每个眼睛小部件的所有侧约束到其放置像素的相应侧来将眼睛小部件集中在放置像素上。

这里有一些显示效果的图片。我测试过的不同尺寸的屏幕显示相同的结果。

Nexus 6 人像

Nexus 6 横向

因为眼睛的 ImageViews 没有缩放,所以它们在 Nexus 10 屏幕上显得更小。眼睛的位置与 Nexus 6 相同。缩放眼睛是另一回事。

Nexus 10 横向

这是显示约束的布局:

position_view.xml

<android.support.constraint.ConstraintLayout 
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:id="@+id/androidGreen"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:src="@drawable/android_image"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintDimensionRatio="H,1:1"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:ignore="ContentDescription" />

    <Space
        android:id="@+id/pinpoint1"
        android:layout_width="1px"
        android:layout_height="1px"
        app:layout_constraintBottom_toBottomOf="@id/androidGreen"
        app:layout_constraintEnd_toEndOf="@id/androidGreen"
        app:layout_constraintHorizontal_bias="0.373"
        app:layout_constraintStart_toStartOf="@id/androidGreen"
        app:layout_constraintTop_toTopOf="@id/androidGreen"
        app:layout_constraintVertical_bias="0.19" />

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/circle"
        app:layout_constraintBottom_toBottomOf="@id/pinpoint1"
        app:layout_constraintEnd_toEndOf="@id/pinpoint1"
        app:layout_constraintStart_toStartOf="@id/pinpoint1"
        app:layout_constraintTop_toTopOf="@id/pinpoint1"
        app:srcCompat="@drawable/circle"
        tools:ignore="ContentDescription" />

    <Space
        android:id="@+id/pinpoint2"
        android:layout_width="1px"
        android:layout_height="1px"
        android:background="@android:color/holo_red_light"
        app:layout_constraintBottom_toBottomOf="@id/androidGreen"
        app:layout_constraintEnd_toEndOf="@id/androidGreen"
        app:layout_constraintHorizontal_bias="0.625"
        app:layout_constraintStart_toStartOf="@id/androidGreen"
        app:layout_constraintTop_toTopOf="@id/androidGreen"
        app:layout_constraintVertical_bias="0.19" />

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/circle"
        app:layout_constraintBottom_toBottomOf="@id/pinpoint2"
        app:layout_constraintEnd_toEndOf="@id/pinpoint2"
        app:layout_constraintStart_toStartOf="@id/pinpoint2"
        app:layout_constraintTop_toTopOf="@id/pinpoint2"
        tools:ignore="ContentDescription" />

</android.support.constraint.ConstraintLayout>

您也可以通过将 ImageView 包装在 ConstraintLayout 中并将 ImageView 的宽度和高度设置为 match_parent 来实现相同的目标。然后,您可以使用百分比准则将小部件定位在 ImageView 上。此方法在布局层次结构中引入了另一个级别,但可能更容易理解。

这里 XML 将演示此方法。

XML 在嵌套 ConstraintLayout

中使用准则
<android.support.constraint.ConstraintLayout 
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.constraint.ConstraintLayout
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintDimensionRatio="H,1:1"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent">

        <ImageView
            android:id="@+id/androidGreen"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:src="@drawable/android_image"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            tools:ignore="ContentDescription" />

        <android.support.constraint.Guideline
            android:id="@+id/guidelineHorizontal"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            app:layout_constraintGuide_percent="0.19" />

        <android.support.constraint.Guideline
            android:id="@+id/guidelineVerticalLeft"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            app:layout_constraintGuide_percent="0.375" />

        <android.support.constraint.Guideline
            android:id="@+id/guidelineVerticalRight"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            app:layout_constraintGuide_percent="0.625" />

        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/circle"
            app:layout_constraintBottom_toBottomOf="@id/guidelineHorizontal"
            app:layout_constraintEnd_toEndOf="@id/guidelineVerticalLeft"
            app:layout_constraintStart_toStartOf="@id/guidelineVerticalLeft"
            app:layout_constraintTop_toTopOf="@id/guidelineHorizontal"
            app:srcCompat="@drawable/circle"
            tools:ignore="ContentDescription" />

        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/circle"
            app:layout_constraintBottom_toBottomOf="@id/guidelineHorizontal"
            app:layout_constraintEnd_toEndOf="@id/guidelineVerticalRight"
            app:layout_constraintStart_toStartOf="@id/guidelineVerticalRight"
            app:layout_constraintTop_toTopOf="@id/guidelineHorizontal"
            tools:ignore="ContentDescription" />

    </android.support.constraint.ConstraintLayout>
</android.support.constraint.ConstraintLayout>