Android
ConstraintLayout

ConstraintLayoutでアスペクト比を動的に設定し高さの最大値を設定する

動作環境

  • com.android.support.constraint:constraint-layout:1.1.0-beta4
    • 1.0.2では期待した動作をしませんでした

実現したいこと

  • WebAPIからImageViewに表示する画像のURLを取得し、そのURLの画像を表示する
  • WebAPIから画像のアスペクト比を取得し、画像取得前にImageViewの高さと幅をfixさせておく
    • これをしないと画像表示時に画面がかくつく
  • 画像の横幅の最大値は画面幅
  • 画像の高さの最大値は固定値(今回の例では220dp)

実現したコード

xml

  • AndroidのDataBindingを使用しています
  • xmlの一部を抜粋しています
  • mainImageUrlは画像のURLです
  • mainImageSizeRatioは幅の比率を1とした場合の高さの比率です
        <android.support.constraint.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal">

            <ImageView
                heightForRatio="@{1}"
                widthForRatio="@{mainImageSizeRatio}"
                maxHeightDp="@{220}"
                imageUrl="@{mainImageUrl}"
                android:layout_width="match_parent"
                android:layout_height="0dp"
                android:adjustViewBounds="true"
                android:scaleType="fitCenter"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintLeft_toLeftOf="parent"
                app:layout_constraintRight_toRightOf="parent"
                app:layout_constraintTop_toTopOf="parent" />

        </android.support.constraint.ConstraintLayout>

BindingAdapter

  • アスペクト比と高さの最大値を設定するコードは以下のとおりです
  • layout_constraintWidth_maxlayout_constraintHeight_maxを使ってみましたが期待した動きはしませんでした
@BindingAdapter("widthForRatio", "heightForRatio", "maxHeightDp")
fun View.setDimensionRatio(width: Number, height: Number, maxHeightDp: Number?) {

    val ratioString = "$width:$height"
    val constraintLayout = parent as ConstraintLayout
    val constraintSet = ConstraintSet()
    constraintSet.clone(constraintLayout)
    constraintSet.setDimensionRatio(id, ratioString)
    constraintLayout.setConstraintSet(constraintSet)

    val (maxHeightPx, maxWidthPx) = if (height.toInt() == 0) {
        0 to 0
    } else {
        maxHeightDp?.let {
            val h = DpPixelConverter.toPixel(context, it)
            val w = (h * width.toFloat() / height.toFloat()).toInt()

            h to w
        } ?: Int.MAX_VALUE to Int.MAX_VALUE

    }



    constraintLayout.maxHeight = maxHeightPx
    /*
     * maxWidthの設定がないと、maxHeightを超える縦長画像が来た場合(その他のケースもあるかもしません)に、
     * Viewの高さがwidthとrationを元に設定され、maxHeightを無視されてしまいます。
     * その為、widthとrationを元に計算される高さをmaxHeightに収めるためにmaxWidthを設定します。
     */
    constraintLayout.maxWidth = maxWidthPx
}

参考までにimageUrlを設定するBindingAdapterも記載しておきます。

@BindingAdapter("imageUrl")
fun ImageView.loadWebImage(imageUrl: String?) {
    Picasso.with(context).load(imageUrl).into(this)
}

参考にしたページ