Android
 Computer >> コンピューター >  >> システム >> Android

KotlinとJetpack Composeで作るAndroid数独アプリ開発講座

Jetpack Composeは、AndroidのネイティブUIを構築するためのモダンな公式ツールキットです。宣言的なアプローチにより、従来のXMLベースの開発よりもUI開発をシンプルかつ迅速に進められるのが大きな特徴です。

無料でプログラミングを学べることで有名なfreeCodeCamp.orgのYouTubeチャンネルでは、KotlinとJetpack Composeを使って数独(Sudoku)アプリをゼロから作り上げるコースが公開されました。視聴時間は約3.5時間。開発を進めながら、グラフデータ構造とアルゴリズムについても自然に学べる構成になっています。

講師を務めるのはRyan M. Kay氏。長年の経験を持つ開発者であり、優れた教育者としても知られています。

コースで学べるトピック一覧

  • アプリ設計方針: サードパーティライブラリの最小限利用と「MV-Whatever」アーキテクチャ
  • Domainパッケージ: リポジトリパターン、Enum、データクラス、Sealed Class、ハッシュコード、インターフェース
  • Commonパッケージ: 拡張関数・拡張プロパティ、開放閉鎖原則(OCP)、抽象クラス、シングルトン
  • Persistenceパッケージ: Javaファイルシステムによるバックエンド、Jetpack Proto DataStore
  • UIパッケージ: Compose UIの基本、スタイル、タイポグラフィ、ライトテーマとダークテーマ
  • UI Componentsパッケージ: Modifier、再利用可能なツールバーとローディング画面
  • Active Game機能: コルーチンを活用したViewModelとプレゼンテーションロジック、Kotlin関数型
  • 数独ゲームUI: Jetpack ComposeによるUI構築とActivityコンテナの実装
  • 計算ロジック: 4×4や9×9など任意サイズの正方形数独に対応するグラフデータ構造・アルゴリズムの設計とテスト

設計思想:ライブラリ最小主義とMV-Whateverアーキテクチャ

このコースの大きな特徴は、ComposeとProto DataStore以外のAndroid Jetpackライブラリをほとんど使わない点です。KotlinおよびJava標準ライブラリとAndroid SDKに依存することで、ライブラリの非推奨化や仕様変更への耐性が高まります。その一方で、Jetpack ViewModelやNavigationが担うような処理は手書きが必要になりますが、小規模アプリでは驚くほど簡単に自作できることも本講座で実証されます。

アーキテクチャについては「Model View Whatever」という考え方を掲げています。特定の流派に固執せず、良いソフトウェア設計の原則とプロジェクトの要件に従ってアーキテクチャを決めていく姿勢です。ComposeはMVVM向きですが、あえてプレゼンテーションロジッククラス(Passive View / Humble Objectパターン)を分離することで、View Modelが肥大化する「Godオブジェクト」化を防ぎ、テストしやすい構造を実現しています。その根底にあるのは、ソフトウェアアーキテクチャにおける最も重要な原則である関心の分離です。

Domainパッケージ:データモデルとリポジトリパターン

Domainパッケージは、プログラムが扱う最も一般的な情報(データクラス、定数、enum)と、プログラムが行うべき最も一般的な操作(関数・インターフェース)を表します。いわばアプリの土台です。

難易度を表すEnumとデータクラス

まずDifficultyというenumクラスを作成し、各エントリにDouble型の値を持たせることで、アルゴリズム側でパズルの難易度を制御できるようにします。続いてSettingsデータクラス(難易度と盤面サイズboundaryを保持)、ユーザーの最短記録を表すUserStatisticsデータクラス(完了時間をミリ秒単位のLong型で保存)を定義します。dataキーワードを付けると、equals、hashCode、copyなどのヘルパーメソッドが自動生成される点も解説されます。

SudokuNodeとハッシュコードの工夫

