Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
[shibuya.apk #41] Jetpack Composeでグリッドに柔軟にスペースを...
Search
syarihu
April 21, 2023
Technology
3.6k
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
[shibuya.apk #41] Jetpack Composeでグリッドに柔軟にスペースを入れたい
https://shibuya-apk.connpass.com/event/277598/
syarihu
April 21, 2023
More Decks by syarihu
See All by syarihu
Recap #io25 What's new in Google Play
syarihu
0
130
[DroidKaigi 2024] Android ViewからJetpack Composeへ 〜Jetpack Compose移行のすゝめ〜 / From Android View to Jetpack Compose: A Guide to Migration
syarihu
4
5.1k
[shibuya.apk #46] Composableの枠を超えてアニメーションする / Animation beyond Composable
syarihu
0
140
[DroidKaigi 2022] 詳解Google Playの新しい定期購入 ~オファーの活用や実装例を添えて~
syarihu
0
4k
5分で分かるGoogle Playの新しいサブスクリプション / Google Play new subscription 2022
syarihu
1
2k
[DroidKaigi 2021] Google Play 定期購入 比例配分モード完全攻略ガイド / Google Play Subscription Proration Mode Complete Guide
syarihu
1
8.2k
[Money Forward Tech Drive] What's new in Google Play [Recap #io21]
syarihu
0
650
[Android 11 Meetups] Google Play Commerce からのアップデート / Android 11 Meetups Google Play Commerce
syarihu
4
3.2k
[potatotips #70] license-list-pluginを使ってOSSライセンス画面を自動生成する / license-list-plugin
syarihu
4
5.2k
Other Decks in Technology
See All in Technology
Multicaで30個のミニプロジェクトをAIエージェント運用して見えてきたこと
eiei114
1
620
AIが当たり前の組織で エンジニアはどう育つか
nishihira
1
960
AIツールを導入しても生産性はあがらない? カオナビが直面した 3つの壁と乗り越え方。/ Overcoming 3 Barriers to AI-Driven Productivity at kaonavi
kaonavi
0
160
AmplifyHostingConstructからSSRフレームワークのためのホスティング設計を考察する/amplify-hosting-construct
fossamagna
1
300
AI、CDK と協働する Full TypeScript アプリケーション開発 / Full TypeScript Application with AI and CDK
geekplus_tech
2
480
壊して学ぶAWS CDK: そのcdk deployで消えるもの、残るもの
k_adachi_01
1
480
なぜ、あなたのAPIは使われないのか? AX時代の設計原則、ガードレール、運用体制
yokawasa
1
210
クラウドを使う側から、作る側へ / 大吉祥寺.pm 2026前夜祭
fujiwara3
4
1.1k
SoccerMaster: A Vision Foundation Model for Soccer Understanding
kzykmyzw
0
160
どこまでAIに任せるか 〜確率論と決定論の境界決定〜
shukob
0
440
CDKで書くECSのベストプラクティス、 改めて考え直す2026 #cdkconf2026
makies
3
930
OpenTelemetryにおけるGoのゼロコード・コンパイル時計装について #fukuokago
quiver
0
200
Featured
See All Featured
Max Prin - Stacking Signals: How International SEO Comes Together (And Falls Apart)
techseoconnect
PRO
0
320
The Hidden Cost of Media on the Web [PixelPalooza 2025]
tammyeverts
2
380
Stop Working from a Prison Cell
hatefulcrawdad
274
21k
Helping Users Find Their Own Way: Creating Modern Search Experiences
danielanewman
31
3.3k
Marketing Yourself as an Engineer | Alaka | Gurzu
gurzu
0
260
Beyond borders and beyond the search box: How to win the global "messy middle" with AI-driven SEO
davidcarrasco
3
190
The Straight Up "How To Draw Better" Workshop
denniskardys
239
140k
Marketing to machines
jonoalderson
1
5.6k
Leveraging Curiosity to Care for An Aging Population
cassininazir
1
410
Digital Ethics as a Driver of Design Innovation
axbom
PRO
1
350
It's Worth the Effort
3n
188
29k
The Success of Rails: Ensuring Growth for the Next 100 Years
eileencodes
47
8.2k
Transcript
Jetpack Composeでグリッドに柔 軟にスペースを入れたい shibuya.apk #41 2023/04/21 (Fri.) Taichi Sato /
@syarihu Giftmall, Inc. Android Engineer
Taichi Sato (@syarihu) • Giftmall, Inc. (LUCHE GROUP) ◦ Android
App Engineer
shibuya.apkは3年ぶり9回目の発表みたいです
今回実現したい レイアウト
今回実現したい レイアウト • あるアイテムではスペー スなしで1行で表示したい
今回実現したい レイアウト • あるアイテムではスペー スなしで1行で表示したい • あるアイテムでは1行に3 列表示するグリッドを作 成し、そのグリッド内では スペースを入れたい
一見簡単そうに見えるが果たして…
LazyVerticalGridの標準機能で 頑張ってみる
スペースを入れない通常のレイアウトを作成する LazyVerticalGrid( modifier = Modifier.fillMaxSize(), columns = GridCells.Fixed(count = GRID_COLUMN_SIZE)
) {
スペースを入れない通常のレイアウトを作成する LazyVerticalGrid(...) { item(span = { GridItemSpan(GRID_COLUMN_SIZE) }) { Box(
modifier = Modifier .fillMaxWidth() .height(100.dp) .background(Color.DarkGray), contentAlignment = Alignment.Center ) { Text(text = "Header", color = Color.White) } }
スペースを入れない通常のレイアウトを作成する LazyVerticalGrid(...) { … items(items = items, span = {
GridItemSpan(1) }) { Column( horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier.fillMaxWidth() .background(color = Color.LightGray) .padding(8.dp) ) { Image(painter = painterResource(R.drawable.ic_launcher_foreground), null) Text(text = it) } }
スペースを入れない通常のレイアウトを作成する LazyVerticalGrid(...) { … item(span = { GridItemSpan(GRID_COLUMN_SIZE) }) {
Box( modifier = Modifier .fillMaxWidth() .height(100.dp) .background(Color.DarkGray), contentAlignment = Alignment.Center ) { Text(text = "Footer", color = Color.White) } }
スペースを入れない 通常のレイアウト これをベースにして、グリッ ド間にスペースを入れられ ないか考えてみる
contentPaddingを使う
contentPaddingを使う LazyVerticalGrid( … contentPadding = PaddingValues(8.dp) ) {
実行結果
実行結果 LazyVerticalGridの上下左 右にpaddingが設定される
verticalArrangementや horizontalArrangementでspacedByを 使ってスペースを設定する
spacedByを使ってスペースを設定する LazyVerticalGrid( … verticalArrangement = Arrangement.spacedBy(8.dp), horizontalArrangement = Arrangement.spacedBy(8.dp) )
{
実行結果
実行結果 • 左端と右端のアイテムに スペースが入らない
実行結果 • 左端と右端のアイテムに スペースが入らない • すべてのアイテムにス ペースが適用されるた め、意図しないところにま でスペースが入ることが ある
アイテムごとにpaddingを設定する
アイテムごとにpaddingを設定する items( items = items, span = { GridItemSpan(1) },
) { Column( horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier .fillMaxWidth() .background(color = Color.LightGray) .padding(8.dp) ) {
アイテムごとにpaddingを設定する items( items = items, span = { GridItemSpan(1) },
) { Column( horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier .fillMaxWidth() .background(color = Color.LightGray) .padding(8.dp) ) {
アイテムごとにpaddingを設定する items( items = items, span = { GridItemSpan(1) },
) { Column( horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier .fillMaxWidth() .padding(8.dp) // <- 背景色の前にpaddingを入れる .background(color = Color.LightGray) .padding(8.dp) ) {
アイテムごとにpaddingを設定する items( items = items, span = { GridItemSpan(1) },
) { Column( horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier .fillMaxWidth() .padding(8.dp) // <- 背景色の前にpaddingを入れる .background(color = Color.LightGray) .padding(8.dp) ) {
実行結果 • 1つのアイテムの上下左 右にpaddingが設定され ているため、アイテムとア イテムの間のpaddingが 大きくなる
アイテムによってpaddingを動的に変える
アイテムによって paddingを動的に変 える • 左端と右端だけ8dpにす る
アイテムによって paddingを動的に変 える • 左端と右端だけ8dpにす る • アイテム間は4dpにする
アイテムによってpaddingを動的に変える private const val GRID_COLUMN_SIZE = 3 val startIndexes =
(1..items.size) .map { GRID_COLUMN_SIZE * it - (GRID_COLUMN_SIZE - 1) } .toList() val endIndexes = (1..items.size) .map { GRID_COLUMN_SIZE * it } .toList()
アイテムによってpaddingを動的に変える itemsIndexed(...) { index, item -> Column( horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier .fillMaxWidth() .padding( top = if (index < GRID_COLUMN_SIZE) 8.dp else 4.dp, bottom = if (index > items.size - GRID_COLUMN_SIZE + 1) 8.dp else 4.dp, start = if (index == 0 || startIndexes.contains(index + 1)) 8.dp else 4.dp, end = if (endIndexes.contains(index + 1)) 8.dp else 4.dp, ) ...
アイテムによってpaddingを動的に変える itemsIndexed(...) { index, item -> Column( horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier .fillMaxWidth() .padding( top = if (index < GRID_COLUMN_SIZE) 8.dp else 4.dp, bottom = if (index > items.size - GRID_COLUMN_SIZE + 1) 8.dp else 4.dp, start = if (index == 0 || startIndexes.contains(index + 1)) 8.dp else 4.dp, end = if (endIndexes.contains(index + 1)) 8.dp else 4.dp, ) ...
アイテムによってpaddingを動的に変える itemsIndexed(...) { index, item -> Column( horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier .fillMaxWidth() .padding( top = if (index < GRID_COLUMN_SIZE) 8.dp else 4.dp, bottom = if (index > items.size - GRID_COLUMN_SIZE + 1) 8.dp else 4.dp, start = if (index == 0 || startIndexes.contains(index + 1)) 8.dp else 4.dp, end = if (endIndexes.contains(index + 1)) 8.dp else 4.dp, ) ...
アイテムによってpaddingを動的に変える itemsIndexed(...) { index, item -> Column( horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier .fillMaxWidth() .padding( top = if (index < GRID_COLUMN_SIZE) 8.dp else 4.dp, bottom = if (index > items.size - GRID_COLUMN_SIZE + 1) 8.dp else 4.dp, start = if (index == 0 || startIndexes.contains(index + 1)) 8.dp else 4.dp, end = if (endIndexes.contains(index + 1)) 8.dp else 4.dp, ) ...
実行結果 • 一見うまくいったように見 えるが…
実行結果 • 一見うまくいったように見 えるが… • 画面幅を変えるとズレる
実行結果 • 一見うまくいったように見 えるが… • 画面幅を変えるとズレる • 列数を変えてもズレる
アイテムによってpaddingを動的に変える • verticalArrangementやhorizontalArrangementで spacedByを使ってスペースを設定した上で左端と右端に動 的にpaddingを設定した場合も、アイテムサイズのズレが発 生する
LazyVerticalGridの標準機能では 実現は難しい…🤔
ならば自分で作ろう💡
1行ごとにRowを生成し、 自前でグリッドを作ってみる
自前でグリッドを作ってみる • グリッド表示したいアイテムを1行ごとの塊に分割する • そのアイテムの塊を自分でRowを使ってグリッドを表現する
自前でグリッドを作ってみる • Rowを使えばRowにpaddingを設定し、自分で列と列の間 にSpacerを追加できる • これならアイテムがずれることがないため、期待通りのレイ アウトが実現できると考えた
自前でグリッドを作ってみる const val chunkSize = 3 val items = buildList
{ repeat(7) { add("item$it") } } val chunkedItems = items.chunked(chunkSize)
自前でグリッドを作ってみる const val chunkSize = 3 val items = buildList
{ repeat(7) { add("item$it") } } val chunkedItems = items.chunked(chunkSize)
自前でグリッドを作ってみる items( items = chunkedItems, span = { GridItemSpan(chunkSize) },
) { chunkedItems ->
自前でグリッドを作ってみる items(...) { chunkedItems -> Row( Modifier.fillMaxWidth() // Rowの左右にpaddingを入れる。 //
上下でpaddingが重ならないように上下ではtopのみにpaddingを入れる .padding(top = 8.dp, start = 8.dp, end = 8.dp), ) {
自前でグリッドを作ってみる items(...) { chunkedItems -> Row(...) { chunkedItems.forEachIndexed { index,
item -> Box(modifier = Modifier.fillMaxWidth().weight(1f)) { itemContent(item) } if (index < chunkedItems.lastIndex || chunkedItems.size < chunkSize) { Spacer(modifier = Modifier.size(rowSpace)) } }
自前でグリッドを作ってみる items(...) { chunkedItems -> Row(...) { chunkedItems.forEachIndexed { index,
item -> Box(modifier = Modifier.fillMaxWidth().weight(1f)) { itemContent(item) } if (index < chunkedItems.lastIndex || chunkedItems.size < chunkSize) { Spacer(modifier = Modifier.size(rowSpace)) } }
自前でグリッドを作ってみる items(...) { chunkedItems -> Row(...) { chunkedItems.forEachIndexed { index,
item -> Box(modifier = Modifier.fillMaxWidth().weight(1f)) { itemContent(item) } if (index < chunkedItems.lastIndex || chunkedItems.size < chunkSize) { Spacer(modifier = Modifier.size(rowSpace)) } }
自前でグリッドを作ってみる items(...) { chunkedItems -> Row(...) { chunkedItems.forEachIndexed { index,
item -> Box(modifier = Modifier.fillMaxWidth().weight(1f)) { itemContent(item) } if (index < chunkedItems.lastIndex || chunkedItems.size < chunkSize) { Spacer(modifier = Modifier.size(rowSpace)) } }
自前でグリッドを作ってみる items(...) { chunkedItems -> Row(...) { ... val spacerLastIndex
= chunkSize - chunkedItems.size - 1 repeat(chunkSize - chunkedItems.size) { index -> Box(modifier = Modifier.fillMaxWidth().weight(1f)) if (index < spacerLastIndex) { Spacer(modifier = Modifier.size(rowSpace)) } }
自前でグリッドを作ってみる items(...) { chunkedItems -> Row(...) { ... val spacerLastIndex
= chunkSize - chunkedItems.size - 1 repeat(chunkSize - chunkedItems.size) { index -> Box(modifier = Modifier.fillMaxWidth().weight(1f)) if (index < spacerLastIndex) { Spacer(modifier = Modifier.size(rowSpace)) } }
自前でグリッドを作ってみる items(...) { chunkedItems -> Row(...) { ... val spacerLastIndex
= chunkSize - chunkedItems.size - 1 repeat(chunkSize - chunkedItems.size) { index -> Box(modifier = Modifier.fillMaxWidth().weight(1f)) if (index < spacerLastIndex) { Spacer(modifier = Modifier.size(rowSpace)) } }
自前でグリッドを作ってみる • アイテムをchunked(決まった数ごと)で取り出す
自前でグリッドを作ってみる • アイテムをchunked(決まった数ごと)で取り出す • chunkごとにRowを生成し、取り出したアイテムをそのRow 内でweight=1fで均等に表示する
自前でグリッドを作ってみる • アイテムをchunked(決まった数ごと)で取り出す • chunkごとにRowを生成し、取り出したアイテムをそのRow 内でweight=1fで均等に表示する • アイテムが列数より少なくなるケースでは足りないアイテム 分、weight=1fのSpacerで埋める
自前でグリッドを作ってみる • 自分でRowを使ってグリッドを表現しているため、これでグ リッド内のスペースを自分でコントロールできるようになった • あとはこれを汎用的に使えるように拡張関数にまとめる
自前でグリッドを作ってみる inline fun <T> LazyGridScope.chunkedItems( items: List<T>, chunkSize: Int, chunkedContentPadding:
PaddingValues = PaddingValues(0.dp), rowSpace: Dp = 0.dp, noinline key: ((item: List<T>) -> Any)? = null, noinline span: (LazyGridItemSpanScope.(item: List<T>) -> GridItemSpan)? = null, noinline contentType: (item: List<T>) -> Any? = { null }, crossinline itemContent: @Composable LazyGridItemScope.(item: T) -> Unit, )
自前でグリッドを作ってみる inline fun <T> LazyGridScope.chunkedItems( items: List<T>, chunkSize: Int, chunkedContentPadding:
PaddingValues = PaddingValues(0.dp), rowSpace: Dp = 0.dp, noinline key: ((item: List<T>) -> Any)? = null, noinline span: (LazyGridItemSpanScope.(item: List<T>) -> GridItemSpan)? = null, noinline contentType: (item: List<T>) -> Any? = { null }, crossinline itemContent: @Composable LazyGridItemScope.(item: T) -> Unit, )
自前でグリッドを作ってみる inline fun <T> LazyGridScope.chunkedItems( items: List<T>, chunkSize: Int, chunkedContentPadding:
PaddingValues = PaddingValues(0.dp), rowSpace: Dp = 0.dp, noinline key: ((item: List<T>) -> Any)? = null, noinline span: (LazyGridItemSpanScope.(item: List<T>) -> GridItemSpan)? = null, noinline contentType: (item: List<T>) -> Any? = { null }, crossinline itemContent: @Composable LazyGridItemScope.(item: T) -> Unit, )
自前でグリッドを作ってみる chunkedItems( items = items, chunkSize = GRID_COLUMN_SIZE, span =
{ GridItemSpan(GRID_COLUMN_SIZE) }, rowSpace = 8.dp, chunkedContentPadding = PaddingValues(top = 8.dp, start = 8.dp, end = 8.dp) ) { item -> // ここにアイテムのレイアウトを入れる } // 下部のスペース item(span = { GridItemSpan(GRID_COLUMN_SIZE) }) { Spacer(modifier = Modifier.size(8.dp)) }
自前でグリッドを作ってみる chunkedItems( items = items, chunkSize = GRID_COLUMN_SIZE, span =
{ GridItemSpan(GRID_COLUMN_SIZE) }, rowSpace = 8.dp, chunkedContentPadding = PaddingValues(top = 8.dp, start = 8.dp, end = 8.dp) ) { item -> // ここにアイテムのレイアウトを入れる } // 下部のスペース item(span = { GridItemSpan(GRID_COLUMN_SIZE) }) { Spacer(modifier = Modifier.size(8.dp)) }
自前でグリッドを作ってみる chunkedItems( items = items, chunkSize = GRID_COLUMN_SIZE, span =
{ GridItemSpan(GRID_COLUMN_SIZE) }, rowSpace = 8.dp, chunkedContentPadding = PaddingValues(top = 8.dp, start = 8.dp, end = 8.dp) ) { item -> // ここにアイテムのレイアウトを入れる } // 下部のスペース item(span = { GridItemSpan(GRID_COLUMN_SIZE) }) { Spacer(modifier = Modifier.size(8.dp)) }
実行結果 3列の場合
実行結果 4列の場合
無事に期待通りのレイアウトを実現できた 🎉
おわりに • 今回実装したコードは以下のgistに公開しています ◦ https://sh.syarihu.net/gist-chunked-items • itemsIndexedのようにindexを取得したいケースのサンプル コードも記載しているので、参考までにどうぞ
Jetpack Composeでグリッドに柔 軟にスペースを入れたい shibuya.apk #41 2023/04/21 (Fri.) Taichi Sato /
@syarihu Giftmall, Inc. Android Engineer