TheLastBert
TheLastBert

Reputation: 466

Setting view to expand in available space up to a maximum size

I have an app with a horizontal recyclerview that displays a square image with an overlaid handle and button in the bottom corners of the image (controlled with a constraint layout).

As the images displayed are all svg graphics, I'd like each of these images to expand to fill the available vertical space. However on some large screens, I'm finding there is enough space that the images then get blown up to an overly large size and so only one or two are displayed on the screen.

What I would like is for each image in the recycler view to scale to fill the space up to a maximum size of, say, 120dp. I have tried to achieve this with a custom image view that overrides onMeasure:

class ComponentImageView : AppCompatImageView {
    constructor(context: Context?) : super(context) {}
    constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) {}

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)

        var size = 0
        val width = measuredWidth
        val height = measuredHeight
        val heightWithoutPadding = height - paddingTop - paddingBottom

        // set the dimensions
        size = heightWithoutPadding

        var dp = (size / resources.displayMetrics.density + 0.5f)
        if(dp > 120) {dp = 120f}
        val px : Int = (dp * resources.displayMetrics.density + 0.5f).toInt()

        setMeasuredDimension(px + paddingLeft + paddingRight, px + paddingTop + paddingBottom)
    }
}

And have used it in the xml like this:

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="wrap_content" android:layout_height="match_parent">

    <bw.graph.ComponentImageView
        android:id="@+id/component_block_image"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"/>

    <bw.smith.TouchableImageView
        android:id="@+id/component_block_handle"
        style="@style/ComponentListIcon"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintBottom_toBottomOf="@id/component_block_image"
        app:srcCompat="@drawable/ic_drag_handle_gray_24dp" />

    <androidx.appcompat.widget.AppCompatTextView
        android:id="@+id/component_block_label"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"/>

    <androidx.appcompat.widget.AppCompatImageButton
        android:id="@+id/component_block_edit"
        style="@style/ComponentListIcon"
        app:layout_constraintStart_toStartOf="@id/component_block_image"
        app:layout_constraintBottom_toBottomOf="@id/component_block_image"
        app:srcCompat="@drawable/ic_edit_gray_24dp" />

</androidx.constraintlayout.widget.ConstraintLayout>

While the image ends up being the correct width, the view itself still fills all the vertical height. So the result is that the image is centered in the available vertical height, and the handle and button that are intended to be overlaid appear to be floating away from the image.

Is there a way to achieve what I want? If I just manually set the android:layout_height attribute to the desired dp (e.g. between 48dp and 120dp) it looks good, I'd just like to find a way to set this in xml or programmatically so that it looks good on different screens without me having to worry.

Upvotes: 2

Views: 530

Answers (1)

Ben P.
Ben P.

Reputation: 54204

ConstraintLayout supports this out of the box, via the app:layout_constraintHeight_max attribute. Add these to the view you want to have stretch:

android:layout_height="0dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHeight_max="[your value here]"

Note that when the parent is taller than the max value given, the view will be centered (vertically) within the parent, unless you add bias:

app:layout_constraintVertical_bias="[0 - 1]"

A bias value of 0 will fix the view to the top of the screen, while 1 will fix it to the bottom. You can use any fractional value if you want to tweak the positioning (e.g., 0.33 to have it be one third from the top of the screen).

In cases where the view is not limited by the max, this bias value will have no effect.

Upvotes: 2

Related Questions