
みなさんこんにちは!
スタメン プロダクト部 モバイルアプリチームでiOSとAndroidのアプリ開発を行っているカーキです。
先月の29日に ConstraintLayout2.0-rc1がリリースされましたね! リリースノートには「安定版前の最後のリリース」とあるので、ConstraintLayout2.0安定版のリリースもかなり近いのではないかと思います。楽しみですね!
そこで今回のブログではConstraintLayout2.0で新たに追加されるFlowを紹介し、1つの例として自動で折り返しされるようなタグ表示を実装していきます。
Flowとは何か
ConstraintLayout2.0から追加されるFlowとは、ConstraintLayoutのChainの概念を用いて自動折り返しなどを行ってくれるヘルパーウィジェットです。 Flexboxのような折り返し表示やGridLayoutのようなカラム表示をシンプルに実現することができます
Flowを使用するメリットとしては
- 複数のビューをネストさせずに整列させることができる
- GridViewなどRecyclerViewを使うと少し手間なところを手軽に実行できる
などが考えられます。
代表的なattribute
Flowでタグを実装する際に使うであろうattributeについて先に紹介します。
flow_wrapMode
flow_wrapModeはFlowを使って表示するレイアウトの表示形式を指定します。
この形式には以下の3種類あります。
- none
- chain
- aligned
それぞれについて説明します
none
単一のチェーンに沿って水平もしくは、垂直に配置することができます。
改行は行われないため、以下の画像のように要素数が多いと見切れてしまいます。

chain
チェーンに沿って水平もしくは、垂直に配置することができます。
Flowの大きさに従って要素が入りきらない場合に、自動で改行を行います。
→Flexboxのようなレイアウトを実現できます

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を使用してタグを表示してみたものが以下になります

<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("たぬき", "きつね", "うさぎ", "かめ", "いぬ", "さる"))

動的にアイテムを追加する場合の注意
ここで一つ注意しておきたいことがあります。
動的にアイテムを追加する場合、addViewしたアイテムに対して整列をかけるため、ロードするたびにレンダリングの処理がかかります。
静的な表示であれば問題にはなりませんが、RecyclerViewの内部のアイテムそれぞれにflowが存在すると、RecyclerViewのスクロールのたびにflowの整列処理が走るため、スクロールがカクカクするなどパフォーマンスの低下が見受けられました。
今後のFlowの改善により整列のパフォーマンスが改善されるかどうかは分かりませんが、現状ではRecyclerViewの内部で動的にFlowを使用するのは現実的ではなさそうです。
最後に
どうだったでしょうか?
Flowを使用すれば簡単に折り返し可能なタグレイアウトを実装することができます
またGridLayoutに関しても同様にして容易に表示ができるため、ちょっとしたセグメント表示などにはかなり使えるのではないでしょうか
ConstraintLayout2.0安定版のリリースがますます楽しみになってきますね
最後になりますが、スタメンではiOS/Androidエンジニアを大募集しています
興味のある方はエンジニア採用サイトをご覧ください