「演習:スーパーヒーローのアプリを作成する」をやりました!
はじめに
この記事は、自分が公式チュートリアル中の「演習:スーパーヒーローのアプリを作成する」でアプリを作成したときにどのようなことを意識して作ったのかをまとめた記事となっています。
演習の概要
この演習で作ることになったアプリは上のスクリーンショットのようにヒーロー一覧を表示させるというものになっています。
演習の流れは、フォントやPaddingの値などある程度の指示があり、それに従いつつアプリを作っていくというものでした。
やっている側が考えてやらなければいけないところはリストのアイテムの配置、リストの作成、トップバーの作成ぐらいで他はどのようにすればいいのか指示をくれるので考えてやるべきところは少なかったです。
意識した点
この演習は、「JetpackComposeでのマテリアルテーマ設定」や「JetpackComposeでのシンプルなアニメーション」というコードラボを踏まえたうえでの演習だったため、そこで学んだことを第一に意識し行いました。自分的に学んでためになったことは過去にそれぞれ記事にしてまとめたので良ければご覧ください(箇条書きしただけですが…)。
JetpackComposeでのマテリアルテーマ設定
JetpackComposeでのシンプルなアニメーション
学んだ中で特に重要だと思い今回の演習で特に意識した点は以下の3つです。(下2つはコードラボの本筋とはそれるため記事に書きませんでしたが、コードラボを進める際に重要だと思った点で、今回意識して書きました。)
paddingやsizeなどの値をそのまま渡さない
Itemを要素ごとに分けそれぞれのComposable関数を作る
上のComposableからmodifierを渡す(それぞれのComposableにmodifierの引数を入れる)
ここからは、どのようにしてコードを書いたのか、重要だと思った理由などをそれぞれ説明しようと思います。
paddingやsizeなどの値をそのまま渡さない
これは文字通りなのですが、paddingを呼び出す際にそのままdpを渡さないようにしました。
やらないようにしていた例
modifier = Modifier
.padding(16.dp)
やっていた例
modifier = Modifier
.padding(dimensionResource(id = R.dimen.padding_medium))
<resources>
・・・
<dimen name="padding_medium">16dp</dimen>
・・・
</resources>
このように、値をそのまま渡さず値をまとめたファイルを作成しそこから呼び出すというふうにしました。こうすることで同じ値を使っている場合に変更を一箇所するだけでいい、どのような値が使われているのか1つのファイルを開くだけで確認できる、これらの理由から重要だと判断し、今回意識してコードを書きました。
Itemを要素ごとに分けそれぞれのComposable関数を作る
上のようにItemを作成という指示だったため、テキストの部分、写真の部分と2つに要素を分けComposable関数を作成しました。
作成したコードは以下のとおりです。
// 作成したItem
@Composabl
fun HeroesListItem(
hero: Hero,
modifier: Modifier = Modifier
) {
Card(
modifier = modifier,
elevation = CardDefaults.elevatedCardElevation(
defaultElevation = dimensionResource(id = R.dimen.card_elevation)
),
shape = MaterialTheme.shapes.medium
) {
Row(
modifier = Modifier
.padding(dimensionResource(id = R.dimen.padding_medium))
.fillMaxWidth()
.height(dimensionResource(id = R.dimen.card_content_height))
) {
// テキストの部分
HeroInfo(
modifier = Modifier.weight(1f),
heroName = hero.nameRes,
heroDescription = hero.descriptionRes
)
Spacer(modifier = Modifier.width(dimensionResource(id = R.dimen.padding_medium)))
// 写真の部分
HeroImage(
heroImage = hero.imageRes
)
}
}
}
// テキストの部分のComposable関数
@Composable
fun HeroInfo(
modifier: Modifier = Modifier,
@StringRes heroName: Int,
@StringRes heroDescription: Int
) {
Column(
modifier = modifier
) {
Text(
text = stringResource(id = heroName),
style = MaterialTheme.typography.displaySmall
)
Text(
text = stringResource(id = heroDescription),
style = MaterialTheme.typography.bodyLarge
)
}
}
// 写真の部分のComposable関数
@Composable
fun HeroImage(
@DrawableRes heroImage: Int
) {
Image(
modifier = Modifier
.size(dimensionResource(id = R.dimen.card_hero_image_size))
.clip(MaterialTheme.shapes.small),
painter = painterResource(id = heroImage),
contentDescription = "HeroImage",
contentScale = ContentScale.Crop
)
}
そこまで量が多くなく、この演習的に要素を他の場所で使うことが無いため今回はItemの中で記述しても問題無いですが、今後開発をしていく中で同じようにItemを作らなければならず、要素を取り出して他の場所で使いたいなどといった場面が出てくることも考えられるため、それぞれの要素に分けコードを書きました。
上のComposableからmodifierを渡す(それぞれのComposableにmodifierの引数を渡す)
このようにすることで、上のComposableで大きさを決めることが出来るため再利用性が高まります。
上のItemの例で言うと、テキストの横幅もっと広くしたItemを作りたいとなった場合にItem側からModifier.fillMaxWidth()を渡し横幅を広くすることが可能になります。もし、テキストのComposable側でModifierを設定していたら、普通の横幅バージョンと横幅広いバージョンの2種類用意しなければならず面倒になります。
また、weightを使う際にはModifierを上から渡さなければ使用することが出来ないため必須でもありました。
さいごに
演習には解答コードがついてあり、それと比べて見ていたのですが、コードが全然違くて驚きました。解答コードではそのまま値を渡しているし、要素を分けてそれぞれのComposableを作っていないし…
解答コードで、以下のコードはいいなと思ったので今後も使おうと思いました。(最低限のサイズを指定できるもの)
modifier = Modifier
.sizeIn(minHeight = 72.dp)
自分は今回学んだことを活かして今後もコードを書いていこうと思います。
今回書いたコードはGithubに上げたのでよければご覧ください。
今後の予定なのですが、春休み中に「Composeを用いたAndroidアプリ開発の基礎」すべて終わらせたいと考えており時間も少ないため、あまり重要ではないと判断したやつは飛ばしてやっていこうと思います。(すでに1つ飛ばしています…)
次は「アクティビティのライフサイクルのステージ」をやろうと思います。