核となるSudokuNodeデータクラスは、グラフデータ構造における個々のノードを表現します。ここでの「色」とは単なる数値(1〜9または1〜4、0は空マス)であり、x・y座標(ゼロ始まり)と編集不可を示すreadOnlyフラグも持ちます。ハッシュコードはx座標を100倍してy座標と組み合わせる独自のアルゴリズムで生成され、9×9盤面でもすべてのタイルに対して一意なキーを保証します。これはLinkedHashMapのキーとしてノードを管理するために活用されます。さらにSerializableを実装することで、ファイルへの読み書きが可能になります。

リポジトリインターフェースと関数型

IGameRepositoryなどのインターフェースには、リポジトリ(ファサード)パターンを採用しています。データ保存の詳細を抽象の背後に隠すことで、実装クラスと疎結合になり、フェイク実装によるテストや、ファイルストレージからRoomデータベースへの差し替えも容易になります。suspendキーワードによるコルーチン対応に加え、onSuccess / onErrorといったKotlin関数型を引数として渡すことで、実装側から結果をコールバックする仕組みを解説。成功時のみUnitを返すケースや、Booleanでゲーム完了を通知するケースなど、関数型の使い分けも丁寧に示されます。

また、関数型プログラミングのEitherモナドに着想を得たResultラッパーをSealed Classで定義し、1つのオブジェクトで成功・失敗の複数状態を表現する方法も紹介されます。Sealed ClassはKotlinならではの強力な機能で、限定された型の集合を安全に扱えます。

Commonパッケージ:再利用可能コードの基盤

Commonパッケージには、様々なクラスや関数で再利用されるコードを配置します。ここでは拡張関数・拡張プロパティ、抽象クラス、開放閉鎖原則(OCP)、object宣言によるシングルトン、コルーチンディスパッチャーといったKotlinの言語機能を効率的に学べます。

開放閉鎖原則(OCP)の実践的理解

OCPとは「ソフトウェア要素は拡張に対して開かれ、修正に対して閉じられているべき」という原則です。頻繁に再利用され変更が予想されるソフトウェア要素(例:Activity)は、公開インターフェースを固定しつつ、実装を差し替えられる仕組みが必要です。もしAndroidプラットフォームがonCreateの引数を突然削除すれば、世界中のコードがコンパイルできなくなるでしょう。だからこそ公開インターフェースは安定させ、実装の変更は継承や拡張で吸収するのです。

拡張関数の便利さ

Extensions.ktファイルには、Activity向けのToast表示を簡略化するmakeToast拡張関数や、経過時間を「分:秒」形式の文字列に変換する拡張関数などを実装。拡張関数は既存ソースコードを修正することなく新機能を追加できる、Javaの静的ユーティリティクラスに代わる強力な手段です。

DispatcherProviderでテスト容易性を確保

DispatcherProviderインターフェースとProductionDispatcherProvider(object=シングルトンでスレッドセーフ)を用意することで、本番環境ではMainディスパッチャーとIOディスパッチャーを返し、JVM上のユニットテストではUnconfinedを返す、という切り替えが可能になります。要するにコードをテストしやすくするための工夫です。

Persistenceパッケージ:データの永続化

Persistenceパッケージは、Androidプロセスのライフサイクルを超えてデータを保存する役割を担います。現在のゲーム進行状況、設定、ユーザーの個人記録(統計)を保存します。アーキテクチャとしては、ゲームリポジトリが2つのデータソースを調整するバックエンドの意思決定者として機能し、CRUD操作を実行して成功か失敗を報告します。実装が変更される可能性のある箇所では必ず抽象化を行う——これは過剰設計ではなく計算された判断です。

Proto DataStoreによる設定と統計の保存

ゲーム設定とユーザー統計には、Protocol Buffers(JSONに似たシリアライズ言語)を使うProto DataStoreを採用。.protoファイルでメッセージとenumを定義すると、プロトコルバッファコンパイラがJavaクラスを自動生成してくれるため、ボイラープレートコードを書く必要がありません。DataStoreデリゲートとSerializerを実装することで、入出力ストリーム周りのエラー処理もライブラリが簡潔なAPIで引き受けてくれます。

