Memo

メモ > 技術 > IDE: AndroidStudio > アプリの作成(Jetpack)

アプリの作成(Jetpack)
■Jetpackについて Android Jetpackとは、アプリを作成するためのコンポーネントやツールなどをひとまとめにしたもの。 AndroidのOSバージョンアップからは切り離されているので、OSのアップデートを待たずに迅速に対応できるようになっている。 一例だが、以下のような機能が提供されている。 compose … 形状とデータの依存関係を記述するコンポーズ可能な関数を使用して、UIをプログラムで定義する。 camera … モバイルカメラアプリを構築する。 appsearch … ユーザー向けにカスタムのアプリ内検索機能を構築する。 Android Jetpack デベロッパー リソース - Android デベロッパー | Android Developers https://developer.android.com/jetpack?hl=ja Google Developers Japan: Android Jetpack を使用してアプリの開発を加速 https://developers-jp.googleblog.com/2018/05/use-android-jetpack-to-accelerate-your.html Android Jetpackってなにもの? - Qiita https://qiita.com/k_masa777/items/c01c1de6ac763ce5c075 ■Jetpack Composeについて Jetpackで提供されるコンポーネントの一つ。 Kotlinで宣言的にUIを記述できる。(SwiftUIのように簡単にレイアウトできるみたい。) 従来のようなXMLではなく、setContentブロック内で「コンポーズ可能な関数」を呼び出してレイアウトを作成する。 コンポーズ可能な関数は、関数名に「@Composable」アノテーションを追加するだけで作成できる。 まずは以下の内容を一読するといい。 UI アプリ開発ツールキット Jetpack Compose - Android デベロッパー | Android Developers https://developer.android.com/jetpack/compose?hl=ja Android Compose のチュートリアル | Android デベロッパー | Android Developers https://developer.android.com/jetpack/compose/tutorial?hl=ja 以下も参考になりそう。 Jetpack Compose入門 はじめの一歩 https://zenn.dev/ko2ic/articles/0a141f9e5a0d39 Jetpack Composeを使ってみた - Qiita https://qiita.com/kota_2402/items/7bbdd87be8024785e25b Jetpack Compose入門 - 縁側プログラミング https://engawapg.net/programming/jetpack-compose/ ■ハローワールド(Koala Feature Drop時点) src/main/java/net/refirio/helloworld/MainActivity.kt
package net.refirio.helloworld import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview import net.refirio.helloworld.ui.theme.HelloWorldTheme class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enableEdgeToEdge() setContent { HelloWorldTheme { Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding -> Greeting( name = "Android", modifier = Modifier.padding(innerPadding) ) } } } } } @Composable fun Greeting(name: String, modifier: Modifier = Modifier) { Text( text = "Hello $name!", modifier = modifier ) } @Preview(showBackground = true) @Composable fun GreetingPreview() { HelloWorldTheme { Greeting("Android") } }
build.gradle.kts
// Top-level build file where you can add configuration options common to all sub-projects/modules. plugins { alias(libs.plugins.android.application) apply false alias(libs.plugins.kotlin.android) apply false }
app\build.gradle.kts
plugins { alias(libs.plugins.android.application) alias(libs.plugins.kotlin.android) } android { namespace = "com.example.helloworld" compileSdk = 34 defaultConfig { applicationId = "com.example.helloworld" minSdk = 26 targetSdk = 34 versionCode = 1 versionName = "1.0" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" vectorDrawables { useSupportLibrary = true } } buildTypes { release { isMinifyEnabled = false proguardFiles( getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro" ) } } compileOptions { sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 } kotlinOptions { jvmTarget = "1.8" } buildFeatures { compose = true } composeOptions { kotlinCompilerExtensionVersion = "1.5.1" } packaging { resources { excludes += "/META-INF/{AL2.0,LGPL2.1}" } } } dependencies { implementation(libs.androidx.core.ktx) implementation(libs.androidx.lifecycle.runtime.ktx) implementation(libs.androidx.activity.compose) implementation(platform(libs.androidx.compose.bom)) implementation(libs.androidx.ui) implementation(libs.androidx.ui.graphics) implementation(libs.androidx.ui.tooling.preview) implementation(libs.androidx.material3) testImplementation(libs.junit) androidTestImplementation(libs.androidx.junit) androidTestImplementation(libs.androidx.espresso.core) androidTestImplementation(platform(libs.androidx.compose.bom)) androidTestImplementation(libs.androidx.ui.test.junit4) debugImplementation(libs.androidx.ui.tooling) debugImplementation(libs.androidx.ui.test.manifest) }
app\src\main\AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"> <application android:allowBackup="true" android:dataExtractionRules="@xml/data_extraction_rules" android:fullBackupContent="@xml/backup_rules" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.HelloWorld" tools:targetApi="31"> <activity android:name=".MainActivity" android:exported="true" android:label="@string/app_name" android:theme="@style/Theme.HelloWorld"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
以前に比べて、MainActivity.kt 内に enableEdgeToEdge() というコードが追加されている。 edge-to-edgeについては、以下などのページを参照。 APIレベルを上げると強制的に適用されるようなので、原則有効であるものと考える方が良さそう。 アプリの対象 API レベル 35 で初めて edge-to-edge に対処する[Android View編] #Android - Qiita https://qiita.com/seabat-dev/items/b1ee9c71674e80abccc7 Androidのedge-to-edge表示対応の作業メモ(Compose向け) https://zenn.dev/tomoya0x00/articles/f854a6825a1182 ■練習用に表示を調整した場合(Koala Feature Drop時点) src/main/java/net/refirio/helloworld/MainActivity.kt - - - - - - - - - - - - - - - - - - - - package net.refirio.helloworld import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview import net.refirio.helloworld.ui.theme.HelloWorldTheme class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enableEdgeToEdge() setContent { HelloWorldTheme { Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding -> MainScreen(modifier = Modifier.padding(innerPadding)) } } } } } @Composable fun MainScreen(modifier: Modifier = Modifier) { Column(modifier = modifier) { Text("Hello Android!!") } } @Preview(showBackground = true) @Composable fun MainScreenPreview() { MainScreen() } - - - - - - - - - - - - - - - - - - - - ■練習用にテーマを外した場合(Koala Feature Drop時点) ※テーマについては、後述の「テーマの適用」も参照。 ※テーマ名の「HelloWorld」は固定の名前ではなく、アプリを作成したときのパッケージ名をもとに付けられるみたい。 よってこのテーマは無理に外さずに、必要に応じてカスタマイズして使うと良さそう。 src/main/java/net/refirio/helloworld/MainActivity.kt - - - - - - - - - - - - - - - - - - - - package net.refirio.helloworld import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enableEdgeToEdge() setContent { Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding -> MainScreen(modifier = Modifier.padding(innerPadding)) } } } } @Composable fun MainScreen(modifier: Modifier = Modifier) { Column(modifier = modifier) { Text("Hello Android!!") } } @Preview(showBackground = true) @Composable fun MainScreenPreview() { MainScreen() } - - - - - - - - - - - - - - - - - - - - また app\src\main\AndroidManifest.xml から以下のように、 「Theme.HelloWorld」に関する記述を削除すればテーマの適用は無くなるみたい。 (ただしテーマが適用されていると全画面で表示されるが、テーマを削除すると上部にタイトルバーが表示される。)
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"> <application android:allowBackup="true" android:dataExtractionRules="@xml/data_extraction_rules" android:fullBackupContent="@xml/backup_rules" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" tools:targetApi="31"> <activity android:name=".MainActivity" android:exported="true" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
■ハローワールド(Flamingo時点) src/main/java/net/refirio/helloworld/MainActivity.kt
package net.refirio.helloworld import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview import net.refirio.helloworld.ui.theme.HelloWorldTheme class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { HelloWorldTheme { // A surface container using the 'background' color from the theme Surface( modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background ) { Greeting("Android") } } } } } @Composable fun Greeting(name: String, modifier: Modifier = Modifier) { Text( text = "Hello $name!", modifier = modifier ) } @Preview(showBackground = true) @Composable fun GreetingPreview() { HelloWorldTheme { Greeting("Android") } }
■練習用にテーマを外した場合(Flamingo時点) ※テーマについては、後述の「テーマの適用」も参照。 ※テーマ名の「HelloWorld」は固定の名前ではなく、アプリを作成したときのパッケージ名をもとに付けられるみたい。 よってこのテーマは無理に外さずに、必要に応じてカスタマイズして使うと良さそう。 src/main/java/net/refirio/helloworld/MainActivity.kt
package com.example.helloworld import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.tooling.preview.Preview class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { MainScreen() } } } @Composable fun MainScreen() { Text("Hello Android!!") } @Preview(showBackground = true) @Composable fun MainScreenPreview() { MainScreen() }
■プレビューの表示をリッチにする showSystemUiを有効にすると、プレビューにUIが表示される。
@Preview(showBackground = true) ↓ @Preview(showSystemUi = true)
Compose のツール | Jetpack Compose | Android Developers https://developer.android.com/jetpack/compose/tooling?hl=ja ■Jetpack Composeの基本 ※上で作成した「練習用に表示を調整した場合」をもとに、MainScreen() の内容だけを以下に記述する。
Text("Hello")
文字列の表示。
Column { // テキストを表示 Text("Hello") // 色名を指定してテキストを表示 Text("Hello", color = Color.Red) // 色コードを指定してテキストを表示 Text("Hello", color = Color(0xff66ccaa)) // フォントサイズを指定してテキストを表示 Text("Hello", fontSize = 10.sp) Text("Hello", fontSize = 30.sp) }
コンポーネントの装飾。
// テキストを装飾 Text( "Hello", modifier = Modifier .size(120.dp, 80.dp) .offset(20.dp, 20.dp) .background(Color(0xff66cdaa), RoundedCornerShape(20.dp)) .border(2.dp, Color(0xff2f4f4f), RoundedCornerShape(20.dp)) .padding(20.dp) )
画像の表示。 あらかじめ、res/drawable 内に画像を配置しておく(ここでは「photo01.jpg」としておく。)
// 画像を表示 Image( painter = painterResource(R.drawable.photo01), contentDescription = "画像の表示サンプル" )
Imageを使えない場合、自動で適切ではないクラスが読み込まれている可能性がある。 この場合、以下のように読み込むクラスを調整する。
import androidx.compose.ui.semantics.Role.Companion.Image ↓ import androidx.compose.foundation.Image
【Jetpack Compose】Image()コンポーザブルが使用できない - Qiita https://qiita.com/antk/items/3b10b5f8843bb8896470 縦に並べる。
Column { Text("Hello!") Text("Hello!!") Text("Hello!!!") }
横に並べる。
Row { Image( painter = painterResource(id = R.drawable.photo01), contentDescription = null, contentScale = ContentScale.Crop, modifier = Modifier.size(50.dp) ) Image( painter = painterResource(id = R.drawable.photo02), contentDescription = null, contentScale = ContentScale.Crop, modifier = Modifier.size(50.dp) ) Image( painter = painterResource(id = R.drawable.photo03), contentDescription = null, contentScale = ContentScale.Crop, modifier = Modifier.size(50.dp) ) }
入れ子にする。
Column { Row { Text("[AAA]") Text("[BBB]") Text("[CCC]") } Row { Text("[DDD]") Text("[EEE]") Text("[FFF]") } }
ボタンの表示。
Column { Text("これはボタンのテストです。") Button( onClick = { Log.d("Button", "onClick") } ) { Text("ボタン") } }
ボタンをクリックしてUIを更新。
Column { // 「by remember」と「mutableStateOf」により、前回の値を記憶している var count by remember { mutableStateOf(0) } Text("ボタンのタップ回数: $count") Button( onClick = { count++ } ) { Text("カウントアップ") } }
値の保持については ・「by remember」で宣言すると、Composable関数で特定の値を保持できる。(値がリセットされない。) rememberの後の「{ 〜 }」ブロック内は、初回しか実行されない。つまり初期値をセットできる。 ・「mutableStateOf」は、値の変更を監視することが可能なMutableStateを返す。 ・「by remember」で宣言された変数(count)は見た目は普通のintだが、ComposeではStateとして扱われる。 ただし、この変数を別のStateでない変数に代入すると、代入先の変数は普通の変数となり、値の変更も監視されなくなる。 という仕組みで実現している。 「remember」や「mutableStateOf」については、以下なども参考になる。 Jetpack Compose入門(11) ボタンクリックでUIを更新する - 縁側プログラミング https://engawapg.net/jetpack-compose/1038/update-ui-on-click-button/ もう雰囲気で使わない。rememberを理解するためのポイント - 縁側プログラミング https://engawapg.net/jetpack-compose/2113/remember-tips/ 「by remember」を使う際、
var count by remember { mutableStateOf(0) }
のコードで以下のようなエラーになることがあった。 この場合、IDEの機能でimportを何度か行うと解消された。
Type 'TypeVariable(T)' has no method 'getValue(Nothing?, KProperty<*>)' and thus it cannot serve as a delegate
Caused by: org.gradle.api.GradleException: Compilation error. See log for more details
UIを専用の関数にまとめる。 コンポーズ可能な(UI用の関数を呼び出す)関数には「@Composable」を付ける。
@Composable fun MainScreen() { Column { SubContents() SubContents() SubContents() } } @Composable fun SubContents() { Row { Text("[AAA]") Text("[BBB]") Text("[CCC]") } }
■Jetpack Composeで画面遷移 Jetpack Compose入門(15) 画面遷移 - 縁側プログラミング https://engawapg.net/jetpack-compose/1393/screen-transition/ 画面遷移には androidx.navigation.compose パッケージが必要なので導入する。 以下のページでバージョンを確認する。 Navigation | Jetpack | Android Developers https://developer.android.com/jetpack/androidx/releases/navigation?hl=ja 現時点での安定板は「2.8.0」となっていた。 build.gradle の dependencies 内に以下を追加する。
implementation("androidx.navigation:navigation-compose:2.8.0")
なお、以前は以下の書き方で紹介されることが多かった。 ※これはGroovy DSLで書かれたGradleファイル用の書き方。以前から使われているもの。 先のカッコありのものは、GradleがKotlin DSLで書かれている場合の書き方。新しいプロジェクトではKotlin DSLが推奨されている。
implementation "androidx.navigation:navigation-compose:2.8.0"
追加したら「Sync Now」をクリックする。 Jetpack Composeで画面遷移させる | mokelab tech sheets https://tech.mokelab.com/android/compose/app/navigation/navigate.html 以下のとおり実装する。
class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enableEdgeToEdge() setContent { HelloWorldTheme { Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding -> MainScreen(modifier = Modifier.padding(innerPadding)) } } } } } @Composable fun MainScreen(modifier: Modifier = Modifier) { Column(modifier = modifier) { val navController = rememberNavController() NavHost(navController = navController, startDestination = "FirstScreen") { composable("FirstScreen") { FirstScreen(navController = navController) } composable("SecondScreen") { SecondScreen(navController = navController) } } } } @Composable fun FirstScreen(navController: NavController) { Column { Text("スクリーンA") Button(onClick = { navController.navigate("SecondScreen") }) { Text("スクリーンBへ") } } } @Composable fun SecondScreen(navController: NavController) { Column { Text("スクリーンB") Button(onClick = { navController.navigateUp() }) { Text("スクリーンAへ") } } } @Preview(showBackground = true) @Composable fun MainScreenPreview() { MainScreen() //FirstScreen() //SecondScreen() }
上のように navController を渡すのは明快ではあるが、テストのことを考えると推奨されないらしい。 以下のようにすることが推奨されるらしい。 Compose を使用したナビゲーション | Jetpack Compose | Android Developers https://developer.android.com/jetpack/compose/navigation?hl=ja
@Composable fun MainScreen(modifier: Modifier = Modifier) { Column(modifier = modifier) { val navController = rememberNavController() NavHost(navController = navController, startDestination = "FirstScreen") { composable("FirstScreen") { FirstScreen( onNavigateToSecondScreen = { navController.navigate("SecondScreen") }, onNavigateToThirdScreen = { navController.navigate("ThirdScreen") } ) } composable("SecondScreen") { SecondScreen( onNavigateToThirdScreen = { navController.navigate("ThirdScreen") } ) } composable("ThirdScreen") { ThirdScreen() } } } } @Composable fun FirstScreen(onNavigateToSecondScreen: () -> Unit, onNavigateToThirdScreen: () -> Unit) { Column { Text("スクリーン1") Button(onClick = onNavigateToSecondScreen) { Text("スクリーン2へ") } Button(onClick = onNavigateToThirdScreen) { Text("スクリーン3へ") } } } @Composable fun SecondScreen(onNavigateToThirdScreen: () -> Unit) { Column { Text("スクリーン2") Button(onClick = onNavigateToThirdScreen) { Text("スクリーン3へ") } } } @Composable fun ThirdScreen() { Column { Text("スクリーン3") } }
Jetpack Compose入門(15) 画面遷移 - 縁側プログラミング https://engawapg.net/jetpack-compose/1393/screen-transition/ 以下のようにすると、画面遷移の際に引数を受け渡しできる。
@Composable fun MainScreen(modifier: Modifier = Modifier) { Column(modifier = modifier) { val navController = rememberNavController() NavHost(navController = navController, startDestination = "FirstScreen") { composable("FirstScreen") { FirstScreen( onNavigateToSecondScreen = { navController.navigate("SecondScreen") }, onNavigateToThirdScreen = { navController.navigate("ThirdScreen") }, onNavigateToProfileScreen = { userId, message -> navController.navigate("ProfileScreen/$userId/$message") } ) } composable("SecondScreen") { SecondScreen( onNavigateToThirdScreen = { navController.navigate("ThirdScreen") } ) } composable("ThirdScreen") { ThirdScreen() } composable( "ProfileScreen/{userId}/{message}", arguments = listOf( navArgument("userId") { type = NavType.IntType }, navArgument("message") { type = NavType.StringType } ) ) { backStackEntry -> val userId = backStackEntry.arguments?.getInt("userId") ?: 0 val message = backStackEntry.arguments?.getString("message") ?: "" ProfileScreen( userId, message, onNavigateToFirstScreen = { navController.navigate("FirstScreen") } ) } } } } @Composable fun FirstScreen( onNavigateToSecondScreen: () -> Unit, onNavigateToThirdScreen: () -> Unit, onNavigateToProfileScreen: (Int, String) -> Unit ) { Column { Text("スクリーン1") Button(onClick = onNavigateToSecondScreen) { Text("スクリーン2へ") } Button(onClick = onNavigateToThirdScreen) { Text("スクリーン3へ") } Text("プロフィール") Button(onClick = { onNavigateToProfileScreen(1, "テスト1") }) { Text("プロフィール1へ") } Button(onClick = { onNavigateToProfileScreen(2, "テスト2") }) { Text("プロフィール2へ") } Button(onClick = { onNavigateToProfileScreen(3, "テスト3") }) { Text("プロフィール3へ") } } } @Composable fun SecondScreen(onNavigateToThirdScreen: () -> Unit) { Column { Text("スクリーン2") Button(onClick = onNavigateToThirdScreen) { Text("スクリーン3へ") } } } @Composable fun ThirdScreen() { Column { Text("スクリーン3") } } @Composable fun ProfileScreen( userId: Int = 0, message: String = "text", onNavigateToFirstScreen: () -> Unit ) { Column { Text("プロフィール $userId $message") Button(onClick = onNavigateToFirstScreen) { Text("スクリーン1へ") } } }
画面遷移については、以下などを参考に引き続き確認したい。 Jetpack Composeで画面遷移させる | mokelab tech sheets https://tech.mokelab.com/android/compose/app/navigation/navigate.html JetpackComposeでQiitaのクライアントアプリを作ろう|Masato Ishikawa https://note.com/masato1230/n/n743532de2d84 [Jetpack Compose] NavigationBar と Nested Navigation https://zenn.dev/ykrods/articles/580bc1fda58081 JetpackComposeのNavigation Componentを触ったのでまとめる - Qiita https://qiita.com/b4tchkn/items/55b1892ed725297eefe3 Jetpack Composeにおける画面遷移とは? - dely Tech Blog https://tech.dely.jp/entry/2021/12/17/170000 【シンプルサンプル】AndroidStudio 画面遷移 - Qiita https://qiita.com/kiyoZy/items/259699222ae1fec65a8f Jetpack Compose入門(15) 画面遷移 - 縁側プログラミング https://engawapg.net/jetpack-compose/1393/screen-transition/ Android Navigation Compose 導入 - Gunosy Tech Blog https://tech.gunosy.io/entry/android_navigation_compose ■Jetpack Composeでリスト表示 Jetpack Compose入門(17) リスト - 縁側プログラミング https://engawapg.net/jetpack-compose/1442/list/ 簡単なリスト。 ただし以下の場合、要素数に応じたUIが最初に用意されるため、要素が多くなればなるほどパフォーマンスが低下する。 また、リストの要素数が多くなって画面からはみ出しても、リストをスクロールして表示することはできない。
val fruits = listOf("リンゴ", "オレンジ", "グレープ", "ピーチ", "ストロベリー") Column(modifier = modifier) { fruits.forEach { fruit -> Text(text = "これは $fruit です。") } }
このような場合、LazyColumnを使って以下のように記述する。 LazyColumnを使えば表示に必要な分だけ展開されるため、要素数が多くなってもパフォーマンスは低下しない。 また、リストをスクロールして表示することもできる。
val fruits = listOf("リンゴ", "オレンジ", "グレープ", "ピーチ", "ストロベリー") LazyColumn { items(fruits) { fruit -> Text(text = "これは $fruit です。") } }
複数セットのデータを扱うリスト。
data class Fruits(val english: String, val japanese: String) val fruits = listOf( Fruits("Apple", "リンゴ"), Fruits("Orange", "オレンジ"), Fruits("Grape", "グレープ"), Fruits("Peach", "ピーチ"), Fruits("Strawberry", "ストロベリー"), ) LazyColumn { items(fruits) { fruit -> Text("${fruit.english}は日本語で${fruit.japanese}です。") } }
タップでトーストを表示。
val context = LocalContext.current data class Fruits(val english: String, val japanese: String) val fruits = listOf( Fruits("Apple", "リンゴ"), Fruits("Orange", "オレンジ"), Fruits("Grape", "グレープ"), Fruits("Peach", "ピーチ"), Fruits("Strawberry", "ストロベリー"), ) LazyColumn { itemsIndexed(fruits) { index, fruit -> Text( text = "${index}. ${fruit.english}", modifier = Modifier.clickable { Toast.makeText(context, "日本語で${fruit.japanese}です。", Toast.LENGTH_SHORT).show() } ) } }
■テーマの適用 Jetpack Compose入門(18) テーマカラーの適用 - 縁側プログラミング https://engawapg.net/jetpack-compose/1457/theme/ AndroidStudioが生成するデフォルトコードでは、プロジェクト名に応じたテーマが適用済みになっている。 src/main/java/net/refirio/helloworld/MainActivity.kt
package net.refirio.helloworld import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview import net.refirio.helloworld.ui.theme.HelloWorldTheme class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enableEdgeToEdge() setContent { HelloWorldTheme { Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding -> Greeting( name = "Android", modifier = Modifier.padding(innerPadding) ) } } } } } @Composable fun Greeting(name: String, modifier: Modifier = Modifier) { Text( text = "Hello $name!", modifier = modifier ) } @Preview(showBackground = true) @Composable fun GreetingPreview() { HelloWorldTheme { Greeting("Android") } }
自動的に作成された HelloWorldTheme テーマが適用されている。 Greeting定義部分の「fun MainScreen(modifier: Modifier = Modifier) {」でデフォルト設定として扱うModifierを参照させている。 app\src\main\AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"> <application android:allowBackup="true" android:dataExtractionRules="@xml/data_extraction_rules" android:fullBackupContent="@xml/backup_rules" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.HelloWorld" tools:targetApi="31"> <activity android:name=".MainActivity" android:exported="true" android:label="@string/app_name" android:theme="@style/Theme.HelloWorld"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
2箇所ある「android:theme="@style/Theme.HelloWorld"」部分でテーマを適用している。 app\src\main\java\net\refirio\helloworld\ui\theme\Theme.kt
package net.refirio.helloworld.ui.theme import android.app.Activity import android.os.Build import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.material3.MaterialTheme import androidx.compose.material3.darkColorScheme import androidx.compose.material3.dynamicDarkColorScheme import androidx.compose.material3.dynamicLightColorScheme import androidx.compose.material3.lightColorScheme import androidx.compose.runtime.Composable import androidx.compose.ui.platform.LocalContext private val DarkColorScheme = darkColorScheme( primary = Purple80, secondary = PurpleGrey80, tertiary = Pink80 ) private val LightColorScheme = lightColorScheme( primary = Purple40, secondary = PurpleGrey40, tertiary = Pink40 /* Other default colors to override background = Color(0xFFFFFBFE), surface = Color(0xFFFFFBFE), onPrimary = Color.White, onSecondary = Color.White, onTertiary = Color.White, onBackground = Color(0xFF1C1B1F), onSurface = Color(0xFF1C1B1F), */ ) @Composable fun HelloWorldTheme( darkTheme: Boolean = isSystemInDarkTheme(), // Dynamic color is available on Android 12+ dynamicColor: Boolean = true, content: @Composable () -> Unit ) { val colorScheme = when { dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { val context = LocalContext.current if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) } darkTheme -> DarkColorScheme else -> LightColorScheme } MaterialTheme( colorScheme = colorScheme, typography = Typography, content = content ) }
テーマファイルも、Kotlinで書かれたプログラムとなっている。 lightColorSchemeの内容を調整することで、配色を変更できる…が、そのままだと適用されない。 さらに以下の部分をコメントアウトもしくは削除すると、色の変更が反映される。
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { val context = LocalContext.current if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) }
【Jetpack Compose】Material3でテーマカラーを変更する際に陥りがちなミス - Qiita https://qiita.com/Nagumo-7960/items/8699f7670bff4cc7a137

Advertisement