【Kotlin】 Jetpack Composeを使ってりんご何個分?アプリを作ろう

Advent Calendar 2024, Android, Kotlin

この記事はレコチョクAdvent Calendar 2024 の21日目の記事となります。

はじめに

こんにちは、Androidアプリ開発グループの我那覇です。
私は今年4月に新卒で入社し、10月よりAndroidグループに配属されました。

プログラミングは完全未経験の状態で入社し、Androidの開発は現在で3か月目となります。
今回はこれまでの学習を振り返りながら、簡単なアプリを作成したので紹介したいと思います!

自己紹介

  • 四年制大学の音楽学科卒
    • 楽器、音楽療法、コンピュータ音楽(創作)を学ぶ
  • プログラミング経験:入社前まではほぼ未経験
    • 入社後、レコチョクで5か月間のエンジニア研修を通して学ぶ
  • 現在:Androidグループ配属され、OJT期間中
    • Googleが作成した教材「Codelab」を用いてAndroid開発を学習中

エンジニアとは縁のない人生を歩んできましたが、パソコンで音を入力・編集したり、自分の手で何かを作り出すことが好きでした。
また、大好きなサンリオのWebページやアプリを見て、この可愛いサイトやアプリはどうやってできているのだろう?と興味を持ったことが、エンジニアを目指したきっかけでした。

そこで今回は、サンリオの可愛いサイトを参考にアプリを作成しました!
至らない点も多くあるかと思いますが、ご指摘やアドバイスなどあれば遠慮なくいただけると嬉しいです。

目指すもの

今回参考にしたのは、ハローキティはりんご3個分〜ドキドキ♡身体測定〜というサイトです。
KotlinとJetpack Composeを使用して身長をりんごに変換する機能を実装し、参考サイトのようにアニメーションを豊富に取り入れた見た目を目指しています。

完成品

20241215_225124.GIF

やること

  • 入力画面
    • 入力された身長をりんごの高さに変換する
    • 測定ボタンを押すと結果表示画面に遷移する
  • 結果表示画面
    • 変換した個数分のりんごの画像を表示する
    • りんごを上に積み上げる
    • りんご全体を左右に揺らす
    • りんごの数をカウントアップして表示する

開発環境

  • macOS 14.4.1 Sonoma
  • Android Studio Koala | 2024.1.1 Patch 1
  • Kotlin 1.9.0
  • Compose 1.6.6

ロジックの作成

はじめに、ユーザーから入力された身長をりんごの高さに変換するロジックを作成します。
ここでは、UIと分離して管理するために ViewModelを用います。

参考:コードラボ
ViewModel にデータを保存する

ユーザーが入力した身長を1りんごの高さ(8cm)で割り、その結果を MutableStateFlowで管理します。
これにより、他のコンポーネントがリアルタイムで結果を受け取ることができ、入力に応じてUIにりんごの個数が反映されます。

これで、身長をりんごの高さに変換するロジックができました。
※ 参考サイトのりんごは約10cmでしたが、私が購入したりんご(サンふじ)が8cmだったため高さを8cmに設定しています🍎

UIの作成

画面遷移
身長を入力する画面から結果を表示する画面への画面遷移を実装します。

  • 定数を定義:enum(列挙型)のクラスを作成し、アプリ内の各画面を定義します。
  • ナビゲーションを設定:画面間を移動するための NavControllerを使ってナビゲーションを管理し、 NavHostで画面(フラグメント)を設定します。

参考:コードラボ
Jetpack Compose でのナビゲーション

これで画面遷移の設定ができました。
次に、身長を入力する画面と結果を表示する画面を作成します。


入力画面

  • テキストフィールドvalue属性に heightInputを設定し、ユーザーの入力があるたびに onValueChangeを通じて内容を更新するようにします。
  • ボタン:ボタンがクリックされ、 Double型に変換できた(有効な数値が入力された)時に viewModel.setHeight(height)を呼び出し、身長をりんごの個数に換算します。