ファイルシステムによるゲーム進行の保存

現在アクティブなゲームの進行状況は、端末の専用ファイル領域に保存します。DomainモデルがSerializableを実装しているおかげで、JavaのObjectInputStream / ObjectOutputStreamを使ってオブジェクトをそのまま読み書きできます。ストリームは必ずクローズする、IO処理はwithContextでメインスレッド外へ逃がす、といった実務的なポイントも押さえられます。

GameRepositoryImpl:バックエンドの司令塔

GameRepositoryImplは、ゲームストレージと設定ストレージという密接に関連する2つのデータソースを調整する橋渡し役です。アプリ起動時に「進行中のゲームがある」「初回起動でまだ何もない」「途中でエラーが発生した」といった複数のイベントストリームがあり得るため、コードを書く前に平易な言葉でユースケースをコメントとして書き出しておく手法が紹介されます。複雑な条件分岐でも可読性を保つため、非常に長く説明的な命名をあえて採用している点も参考になります。

UIパッケージ:テーマ、カラー、タイポグラフィ

UIパッケージのトップレベルには、Color.kt、Shape.kt、Type.kt、Theme.ktの4つの小さなファイルがあります。

Color.ktはcolors.xmlの代替となるファイルです。16進数カラーコードの読み方(先頭2桁がアルファ値=不透明度、以降がRGB)を解説しながら、カラーオブジェクトを定義します。Shape.ktでは角丸背景などをkotlinコードだけで定義し、dp拡張によって密度非依存ピクセルを指定。Type.ktではTextStyleを複数作成し、Typographyオブジェクトにまとめます。

GraphSudokuThemeコンポーザブルでは、isSystemInDarkTheme()でシステム設定を取得し、ライト用とダーク用の2つのカラーパレットをMaterialThemeに適用。わずかなコード量でダークモード対応が完結することを、実際のデモとともに示します。

UI Components:再利用性の高い部品設計

UI Componentsパッケージには、複数画面で使い回せるコンポーザブルを配置します。このアプリではツールバーとローディング画面の2つです。

Composeでコンポーネントを再利用可能にする方法は主に2つ。ひとつはModifierを親から渡す方式で、配置やサイズの決定権を親コンポーザブルに委ねます。もうひとつはコンポーザブルそのものを引数として渡す方式で、このアプリでは2画面それぞれ異なるツールバーアイコンを渡すことで、同じAppToolbarを使い回しています。onClickの扱いやアイコンの見た目をツールバー本体ではなく親側で決めることが、再利用性を生む鍵です。

LoadingScreenではSurfaceの中にColumnでロゴ、プログレスバー、テキストを縦に積み重ね、fillMaxHeight(0.8f)で画面の大部分を占めつつ広告バナー等のスペースも確保します。painterResourceはビットマップかベクターかを自動判別してくれる便利なAPIで、copy関数を使えば色のアルファ値だけ変えた派生スタイルも簡単に作れます。

Active Game機能:ViewModelとプレゼンテーションロジック

このセクションでは、ActiveGameEvent Sealed Class、ViewModel、Presenter(Logicクラス)を作成します。

プレゼンテーションロジッククラスは、コンテナ・ViewModel・バックエンドリポジトリの調整役であり、非停止イベントの通知を受けるとすべてのコルーチンをキャンセルします。Androidプラットフォームのコードを一切含まないため、疎結合かつテスト容易で、デスクトップ版への転用も視野に入ります。

ViewModelはViewが観察する「UIの仮想表現」です。Jetpack ViewModelを継承しない理由は、(1) Androidプラットフォームとの緊密な結合を避けたい、(2) このアプリではActivity再生成やプロセス死を跨ぐデータ保持が不要で、ファイルシステムからの安価な再読み込みで十分だから。ただし、この手法がすべてのアプリに適するわけではない点にも注意を促しています。購読者への通知には、複数オブザーバが不要な場合にシンプルなKotlin関数型を使う「貧者のObserverパターン」を採用しています。

