ConstraintLayout2.0で追加されるFlowを使ってタグを実装する

f:id:khaki0928:20200821154451p:plain

みなさんこんにちは!

スタメン プロダクト部 モバイルアプリチームでiOSAndroidアプリ開発を行っているカーキです。

先月の29日に ConstraintLayout2.0-rc1がリリースされましたね! リリースノートには「安定版前の最後のリリース」とあるので、ConstraintLayout2.0安定版のリリースもかなり近いのではないかと思います。楽しみですね!

そこで今回のブログではConstraintLayout2.0で新たに追加されるFlowを紹介し、1つの例として自動で折り返しされるようなタグ表示を実装していきます。

Flowとは何か

ConstraintLayout2.0から追加されるFlowとは、ConstraintLayoutのChainの概念を用いて自動折り返しなどを行ってくれるヘルパーウィジェットです。 Flexboxのような折り返し表示やGridLayoutのようなカラム表示をシンプルに実現することができます

Flowを使用するメリットとしては

  1. 複数のビューをネストさせずに整列させることができる
  2. GridViewなどRecyclerViewを使うと少し手間なところを手軽に実行できる

などが考えられます。

代表的なattribute

Flowでタグを実装する際に使うであろうattributeについて先に紹介します。

flow_wrapMode

flow_wrapModeはFlowを使って表示するレイアウトの表示形式を指定します。

この形式には以下の3種類あります。

  • none
  • chain
  • aligned

それぞれについて説明します

none

単一のチェーンに沿って水平もしくは、垂直に配置することができます。

改行は行われないため、以下の画像のように要素数が多いと見切れてしまいます。

f:id:khaki0928:20200819144306j:plain
noneでの整列

chain

チェーンに沿って水平もしくは、垂直に配置することができます。

Flowの大きさに従って要素が入りきらない場合に、自動で改行を行います。

→Flexboxのようなレイアウトを実現できます

f:id:khaki0928:20200819144458j:plain
chainでの整列

aligned

改行されるという点ではchainの場合と同じです。

ただchainではなく行と列のセットで配置されるため、chainにあるような水平・垂直で個別にattributeを指定することができません。

→GridLayoutのようなレイアウトを実現できます

続いて、他のflowの主要なattributeを紹介します

constraint_referenced_ids

Flowによって制約を加えるビューのidを指定します

複数のidを指定する場合はidの間にカンマを入れて指定します

flow_horizontalGap, flow_verticalGap

水平・垂直方向の要素同士の間隔を指定することができます

flow_horizontalStyle, flow_verticalStyle

水平・垂直方向のChain Styleを指定することができます

ChainStyleには3種類ありますが、それぞれのStyleによってどのように配置されるかはConstraintLayoutのドキュメントに詳しく記載されています。

flow_horizontalBias

Flowの内部で使用できるlayout_constraintHorizontal_biasのようなもので、要素全体の重心を指定することができます

"0"で指定すればstartの方向、"1"に近づくほどendの方向に重心が寄ります。

Chainを使ってタグ表示を行う(XML)

上記のattributeを使用してタグを表示してみたものが以下になります

f:id:khaki0928:20200819144458j:plain
chainを使用したタグの表示

 <androidx.constraintlayout.helper.widget.Flow
        android:id="@+id/flowLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:layout_marginTop="20dp"
        app:constraint_referenced_ids="tag1, tag2, tag3, tag4, tag5, tag6, tag7, tag8"
        app:flow_horizontalBias="0"
        app:flow_horizontalGap="8dp"
        app:flow_horizontalStyle="packed"
        app:flow_verticalGap="8dp"
        app:flow_verticalStyle="packed"
        app:flow_wrapMode="chain"
        app:layout_constraintTop_toTopOf="parent" />

上記のxmlには記述していませんがconstraint_referenced_idsに指定しているidには並べたいビューのidを指定しています

動的にFlowにアイテムを追加する

タグを実装する場合、タグ自身を動的に追加することになります

動的に追加する際は、レイアウトファイルにおいたflowのreferencedIdsに整列したいViewのidリストを渡すことで行うことができます

class MainActivity : AppCompatActivity() {

    private fun setup(parentView: ViewGroup, tagTitles: List<String>) {

        val referenceIds = IntArray(tagTitles.size)

        for (i in tagTitles.indices) {
            val textView = TextView(this)
            textView.text = tagTitles[i]
            textView.background = getDrawable(android.R.color.holo_green_light)
            textView.id = View.generateViewId()

            parentView.addView(textView)
            referenceIds[i] = textView.id
        }

        // 生成したtextViewのidをreferenceIdsに設定する
        flowLayout.referencedIds = referenceIds
    }
}

あとは呼び出し側から親ビューとタグのタイトル配列を渡すだけです

setup(parentView, listOf("たぬき", "きつね", "うさぎ", "かめ", "いぬ", "さる"))

f:id:khaki0928:20200819144707j:plain
動的にタグを表示する

動的にアイテムを追加する場合の注意

ここで一つ注意しておきたいことがあります。

動的にアイテムを追加する場合、addViewしたアイテムに対して整列をかけるため、ロードするたびにレンダリングの処理がかかります。

静的な表示であれば問題にはなりませんが、RecyclerViewの内部のアイテムそれぞれにflowが存在すると、RecyclerViewのスクロールのたびにflowの整列処理が走るため、スクロールがカクカクするなどパフォーマンスの低下が見受けられました。

今後のFlowの改善により整列のパフォーマンスが改善されるかどうかは分かりませんが、現状ではRecyclerViewの内部で動的にFlowを使用するのは現実的ではなさそうです。

最後に

どうだったでしょうか?

Flowを使用すれば簡単に折り返し可能なタグレイアウトを実装することができます

またGridLayoutに関しても同様にして容易に表示ができるため、ちょっとしたセグメント表示などにはかなり使えるのではないでしょうか

ConstraintLayout2.0安定版のリリースがますます楽しみになってきますね

最後になりますが、スタメンではiOS/Androidエンジニアを大募集しています

興味のある方はエンジニア採用サイトをご覧ください