はじめに
こんにちは。MAMADAYSでAndroidアプリの開発を担当している高野です。
UIはXMLで作成したほうが楽なのではないかと思い、まだ Jetpack Composeを触ったことがなかったのでチュートリアルに沿って進めながら体験してみたいと思います。
※Jetpack Compose は Android の UI を構築するための新しいツールキットです。2021年7月にバージョン 1.0 をリリースし、チュートリアルのページも用意されました。
チュートリアルでやること
チュートリアルでは4つのレッスンを通して次のことを学ぶことができるようです。
- 要素の追加
- プレビュー方法
- 複数要素のレイアウト
- デザインテーマの適用
- リストの実装
- アニメーションの実装
環境の準備
現在使っているAndroid Studioをそのまま使用します。
- Android Studio Arctic Fox 2020.3.1 Patch 3
プロジェクトは新規で作成し、テンプレートは Empty Activity
を使用します。
Empty Activity
のプロジェクトでJetpack Composeを使用できるようにセットアップの例に合わせてgradleファイルを修正します。
build.gradle
@@ -6,7 +6,7 @@ buildscript { } dependencies { classpath "com.android.tools.build:gradle:7.0.3" - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.31" + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.21" // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files
app/build.gradle
@@ -15,7 +15,9 @@ android { testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } - + buildFeatures { + compose true + } buildTypes { release { minifyEnabled false @@ -28,6 +30,11 @@ android { } kotlinOptions { jvmTarget = '1.8' + useIR = true + } + composeOptions { + kotlinCompilerVersion '1.4.21' + kotlinCompilerExtensionVersion '1.0.0-alpha10' } } @@ -37,7 +44,17 @@ dependencies { implementation 'androidx.appcompat:appcompat:1.3.1' implementation 'com.google.android.material:material:1.4.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.1' + + implementation 'androidx.compose.ui:ui:1.0.0-alpha10' + implementation 'androidx.compose.ui:ui-tooling:1.0.0-alpha10' + implementation 'androidx.compose.foundation:foundation:1.0.0-alpha10' + implementation 'androidx.compose.material:material:1.0.0-alpha10' + implementation 'androidx.compose.material:material-icons-core:1.0.0-alpha10' + implementation 'androidx.compose.material:material-icons-extended:1.0.0-alpha10' + implementation 'androidx.compose.runtime:runtime-livedata:1.0.0-alpha10' + testImplementation 'junit:junit:4.+' androidTestImplementation 'androidx.test.ext:junit:1.1.3' androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' + androidTestImplementation 'androidx.compose.ui:ui-test-junit4:1.0.0-alpha10' }
この後の流れはチュートリアルの内容をなぞるだけなので実装については割愛します。
チュートリアルを終えて
チュートリアルはとてもよくできていて、つまずくポイントはほとんどありませんでした。強いて言えば、Lesson3のマテリアルデザインを使用するためのComposeTutorialTheme
って何だろう…?という部分ぐらいです。一通り終えるため、今回はTheme
と入力した時にサジェストされた MaterialTheme
という記述で代用しました。
Lesson1からLesson4までを通して次のような成果物ができました。
少しカスタマイズしてみる
チュートリアルのサンプルはチャット画面のようなレイアウトになっています。サンプルのままだと一人でチャットしているようで寂しいので、登場人物を二人にしてみたいと思います。
イメージとしてはこんな感じです。
Message の修正
データクラスMessageをsealed classにしてMe/Youを追加します。
sealed class Message { abstract val author: String abstract val body: String data class Me(override val author: String, override val body: String) : Message() data class You(override val author: String, override val body: String) : Message() }
サンプルデータ
サンプルデータは好みで修正します。
object SampleData { // Sample conversation data val conversationSample = listOf( Message.You( "Colleague", "Test...Test...Test..." ), Message.Me( "Me", "List of Android versions:\n" + ...
幅一杯に広げる
ModifierクラスにfillMaxWidthという関数があるので追加します。
@Composable fun MessageCard(msg: Message) { Row( modifier = Modifier .padding(all = 8.dp) .fillMaxWidth()
位置の調整
MessageCardを含むRowの定義を見てみます。
@Composable inline fun Row( modifier: Modifier = Modifier, horizontalArrangement: Arrangement.Horizontal = Arrangement.Start, verticalAlignment: Alignment.Vertical = Alignment.Top, content: @Composable RowScope.() -> Unit ) { ...
horizontalArrangement
という引数があります。この引数にArrangement
の値を渡すことで位置の調整ができるようです。horizontalArrangement
を指定する際に引数msgの型を判定して位置を調整します。
@Composable fun MessageCard(msg: Message) { Row( modifier = Modifier .padding(all = 8.dp) .fillMaxWidth(), horizontalArrangement = when (msg) { is Message.Me -> Arrangement.End is Message.You -> Arrangement.Start }
合わせて、アイコンや名前・メッセージも修正を加えます。名前とメッセージはColumnの中なので、Columnの定義をみてみます。
inline fun Column( modifier: Modifier = Modifier, verticalArrangement: Arrangement.Vertical = Arrangement.Top, horizontalAlignment: Alignment.Horizontal = Alignment.Start, content: @Composable ColumnScope.() -> Unit ) { ...
Columnの場合はhorizontalAlignment
でAlignment
を指定するようですね。ついでにsurfaceColor
も修正しています。
if (msg is Message.You) { Image( painter = painterResource(R.drawable.ic_launcher_foreground), modifier = Modifier .size(40.dp) .clip(CircleShape) .border(1.5.dp, MaterialTheme.colors.secondary, CircleShape) ) Spacer(modifier = Modifier.width(8.dp)) } ... val surfaceColor: Color by animateAsState( if (isExpanded) MaterialTheme.colors.primary else when (msg) { is Message.Me -> MaterialTheme.colors.surface is Message.You -> MaterialTheme.colors.secondary }, ) Column( modifier = Modifier.clickable { isExpanded = !isExpanded }, horizontalAlignment = when (msg) { is Message.Me -> Alignment.End is Message.You -> Alignment.Start } ) { ... } if (msg is Message.Me) { Spacer(modifier = Modifier.width(8.dp)) Image( painter = painterResource(R.drawable.ic_launcher_foreground), modifier = Modifier .size(40.dp) .clip(CircleShape) .border(1.5.dp, MaterialTheme.colors.secondary, CircleShape) ) }
エミュレータで確認
チャット画面らしくなりましたね。
最後に
Composeでの実装は想像していたよりずっと簡単でした。特に、リストがたった5行で実装できることに驚きました。RecyclerViewと比べるととても簡単ですよね。
まだComposeに慣れていないためもどかしさは感じましたが、簡単なアプリをいくつか作っていくうちに感覚的につかめそうだなといった印象です。