スクリーンショット 2024-12-16 1.10.17.png

参考:コードラボ
Compose の状態の概要


結果画面
次に、ユーザーが入力した身長の結果をりんご単位で表示する画面を作成します。

  • りんごの取得と表示:最初に作成した ConversionViewModelからりんごの数を取得し、それに基づいて身長がりんご何個分かを表示します。
  • フォーマット"%.1f".format(appleCount)を使用し、 appleCountの値を小数点以下1桁まで表示するようにします。

20241216_012514.GIF


りんごの画像を表示
りんごを縦に並べて表示するために、 Columnコンポーザブルを使用して配置します。

💡Point:少数部分のりんごも画像で忠実に再現するために、整数部分と少数部分をリストに分け、それぞれに対応する画像IDを設定して表示するようにしました!

  • りんごの個数を取得:取得した appleCountに基づいて、りんごの画像IDを含むリストを生成します。
  • スクロール機能verticalScroll(rememberScrollState())を使用し、多くのりんごが画面に表示された場合でも、スクロールして全体を表示できるようにします。
  • 画像の表示:生成された appleList内の各画像IDを AppleItemコンポーザブル関数を通じて描画し、各りんごの画像を表示します。
    また、少数部分のりんごが画面の上部に配置されるように、 asReversed()を利用してリストを逆順にしています。

20241216_013416.GIF

参考:コードラボ
Kotlin でコレクションを使用する
基本的なレイアウトを作成する

スクリーンショット 2024-12-16 0.57.02.png

ここでは when式を使用し、小数部分のりんごに対応した画像IDを選択するための条件分岐を行っています。

これで、身長をりんごの高さで表現することができました!

アニメーションの追加

最後に、より参考サイトに近づけるためにアニメーションを追加します。

りんごを左右に揺らす

  • 回転の初期値を設定remember { Animatable(-2f) }として、回転角度の初期値を -2f(-2度の位置)に設定します。
  • アニメーションの設定
    • targetValueでアニメーションの目標値を2fに設定し、-2度から2度の間で左右に揺れるようにします。
    • animationSpec = infiniteRepeatableを使用して、アニメーションが繰り返されるように設定します。
    • FastOutSlowInEasingを使用して、アニメーションが素早く始まり徐々に減速するように設定します。これにより、りんごが自然に揺れているように見せることができます。
    • RepeatMode.Reverseを使用して、アニメーションが終了したときに反転し、逆方向に進むようにします。これで左右に揺れるアニメーションを連続させることができます。
  • 基点の位置を設定graphicsLayerプロパティ
    • 回転の基点を、水平方向に 0.5f(コンポーネントの幅に対して50%の位置 中央)、垂直方向 に 1f(コンポーネントの高さに対して100%の位置 一番下)に設定します。

こうすることで、積み上がったりんごが一番下のりんごを基点に、左右に揺れるようになりました!

20241216_021702.GIF

参考:アニメーションをカスタマイズする

文字のアニメーション
最後に、りんごの数を表示する数値部分にカウントアップ表示のアニメーションを設定します。
これは揺れのアニメーションとほぼ同様の方法で実装しているため、詳細な説明は省略します。

  • remember { Animatable(0f) }で初期値を保持します。
  • animationSpectween関数を使用し、2000ミリ秒(2秒)かけて加速させます。
    同時に、 FastOutSlowInEasingを設定して始まりと終わりが緩やかになるように調整しています。

20241216_022844.GIF

最後に

長文となりましたが、最後までお読みいただきありがとうございます!
学んだ内容を振り返りつつ、目指していたものを形にすることができました。
想定通りにいかないことも多く苦戦しましたが、一からアプリを作成し記事にまとめる過程で、学んだことをアウトプットし、より一層理解を深めることができたと感じています。

これからも楽しみながら、一歩一歩頑張っていきたいと思います。

明日の レコチョク Advent Calendar 2024 は22日目「デザインシステムでデザインが“ととのい”はじめた」です。お楽しみに!