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
Sponsored
·
Ship Features Fearlessly
Turn features on and off without deploys. Used by thousands of Ruby developers.
→
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
130
[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
640
[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.1k
Other Decks in Technology
See All in Technology
初めてのDatabricks勉強会
taka_aki
2
250
アカウントが増えてからでは遅い? ~ マルチアカウント統制の勘所 ~
kenichinakamura
0
200
オブザーバビリティ、本当に活用できてる? 〜API連携×生成AIで成熟度を自動評価〜
dmmsre
1
2.4k
SRE依存からの脱却 運用を開 発チームへ移す、 フルサイ クル開 発体制の実践
joooee0000
0
2.2k
脱金融のフューチャー・デザイン / Future Design Beyond Finance
ks91
PRO
0
140
【Claude Code】鹿野さんに聞く 私の推しの並行開発環境 大公開 / claude-code-parallel-2026-07-15
tonkotsuboy_com
3
370
Keeping applications secure by evolving OAuth 2.0 and OpenID Connect
ahus1
PRO
1
150
Mastraエージェント、どのクラウドにデプロイする?
minorun365
PRO
2
180
勉強会企画をアプリで構造化してみた 〜そこで見えた、AIとの付き合い方〜 / I've structured a study group plan using an app.
pauli
0
330
CSに"SLO"は要らない、経営層に"99.9%"は伝わらない - SREを全社に"翻訳"する3原則
cscengineer
PRO
1
3.9k
金融の未来を考える / Thinking About the Future of Finance
ks91
PRO
0
180
AIに「使われる」時代のSaaS戦略 〜既存WebAPIのMCPサーバー化における開発ノウハウ〜
ekispert_api
0
300
Featured
See All Featured
[Rails World 2023 - Day 1 Closing Keynote] - The Magic of Rails
eileencodes
38
2.9k
Building an army of robots
kneath
306
46k
Practical Tips for Bootstrapping Information Extraction Pipelines
honnibal
25
2k
Intergalactic Javascript Robots from Outer Space
tanoku
273
27k
RailsConf & Balkan Ruby 2019: The Past, Present, and Future of Rails at GitHub
eileencodes
141
35k
How GitHub (no longer) Works
holman
316
150k
Bioeconomy Workshop: Dr. Julius Ecuru, Opportunities for a Bioeconomy in West Africa
akademiya2063
PRO
1
170
SEO Brein meetup: CTRL+C is not how to scale international SEO
lindahogenes
1
2.8k
How to make the Groovebox
asonas
2
2.3k
DevOps and Value Stream Thinking: Enabling flow, efficiency and business value
helenjbeal
1
260
30 Presentation Tips
portentint
PRO
1
340
Agile Leadership in an Agile Organization
kimpetersen
PRO
0
180
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