JetpackComposeを使ってみる

Pocket

JetpackCompseを使ってみる

どうして使ってみるの?

Jetpack Compose、Swift UIなど最近流行ってるので、そろそろ手をつけておかないと・・・
という不安から使ってみました。


Jetpack Compose?

こちらによると

UI 構築のための宣言型ツールキット

Jetpack Compose は、UI 開発をシンプルにするために設計された未バンドルのツールキットです。
リアクティブ プログラミング モデルに、Kotlin プログラミング言語の簡潔さと使いやすさを組み合わせています。

よくわからないけど、UIを書くためのものなのね


何をする?

Jetpack Composeを学ぶために、チュートリアルをやってみたいと思います。
具体的には以下のリンクです。

Jetpack Compose Basics


準備

Jetpack Composeは Android Studio4.0 Canary1 で正式にサポートされました。
こちらから4.0をダウンロードしておいてください。


新規プロジェクトを作る

まずはプロジェクトを作成します。
以下の内容でAndroid Studioで新規プロジェクトを作成してください。

  • Empty Compose Activity
  • 言語はKotlin
  • API level is 21以上


Lesson 1: Composable functions

さて、準備が終わると以下のようなコードが自動生成されます。

実はこれでLesson1の内容は終わっているのですが、少し説明してみましょう。
※Empty Compose Acitivyで作成すると Jetpack Compose Basicsとは少し異なりますが適宜読みかえてください

Jetpack Composeでは @Composable アノテーションをつけたファンクションがUIを構成する要素になります。
Text()というファンクションも @Composable がついたファンクションです。
Text()は従来のTextViewに相当するものだと考えてください。

Text()に"Hello $name"を渡すことで Hello Android! という文字が画面に表示されるようになっています。

Android Studio4.0 Canary1からはIDE上でプレビューが表示されるようになっています。
@Preview アノテーションをつけたファンクションの内容がIDE上にプレビュー表示されます。

プレビュー用のファンクション(DefaultPreview())にも@Previewの後に@Composableが必要なので忘れないでください。
プレビュー表示を更新したい場合は、リフレッシュボタンを押して更新してください。


Lesson 2: Layouts

UI要素は階層的に表示することができます。
先ほどのコードを以下のように書き換えてみましょう。

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            NewsStory()
        }
    }
}

@Composable
fun NewsStory() {
        Text("A day in Shark Fin Cove")
        Text("Davenport, California")
        Text("December 2018")
}

@Preview
@Composable
fun DefaultPreview() {
    NewsStory()
}

Columnファンクション

NewsStory()にはText()が3つありますが、右のプレビューでは重なって表示されています。
このままでは文字が読めませんので、Column() ファンクションを使って縦に並べてみましょう。
Column() ファンクションは何も設定していない状態では、左上から一つずつ縦にスペース無しで並びます。

@Composable
fun NewsStory() {
    Column {
        Text("A day in Shark Fin Cove")
        Text("Davenport, California")
        Text("December 2018")
    }
}

ColumnにStyleを設定してみる

Column()ファンクションにStyleを設定します。

@Composable
fun NewsStory() {
    Column(
            crossAxisSize = LayoutSize.Expand,
            modifier = Spacing(16.dp)
    ) {
        Text("A day in Shark Fin Cove")
        Text("Davenport, California")
        Text("December 2018")
    }
}

  • crossAxisSize
    水平方向のColumnのサイズを示します。
    LayoutSize.Expand は親要素の幅まで拡張します。

  • modifier
    様々な変更を行うことができます。
    ここでは Spacing() ファンクションを使って、Columnのスペース幅を16dpに設定しています。

画像を表示する

画像を表示してみましょう。
この画像をdrawableに保存してください。
そしてコードを以下のように変更してください。

@Composable
fun NewsStory() {
    val image = +imageResource(R.drawable.header)

    Column(
            crossAxisSize = LayoutSize.Expand,
            modifier = Spacing(16.dp)
    ) {
        DrawImage(image)

        Text("A day in Shark Fin Cove")
        Text("Davenport, California")
        Text("December 2018")
    }
}

DrwawImage()により画像が描画されました。
ただし、この状態ではテキストと重なってしまっています。
見辛いので修正しましょう。

以下のように修正します。

@Composable
fun NewsStory() {
    val image = +imageResource(R.drawable.header)

    Column(
            crossAxisSize = LayoutSize.Expand,
            modifier = Spacing(16.dp)
    ) {
        Container(expanded = true, height = 180.dp) {
            DrawImage(image)
        }

        Text("A day in Shark Fin Cove")
        Text("Davenport, California")
        Text("December 2018")
    }
}

Container の中に画像を入れて、サイズなどを設定します。

  • expanded
    Container を拡張するかを設定します。デフォルトはfalseです。
    true を設定すると親要素のサイズまで拡張します。
  • height
    Container の高さを設定します。
    expanded の設定より優先されます。

HeightSpacer() も追加して、画像とテキストの間にスペースを入れましょう。

@Composable
fun NewsStory() {
    val image = +imageResource(R.drawable.header)

    Column(
            crossAxisSize = LayoutSize.Expand,
            modifier = Spacing(16.dp)
    ) {
        Container(expanded = true, height = 180.dp) {
            DrawImage(image)
        }

        HeightSpacer(16.dp)

        Text("A day in Shark Fin Cove")
        Text("Davenport, California")
        Text("December 2018")
    }
}

こうなりました。


マテリアルデザインに対応する

マテリアルデザインにも対応しています。
やってみましょう。

画像の形を変える

Clip()ファンクションを使って画像に丸角をつけてみましょう。