さらに、StrategyパターンとしてActiveGameEvent Sealed Classを導入。UIイベントごとに関数を増やすのではなく、単一のonEvent関数が全イベントを表現できるオブジェクトを1つ受け取ることで、クラス間インターフェースをすっきりさせます。

コルーチンタイマーはinline + crossinlineキーワードを使ったwhile(true)ループで実装。delay(1000)はスレッドをブロックせずコルーチンを中断する点がポイントで、Jobオブジェクトをキャンセルすることで停止します。crossinlineはラムダ内でのreturnを禁止し、意図しない早期リターンを防ぐ保険です。

Composeで数独ゲーム画面を構築する

最後に、ActiveGameActivityをコンテナとして、数独ボードを含むゲーム画面をComposeで組み上げます。ActivityはsetContentでコンポーザブルをホストし、イベントハンドラとしてlogic::onEventという関数参照を渡します。関数型とStrategyパターンの組み合わせは、onClickイベント処理の優れた手法です。依存関係注入も、HiltやDaggerのようなDIコンテナを使わず、シンプルな拡張関数として手書きすることで、フレームワークの裏側で何が行われているかを理解できます。

画面のルートコンポーザブルであるActiveGameScreenは、Loading / Active / Completeの3状態をmutableTransitionStateで管理し、アルファ値の遷移アニメーションで状態間をフェード切り替えします。rememberデリゲートでstateの変更を監視し、ViewModelの関数型にラムダを束縛することで、データ更新が自動的に再コンポジションを引き起こす仕組みを解説します。

数独ボード本体はBoxWithConstraintsで画面幅を取得し、LocalDensityを介して密度非依存のタイルサイズと余白を計算。ConstraintLayoutでボード・難易度アイコン・タイマー・入力ボタンを配置し、ヘルパー関数に分割することで「Godコンポーザブル」化を回避します。81個ものTextウィジェットを効率よく描画し、readOnlyマスと編集可能マスを見た目で区別、sub-grid境界線は太さを変えて描画するなど、細部まで配慮された実装内容が丁寧に紹介されます。

まとめ

本コースは、Jetpack Composeの入門にとどまらず、リポジトリパターン、OCP、Strategyパターン、コルーチン、Proto DataStore、グラフアルゴリズムまで、実践的なアーキテクチャ設計を一気通貫で学べる充実の内容です。「コードを一緒に書きながら学ぶ」形式のため、初心者は自分のペースで手を動かし、上級者は完成ソースコードを読んで知識の穴を埋めるのに最適です。Androidアプリ開発の土台を固めたい方は、ぜひfreeCodeCamp.orgのYouTubeチャンネルで受講してみてください。

  1. 無料Androidアプリ3選!アニメーションテキストを簡単に作成する方法

    アニメーションテキストは、普通の静止テキストよりもずっと目を引きます。動きのあるテキストに加えられるさまざまなエフェクトによって、伝えたいメッセージがぐっと魅力的になります。嬉しいことに、こうしたクリエイティブな制作を手軽にサポートしてくれるアプリが存在します。以下で紹介するアプリはすべて無料です。もし期待していたものと違ったとしても、アンインストールするだけで済みます。各アプリには多彩なオプションが用意されており、映画のイントロ風のスタイルなど、個性豊かなアニメーションテキストを作成できます。1. Animated Textリストの筆頭に挙げられるのが「Animated Text」です。操作

  2. React Native と Native Base を使用して Android ニュース アプリを作成する方法

    私たちは物事が絶えず変化する世界に住んでいます。したがって、何が起こっているかを最新の状態に保ちたい場合は、優れたニュースアプリが必要になります. クールな技術を学び、最新の状態を保つために、このブログ投稿では、React Native を使用して Android 用のニュース アプリケーションを作成します。さまざまなニュース チャンネルから見出しを取得し、カテゴリ別に表示します。 完了すると、アプリは次のようになります。それでは早速見ていきましょう。 Expo のインストール方法 では、万博とは? Expo は、React Native アプリをすばやく簡単に構築してデプロイするの