フロントエンドエンジニアがAndroid Jetpack Composeで遊んでみた

はじめに

こんにちは、AI・IoTサービス開発部の岩丸です。

前回はVuetifyで直面した問題について執筆を行いました。もしお時間ある方は合わせてご覧ください。

tech-blog.optim.co.jp

また、2022/01/27(Thu)に開催されるOPTiM TECH NIGHTでは以前執筆したOPTiM AI Camera Web APIに関する内容で登壇を行います。 こちらも合わせてご確認くださいませ。

optim.connpass.com


今回の記事ではAndroidのJetpack Composeで遊んでみたいと思います。

普段はフロントエンドエンジニアとしてVue.js + TypeScriptと格闘しておりますが、Vue.js / React.jsのように宣言的UIでAndroidの実装が行えるということで非常に楽しみ楽しみながら遊んでみました。

目次

Jetpack Composeとは

Jetpack Composeとは、2021年の夏にv1.0がリリースされた新しいネイティブUIの構築方法です。

Android Developers Blog: Jetpack Compose is now 1.0: announcing Android’s modern toolkit for building native UI

Jetpack Composeを利用することで、より直感的にネイティブUIの実装が実現できるようになります。また、従来の実装よりもコードを削減することができ、バグ発生の抑制に繋がりコードのメンテコストが大幅に減少いたします。


公式ドキュメントに「Composeを導入する理由」という項目がございますので、まずはこちらをご一読いただけるとJetpack Composeの良さが伝わるかと思います。

developer.android.com

実際に試してみる

導入

まずは公式ドキュメントのSetupチュートリアルを参考にしながらJetpack Composeで遊ぶ準備を行います。

Jetpack Compose導入にあたりAndroid Studioのバージョンを更新する必要があります。公式チュートリアルには以下のような書かれています。

まず、Android Studio Arctic Fox の最新バージョンをダウンロードし、Empty Compose Activity テンプレートを使用してアプリを作成します。 デフォルト テンプレートにはあらかじめいくつかの Compose 要素が含まれていますが、ここでは最初から順を追って作成しましょう。

そのため、Android StudioがArctic Fox以前の方は最新バージョンへアップロードする必要があります。 アップデートすると新規プロジェクト作成時にEmpty Compose Activityの追加を確認できるかと思います。


既存プロジェクトにおける更新手順はこちらの記事で執筆されておりますので合わせてご一読ください。

tech-blog.optim.co.jp

サンプルをいじってみる

公式のチュートリアルに1つ味付けを施し、以下のようなものを作ってみます。 今回ファイル構成はフロントエンドでよく採用されるAtomic Designを混ぜてみます。

com.example.hello
├── ui
│   ├── components
│   │   ├── atoms
│   │   │   ├── ToggleButton.kt
│   │   │   ├── UserDepartment.kt
│   │   │   ├── UserImageIcon.kt
│   │   │   └── UserName.kt
│   │   ├── molucules
│   │   │   └── TextArea.kt
│   │   └── organisms
│   │       ├── TextAreas.kt
│   │       └── UserContent.kt
│   └── theme
│       ├── molucules
│       ├── molucules
│       ├── molucules
│       └── organisms
├── MainActivity.kt
└── MainScreen.kt

実際に作ってみたもの以下の通りです。テキストフィールドへの入力が反映されるという簡単なものです。

f:id:optim-tech:20220124105446g:plain

遊んでみた感想

1. Vue.jsやReact.jsを実装しているのと感覚が変わらずに実装できる

以下のコードは/organisms/UserContent.ktの@Composable部分です。 以下の実装からも分かるようにコンポーネントを組み立てるように実装が可能となり、Vue.jsやReact.jsを実装しているのと体感変わらずに実装が可能でした。

主戦場がフロントエンドの方でも始めやすいなと感じた点の1つです。