@Composable
fun NewsStory() {
    val image = +imageResource(R.drawable.header)

    Column(
        crossAxisSize = LayoutSize.Expand,
        modifier=Spacing(16.dp)
    ) {
        Container(expanded = true, height = 180.dp) {
            Clip(shape = RoundedCornerShape(8.dp)) {
                DrawImage(image)
            }
        }

        HeightSpacer(16.dp)

        Text("A day in Shark Fin Cove")
        Text("Davenport, California")
        Text("December 2018")
    }
} 

ほんの少し画像の角が丸くなったのがわかると思います。

次にテキストにスタイルを適用しましょう。
MaterialTheme() で囲んであげるとマテリアルデザインが適用されます。

@Composable
fun NewsStory() {
    val image = +imageResource(R.drawable.header)

    MaterialTheme {
        Column(
            crossAxisSize = LayoutSize.Expand,
            modifier=Spacing(16.dp)
        ) {
            Container(expanded = true, height = 180.dp) {
                Clip(shape = RoundedCornerShape(8.dp)) {
                    DrawImage(image)
                }
            }

            HeightSpacer(16.dp)

            Text("A day in Shark Fin Cove")
            Text("Davenport, California")
            Text("December 2018")
        }
    }
}

このままではMaterialThemeのデフォルト値が設定されていますので、それぞれにTextStyleを設定します。


@Composable
fun NewsStory() {
    val image = +imageResource(R.drawable.header)

    MaterialTheme {
        Column(
            crossAxisSize = LayoutSize.Expand,
            modifier=Spacing(16.dp)
        ) {
            Container(expanded = true, height = 180.dp) {
                Clip(shape = RoundedCornerShape(8.dp)) {
                    DrawImage(image)
                }
            }

            HeightSpacer(16.dp)

            Text("A day in Shark Fin Cove",
                style = +themeTextStyle { h6 })
            Text("Davenport, California",
                style = +themeTextStyle { body2 })
            Text("December 2018",
                style = +themeTextStyle { body2 })
        }
    }
}

各テキストに不透明度を設定しましょう。

@Composable
fun NewsStory() {
    val image = +imageResource(R.drawable.header)

    MaterialTheme {
        Column(
            crossAxisSize = LayoutSize.Expand,
            modifier=Spacing(16.dp)
        ) {
            Container(expanded = true, height = 180.dp) {
                Clip(shape = RoundedCornerShape(8.dp)) {
                    DrawImage(image)
                }
            }

            HeightSpacer(16.dp)

            Text("A day in Shark Fin Cove",
                style = (+themeTextStyle { h6 }).withOpacity(0.87f))
            Text("Davenport, California",
                style = (+themeTextStyle { body2 }).withOpacity(0.87f))
            Text("December 2018",
                style = (+themeTextStyle { body2 }).withOpacity(0.6f))
        }
    }
}

記事のタイトルに当たる部分のテキストが短いので長くします。
記事タイトルを長くすると、改行が増えてアプリの見た目が悪くなることがあります。
次のステップで修正しましょう。

@Composable
fun NewsStory() {
    val image = +imageResource(R.drawable.header)

    MaterialTheme {
        Column(
            crossAxisSize = LayoutSize.Expand,
            modifier=Spacing(16.dp)
        ) {
            Container(expanded = true, height = 180.dp) {
                Clip(shape = RoundedCornerShape(8.dp)) {
                    DrawImage(image)
                }
            }

            HeightSpacer(16.dp)

            Text("A day wandering through the sandhills in Shark " +
                "Fin Cove, and a few of the sights I saw",
                style = (+themeTextStyle { h6 }).withOpacity(0.87f))
            Text("Davenport, California",
                style = (+themeTextStyle { body2 }).withOpacity(0.87f))
            Text("December 2018",
                style = (+themeTextStyle { body2 }).withOpacity(0.6f))
        }
    }
}

最大行数と溢れた場合の設定を追加します。
TextOverflow.Ellipsis を設定することで、溢れた場合は...で省略されるようになります。

@Composable
fun NewsStory() {
    val image = +imageResource(R.drawable.header)

    MaterialTheme {
        Column(
                crossAxisSize = LayoutSize.Expand,
                modifier=Spacing(16.dp)
        ) {
            Container(expanded = true, height = 180.dp) {
                Clip(shape = RoundedCornerShape(8.dp)) {
                    DrawImage(image)
                }
            }

            HeightSpacer(16.dp)

            Text("A day wandering through the sandhills in Shark " +
                    "Fin Cove, and a few of the sights I saw",
                    maxLines = 2, overflow = TextOverflow.Ellipsis,
                    style = (+themeTextStyle { h6 }).withOpacity(0.87f))
            Text("Davenport, California",
                    style = (+themeTextStyle { body2 }).withOpacity(0.87f))
            Text("December 2018",
                    style = (+themeTextStyle { body2 }).withOpacity(0.6f))
        }
    }
}


終わりに

今後当たり前のように使われるであろうJetpack Composeを試してみました。
コードベースなので、XMLで記述するよりはわかりやすいような気もしますが、まだあまり便利さがわかってません。
リストやボタン、タッチイベントなどがどうなるのかまだ試していませんが、そちらもそのうちやってみたいと思います。

Jetpack Composeのサンプルも見ておいた方が良さそうです。。。

追記

あんざいゆきさんがAndroid Dev Summit2019報告会で以下のような発表をされていました。
資料はこちら
これを読めばJetpackComposeの現状がわかると思います。

Pocket

匿名 へ返信する コメントをキャンセル

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です