こちらを作るためのKotlinのコード解説
@PreviewScreenSizes
@Composable
fun TestApp() {
var currentDestination by rememberSaveable { mutableStateOf(AppDestinations.HOME) }
Scaffold(
bottomBar = {
NavigationBar {
AppDestinations.entries.forEach { it ->
val isSelected = (it == currentDestination)
NavigationBarItem(
icon = {
//アイコンがクリックされている時のみ白くなる処理
val iconImage = if (isSelected) {
it.icon
} else {
//選ばれてない時は枠線だけ
when(it) {
AppDestinations.HOME -> Icons.Outlined.Home
AppDestinations.RANKS -> Icons.Outlined.Leaderboard
AppDestinations.TASKS -> Icons.Outlined.Checklist
AppDestinations.TRENDS -> Icons.AutoMirrored.Outlined.TrendingUp
}
}
Icon(
imageVector = iconImage,
contentDescription = it.label
)
},
label = { Text(it.label) },
selected = isSelected,
onClick = { currentDestination = it },
//ここで色や座布団(インジゲーター)を消す
colors = NavigationBarItemDefaults.colors(
//選ばれているアイコンの色
selectedIconColor = MaterialTheme.colorScheme.onSurface,
//選ばれていないアイコンの色
unselectedIconColor = MaterialTheme.colorScheme.onSurfaceVariant,
//インディゲーターの透明化
indicatorColor = Color.Transparent
)
)
}
}
}
) { innerPadding ->
//選ばれているボタン(currentDestination)に応じて、表示する画面を切り替える
Box(
modifier = Modifier
.fillMaxSize()
.padding(innerPadding)
) {
when(currentDestination) {
AppDestinations.HOME -> HomeScreen()
AppDestinations.RANKS -> RanksScreen()
AppDestinations.TASKS -> TasksScreen()
AppDestinations.TRENDS -> TrendsScreen()
//AppDestinations.PROFILE -> ProfileScreen()
}
}
}
}
enum class AppDestinations(
val label: String,
val icon: ImageVector,
) {
HOME("Home",Icons.Default.Home),
RANKS("Ranks",Icons.Default.Leaderboard),
TASKS("Tasks",Icons.Default.Checklist),
TRENDS("Trends",Icons.AutoMirrored.Filled.TrendingUp)
//アカウントアイコンから読み込むので後回し PROFILES("Profile",)
}
//ホーム画面(TopAppBar付き)
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun HomeScreen() {
Scaffold(
//上のバー(TopAppBar)の設定を追加する
topBar = {
TopAppBar(
//ここの中をいじればトップバーに色々描ける
title = { Text("Home") },
colors = TopAppBarDefaults.topAppBarColors(
containerColor = MaterialTheme.colorScheme.surfaceContainer
)
)
}
) { innerPadding ->
Box(
Modifier
.fillMaxSize()
.padding(innerPadding),
contentAlignment = Alignment.Center
) {
//ここにホーム画面の中身を作っていく
}
}
}
1 アプリ全体のプレビュー設定部分
@PreviewScreenSizes
解説: Android Studioのプレビュー機能です。「スマホ」「折りたたみデバイス」「タブレット」など、複数の異なる画面サイズでの見た目を一度に確認できるようにします。
@Composable
fun TestApp() {
解説: アプリ全体の画面を組み立てるメインのコンポーザブル関数(UIを作る関数)を定義しています。
var currentDestination by rememberSaveable { mutableStateOf(AppDestinations.HOME) }
解説: 現在どの画面(Home、Ranksなど)を開いているかを記録する変数です。rememberSaveable を使っているため、スマホの画面を回転させても選んでいた画面がリセットされず維持されます。初期値は HOME です。
2 Scaffold(画面の骨組み)の開始
Scaffold(
解説: 画面の基本レイアウト(上にバー、下にナビゲーションなど)を簡単に配置するための土台となるコンポーネントを開始します。
bottomBar = {
解説: Scaffold の「画面の一番下に配置するエリア(ボトムバー)」の設定をここから書きます。
NavigationBar {
解説: Material Design 3 仕様のボトムナビゲーションバーを配置します。
3 ナビゲーションアイテムの動的生成
AppDestinations.entries.forEach { it ->
解説: 後述する enum class AppDestinations に登録されているすべての画面データ(HOME, RANKS, TASKS, TRENDS)に対して、1つずつループ処理(繰り返し)を行います。it に各画面の情報が入ります。
val isSelected = (it == currentDestination)
解説: 「今ループで処理している画面(it)」が、「現在ユーザーが選択している画面(currentDestination)」と同じかどうかを判定し、正しければ true、違えば false を isSelected という変数に代入します。
NavigationBarItem(
解説: ボトムバーに並ぶ、アイコンと文字がセットになった「1つのボタン」を作ります。
4 アイコンの出し分けロジック
icon = {
解説: ボタンに表示するアイコン(画像)の定義をここから書きます。
val iconImage = if (isSelected) {
解説: もしこのボタンが「現在選択されている状態(isSelected が true)」なら、次の行の処理を行います。
it.icon
解説: Enumデータにあらかじめ登録してある「デフォルトの塗りつぶしアイコン」を iconImage に代入します。
} else {
解説: もしこのボタンが「選択されていない状態」なら、以下の when 文を実行します。
when(it) {
AppDestinations.HOME -> Icons.Outlined.Home
AppDestinations.RANKS -> Icons.Outlined.Leaderboard
AppDestinations.TASKS -> Icons.Outlined.Checklist
AppDestinations.TRENDS -> Icons.AutoMirrored.Outlined.TrendingUp
}
解説: 選択されていない画面に応じて、それぞれの「枠線だけ(Outlined)のアイコン」を厳選して iconImage に代入しています。
}
解説: アイコンの出し分け条件分岐(if-else)の終了です。
Icon(
imageVector = iconImage,
contentDescription = it.label
)
解説: 決定したアイコン画像(iconImage)を実際に画面に描画します。contentDescription は目の不自由な方向けの読み上げテキスト(画面の名前)です。
},
解説: アイコン設定エリア(icon = { ... })の終了です。
5 ボタンのテキスト・クリック・色設定
label = { Text(it.label) },
解説: アイコンの下に表示する文字(Home や Tasks など)を設定しています。
selected = isSelected,
解説: このボタンが現在選択されているかどうかをシステムに伝えます。これにより色の変化などが制御されます。
onClick = { currentDestination = it },
解説: このボタンがタップされたとき、アプリの現在地(currentDestination)をタップされた画面(it)に書き換えます。
colors = NavigationBarItemDefaults.colors(
解説: ボタンの色を細かくカスタマイズするための設定を開始します。
selectedIconColor = MaterialTheme.colorScheme.onSurface,
解説: 選択されているときのアイコンの色を、テーマの基本文字色(onSurface)にします。
unselectedIconColor = MaterialTheme.colorScheme.onSurfaceVariant,
解説: 選択されていないときのアイコンの色を、少し控えめな文字色(onSurfaceVariant)にします。
indicatorColor = Color.Transparent
解説: Material 3のデフォルトで選択アイコンの背景に出る「楕円形の座布団(インジゲーター)」の色を透明(Transparent)にして非表示にします。
)
解説: 色設定の終了です。
)
解説: 1つのナビゲーションボタン(NavigationBarItem)の設定終了です。
}
解説: ボタンを自動生成するループ処理(forEach)の終了です。
}
解説: NavigationBar の終了です。
}
解説: ボトムバーエリアの設定終了です。
6 メインコンテンツ(画面の切り替え表示)部分
) { innerPadding ->
解説: ボトムバー以外のメインコンテンツエリアの記述を開始します。innerPadding には、ボトムバーが画面を占有している高さ分の余白情報が入っています。
Box(
modifier = Modifier
.fillMaxSize()
.padding(innerPadding)
) {
解説: 画面全体に広がるレイアウト(Box)を作り、ボトムバーと中身が被らないように padding(innerPadding) を適用します。
when(currentDestination) {
解説: 現在選択されている画面(currentDestination)がどれかによって、表示する中身を条件分岐します。
AppDestinations.HOME -> HomeScreen()
AppDestinations.RANKS -> RanksScreen()
AppDestinations.TASKS -> TasksScreen()
AppDestinations.TRENDS -> TrendsScreen()
解説: それぞれ選ばれている値に応じて、対応する画面の関数(コンポーザブル)を呼び出して表示します。
}
解説: 条件分岐(when)の終了です。
}
解説: メインコンテンツを囲む Box の終了です。
}
}
解説: Scaffold および KimbalApp 全体の終了です。
7 Enum(画面リストの定義)部分
enum class AppDestinations(
val label: String,
val icon: ImageVector,
) {
解説: アプリ内の画面の一覧を管理するための列挙型(Enum)を定義しています。各画面は名前(label)と、アイコン画像(icon)のデータをセットで持ちます。
HOME("Home",Icons.Default.Home),
RANKS("Ranks",Icons.Default.Leaderboard),
TASKS("Tasks",Icons.Default.Checklist),
TRENDS("Trends",Icons.AutoMirrored.Filled.TrendingUp)
}
解説: 具体的な4つの画面のデータを定義しています。右側の Icons.Default... は、選択された時に表示する塗りつぶしのアイコンです。
8 HomeScreen(ホーム画面)部分
@OptIn(ExperimentalMaterial3Api::class)
解説: 次の行で使用する TopAppBar が「実験的API」であるため、それを使用することを承認するアノテーションです。
@Composable
fun HomeScreen() {
解説: ホーム画面の中身を描画する関数です。
Scaffold(
解説: ホーム画面の中に、さらに独自の骨組み(Scaffold)を作ります(これでホーム画面の時だけトップバーを表示させます)。
topBar = {
解説: 画面上部に配置するエリア(トップバー)の設定を開始します。
TopAppBar(
解説: 上部のアプリバー本体を配置します。
title = { Text("Home") },
解説: トップバーの中央または左側に表示されるタイトルテキストを「Home」に設定します。
colors = TopAppBarDefaults.topAppBarColors(
containerColor=MaterialTheme.colorScheme.surfaceContainer
)
解説: トップバーの背景色を、アプリのテーマに合わせたコンテナカラー(surfaceContainer)に設定します。
)
}
解説: トップバーエリアの設定終了です。
) { innerPadding ->
解説: ホーム画面のメインコンテンツエリアの開始です。ここでの innerPadding には「トップバーの高さ分」の余白が入っています。
Box(
Modifier
.fillMaxSize()
.padding(innerPadding),
contentAlignment = Alignment.Center
) {
解説: 画面いっぱいに広がりつつ、トップバーに被らないよう余白を空けます。さらに、中に入れた要素が画面の真ん中に来るように設定(Alignment.Center)して Box を開始します。
}
}
}
解説: Box、Scaffold、そして HomeScreen 関数のすべての終了です。
