ConstraintLayout relative to ImageView dimensions

孤人 提交于 2019-12-01 11:48:37

Here is a easier way to place a widget precisely on an ImageView or any other view that will retain it placement regardless of any change in size of the ImageView as long as the aspect ratio remains constant. In the first proposed solution of my other answer, I suggested using bias to place the widget within the ImageView. Unfortunately, a change in the relative size of the widget to to the size of the ImageView causes the widget to shift on the image which is not desireable.

To get around this, I suggest placing a 1x1 pixel Space view on the ImageView using bias. Because the Space view is just 1x1 pixel in size, it can be placed anywhere within the ImageView by using bias. It's relative position will remain constant as the ImageView changes size due to rotation on a single device or because the app is being hosted on devices with different screen sizes. It is just important that the aspect ratio of the image remain constant regardless of the size of the screen.

Once the 1x1 pixel view is placed, the widget can be attached through constraints. In the example below, I have chosen to center the widgets for the eyes on the placement pixels by constraining all sides of each eye widget to the corresponding sides of its placement pixel.

Here are some images showing the effect. Screen with varying sizes that I have tested show the same result.

Nexus 6 Portrait

Nexus 6 Landscape

Because the ImageViews for the eyes are not scaled, they appear smaller on the Nexus 10 screen. The eyes are placed in the same location as on the Nexus 6. Scaling the eyes is a different matter.

Nexus 10 Landscape

Here is the layout showing the constraints:

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>

You can also achieve the same goal by wrapping the ImageView in a ConstraintLayout and setting the width and height of the ImageView to match_parent. You can then use percent guidelines to position a widget on the ImageView. This method introduces another level in the layout hierarchy but may be easier to understand.

Here is XML that will demonstrate this method.

XML using guidelines within nested 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>

If you want to position a view within another view, take a look at Centering positioning and bias in ConstraintLayout.

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:

Here is a TextView positioned 20% from the left side of the image and 70% from the top. The positioning will remain constant with different screen sizes, but you will have to make sure the aspect ratio stays constant; otherwise, the TextView will drift.

Here is the XML for the image above:

<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>

Another way to handle positioning within a view that provides more precise targeting is to define a horizontal and vertical chain of Space views that use weights to divide up the container view. Here is the same layout as above but with a vertical boundary between two Space views at 20% of the width of the ImageView. An additional two Space views have a horizontal boundary at 70% of the container view. See Chains in the documentation for ConstraintLayout.

Here is the 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>
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!