@Composable
fun UserContent(name: String, department: String){
    Row(modifier = Modifier.padding(all = 8.dp)) {
        // アイコン
        UserImageIcon()
        
        Spacer(modifier = Modifier.width(8.dp))
        
        Column {
            // ユーザー名
            UserName(name = name)
            Spacer(modifier = Modifier.width(4.dp))
            Surface(
                shape = MaterialTheme.shapes.medium,
                elevation = 1.dp
            ) {
                // 所属
                UserDepartment(text = department)
            }
        }
    }
}

2. プレビューモードが嬉しい

個人的に感動した点はプレビューモードが存在することです。公式ドキュメントでも説明があるようにAndroid Studioでプレビューすることができます。

上記のコンポーネントのプレビューは以下のようになります。

f:id:optim-tech:20220124102522p:plain

プレビューモードでは@Previewに対して渡すパラメータを変えることでライトモード、ダークモードの確認も可能です。

Storybookが気軽に使える環境がすでに準備されていると考えると、非常にありがたい機能だと感じました。

@Preview(
    name = "Light Mode",
    showBackground = true
)
@Preview(
    name = "Dark Mode",
    showBackground = true,
    uiMode = Configuration.UI_MODE_NIGHT_YES
)
@Composable
fun PreviewUserContent(){
    HelloTheme {
        UserContent(name = "GANGAN", "株式会社OPTiM")
    }
}

3. 状態ホイスティングを使用することでステートレスなコンポーネントの実現が可能である

Composeには状態ホイスティングという考え方があります。公式ドキュメントでは以下のように説明されています。

Compose の状態ホイスティングは、状態をコンポーザブルの呼び出し元に移動してコンポーザブルをステートレスにするプログラミング パターンです。Jetpack Compose の状態ホイスティングの一般的なパターンでは、状態変数を次の 2 つのパラメータに置き換えます。

value: T: 表示する現在の値。

onValueChange: (T) -> Unit: 値の変更をリクエストするイベント。Tは提案される新しい値です。


例えば、上記で実装したTextFiledを参考に状態ホイスティングを見ていきたいと思います(※ 説明用に修正を加えているためGIFとは少し異なります)。


親コンポーザブルは以下の通りです。親コンポーザブルではrememberコンポーザブル(参照: 公式ドキュメント/コンポーザブル内の状態 )を用いてinputNameの内容を保存することができます。

例えば、子コンポーザブル側でinputTextのvalueが変更されると、親側のinputNameも変更されます。変更する値と値を変更するイベントを親側から渡すので、子コンポーザブル側はステートフルとなりテスト実装が用意になります。また、再利用性も高まります。

// 親コンポーザブル
@Composable
fun TextAreas() {
    var inputName by remember { mutableStateOf("") }

    TextArea(
        description = "名前を入力してください。",
        inputText = inputName,
        onTextChange = { inputName = it }
    )
}

子コンポーザブルは以下の通りです。親側からパラメータを受け取るので子コンポーザブルを「UIの描画」に専念させることが出来ます。

// 子コンポーザブル
@Composable
fun TextArea(description: String, inputText:String, onTextChange:(String) -> Unit) {
    Column(modifier = Modifier
        .padding(all = 4.dp)
        .border(
            1.0.dp,
            color = MaterialTheme.colors.primary)
    ) {
        Text(
            text = description,
            modifier = Modifier.padding(bottom = 8.dp)
        )
        TextField(
            value = inputText,
            onValueChange = onTextChange,
            modifier = Modifier.padding(all = 4.dp)
        )
    }
}

おわりに

今回はフロントエンドエンジニアがAndroidのJetpack Composeで遊んでみました。 Jetpcak ComposeやSwiftのSwift UIの登場で普段の知見を活かせる場所がどんどん増えると、普段と異なる技術での楽しさもより広がると思います。

KMM(Kotlin Multiplatform Mobile)のベータ版発表もあり、Kotlin、Jetpack Composeの今後の発展が楽しみです。


OPTiMではフロントエンド、モバイル、AWSなど技術領域に関わらずマルチに挑戦するエンジニアを随時募集しております。少しでもご興味のある方はこちらをどうぞご覧ください。

www.optim.co.jp