【Android】Jetpack Composeでよく使うプロパティをまとめてみた

こちらの記事は[食べログAdvent Calendar2021](https://qiita.com/advent-calendar/2021/tabelog)の20日目の記事になります。

今年の8月に食べログのAndroidエンジニアとして入社しました、柴犬好きな @hokutonikukyu です！
食べログでは、主にUI/UX周りの開発・修正を担当しています！

AndroidのJetpack Composeが追加されてしばらく経ちますが、まだがっつり触れることができていないです、、、
今回は自身のメモがてらComposeのViewプロパティについて少しだけまとめてみようかと思います！！

#はじめに
JetpackComposeとはどういうものなのか、軽く触れます。
以下、[ **公式ドキュメント** ](https://developer.android.com/jetpack/compose/why-adopt?hl=ja)からの抜粋です。
> Jetpack Compose は、ネイティブ UI をビルドするための Android の最新ツールキットです。少ないコード、パワフルなツール、直感的な Kotlin API を使用してアプリをすぐに動かすことができ、Android での UI 開発を簡素化し、加速します。Android UI の構築がより迅速かつ容易になります。

>Compose では宣言型 API を使用します。つまり、必要なのは UI を記述することだけであり、残りの処理は Compose が行います。API は直感的で、簡単に見つけて使用できます。「私たちのテーマレイヤは非常に直感的で判読が容易です。通常であれば複数のレイヤで構成されるテーマ オーバーレイを介して属性の定義と割り当てを処理する複数の XML ファイルにまたがる機能を、単一の Kotlin ファイル内で実現できるようになりました。」

今まではxml形式にてUI作成を行い、対象のjavaやKotlinのファイルと紐づける必要がありましたがComposeではそれが必要なくなります。
大まかに伝えるとすると「UIをソース上で簡単に作ることができるようになりましたよ」というものです。
実装を進めているとパーツとしてxmlを複数作って１つに組み合わせたりすることがあるのですが、如何せんファイル数もそれに応じて増えてしまい、探したり参照するのも手間だったりしました。
そういった煩わしさはこれで解消されているかと思います！

#紹介の前に
環境設定などはこの記事では割愛します。
[**こちら**](https://developer.android.com/jetpack/compose/setup?hl=ja)の公式ドキュメントを参照の上導入しましょう！

尚、ComposeではIDE上でのViewの表示確認のために
**@Preview**アノテーションをメソッドにつける必要があります。
表示確認用のために以下のようにPreview用のメソッドを作成することをお勧めします。

```kotlin
@Preview
@Composable
private fun ALLLayout() {
    TestText()
}

@Composable
private fun TestText() {
    Text(
        text = "Hello World!!"
    )
}
```

 ※この記事は2021/12時点でのものです。（使用version：1.0.2）
　参照される際は、情報が古くなっている可能性があるためお気をつけください。

##Text
TextViewの代わりとなるコンポーネントです！！
Textでは今までTextViewでできていたことは大抵実現できるようになっているかと思います。
下記がよく使うことになるであろうプロパティです。

|プロパティ名|設定内容|
|:-------|:-------|
|**text**|表示したい文字・数値をセット|
|**modifier**|Viewの高さや幅、クリックイベントなどをセット（Modifier.**で指定）|
|**color**|文字の色|
|**fontSize**|文字のサイズ（n.sp）|
|**fontStyle**|文字のスタイル（斜体 or 通常　FontStyle.**で指定）|
|**fontWeight**|文字のスタイル（太字など FontWeight.**で指定）|
|**fontFamily**|文字のフォント（FontFamily.** or res/fontsで指定して新しいFontFamily作成可能）|
|**letterSpacing**|文字と文字の間隔（n.sp）|
|**textDecoration**|文字の装飾（アンダーライン等 TextDecoration.**）|
|**textAlign**|文字の位置（中央寄せなど TextAlign.**で指定）|
|**lineHeight**|テキストボックスの高さを指定する|
|**overflow**|文字のオーバーフロー 領域を超えた際に…にする等（TextOverflow.**で指定）|
|**softWrap**|行の折り返しを許可するかどうか（Boolean）|
|**maxLines**|文字の最大行数|
|**onTextLayout**|Textが生成されたときのViewのStatusを取得するlistener|
|**style**|全体のスタイル（未指定の場合、LocalTextStyleを利用）|

試しにComposeで以下のようなレイアウトを作ると、このような形となります。

<img width="500" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/106030/db3d9c16-08ff-7d20-0ccc-edd3c5e9fbde.png">


```kotlin
@Preview
@Composable
private fun ALLLayout() {
    TestText()
}

@Composable
private fun TestText() {
    Text(
        text = "Hello World!!".repeat(3),
        modifier = Modifier
            .fillMaxWidth()
            .height(50.dp)
            .clickable { Log.d("test", "クリックされた！！！") },
        color = Color.White,
        fontSize = 20.sp,
        letterSpacing = 2.sp,
        textAlign = TextAlign.Center,
        fontStyle = FontStyle.Italic,
        fontWeight = FontWeight.Bold,
        textDecoration = TextDecoration.Underline,
        maxLines = 2,
        softWrap = true
    )
}
```
###■ClickableText
上記のText()でもModifierを使用すればクリックイベントを追加はできましたが
それとは別に `ClickableText` というコンポーネントもあり、こちらではonClickイベントとして直接セットすることができるので用途に応じて使い分けができそうな感じです。
プロパティに関してはText()と同じものが使用できます。

###■buildAnnotatedString
今まで1つのTextView内で特定文字の色やサイズを変える際は「Html.fromHtml」などでHtml化した文字をセットすることが多かったと思います。
これだとxml上での確認ができずに煩わしさがありましたが、ComposeのTextでは `buildAnnotatedString`というものが用意されており、これにより文字連結や特定の文字のときの装飾が簡単に行えるようになりましたので紹介しておきます。 
以下は[ **公式ドキュメント** ](https://developer.android.com/jetpack/compose/text?hl=ja)に上がっているサンプルコードとその結果のUIです。

```kotlin
@Composable
fun MultipleStylesInText() {
    Text(
        buildAnnotatedString {
            withStyle(style = SpanStyle(color = Color.Blue)) {
                append("H")
            }
            append("ello ")

            withStyle(style = SpanStyle(fontWeight = FontWeight.Bold, color = Color.Red)) {
                append("W")
            }
            append("orld")
        }
    )
}
```

<img width="250" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/106030/3928de16-cee1-5ab5-e7ac-9fca38945e82.png">



##Button
Buttonのコンポーネントです、こちらは以前のViewと同じ名称ですね。
elevationやborderなど、MaterialComponentsのButtonと同じような感覚で使うことができます。
以下がButtonのプロパティとなります。

|プロパティ名|設定内容|
|:-------|:-------|
|**onClick**|クリックイベント（Buttonにはデフォルトで付いている）|
|**modifier**|Viewの高さや幅、クリックイベントなどをセット（Modifier.**で指定）|
|**enabled**|ボタンの有効にするかどうか（Boolean）|
|**elevation**|ボタンの影（ButtonDefaults.elevation(defaultElevation = n.dp, pressedElevation = n.dp, disabledElevation = n.dp) で指定）|
|**shape**|ボタンの角丸（RoundedCornerShape(n.dpまたはsize(Float)またはpercent(Int))で指定）|
|**border**|ボタンの枠（BorderStroke(n.dp, color)）で指定可能|
|**colors**|ボタンの色（ButtonDefaults.textButtonColors(backgroundColor(背景), contentColor(文字色), disabledContentColor(無効時のボタン色))）|
|**contentPadding**|ボタン内のpadding値（PaddingValues(n.dp) ※全体、縦横、上下左右を指定可能）|

ButtonはIconやTextと組み合わせて使うこともできます。
Iconについては特に触れませんがImageと近いものとなります。
こちらもサンプルコードとその結果のUIも挙げておきます。

<img width="200" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/106030/d5928552-22be-b585-8cb3-84e288a358bd.png">


```kotlin
@Preview
@Composable
private fun ALLLayout() {
    TestButton()
}

@Composable
private fun TestButton() {
    Button(
        onClick = { Log.d("test", "クリックされた！！！") },
        modifier = Modifier
            .width(120.dp)
            .height(48.dp),
        enabled = true,
        elevation =  ButtonDefaults.elevation(defaultElevation = 5.dp, pressedElevation = 5.dp, disabledElevation = 0.dp),
        shape = RoundedCornerShape(5.dp),
        border = BorderStroke(1.dp, Color.White),
        colors = ButtonDefaults.textButtonColors(
            backgroundColor = Color.Blue,
            contentColor = Color.White,
            disabledContentColor = Color.LightGray
        ),
        contentPadding = PaddingValues(10.dp)
    ) {
        Icon(
            imageVector = Icons.Filled.Favorite,
            contentDescription = null,
            modifier = Modifier.size(ButtonDefaults.IconSize)
        )
        Spacer(Modifier.size(ButtonDefaults.IconSpacing))
        Text(text = "クリック")
    }
}
```


##Image
ImageViewやImageButtonの代わりとなるコンポーネントです！
こちらは第一引数で、セット対象を`ImageBitmap`、`ImageVector`、`Painter`の中から選ぶことができます。

|プロパティ名|設定内容|
|:-------|:-------|
|**※ImageBitmap(どれか1つ)**|Bitmapファイル（ImageBitmap.imageResource(id =R.**.**)で指定）|
|**※ImageVector(どれか1つ)**|Vectorファイル（ImageVector.vectorResource(id = R.**.**)で指定）|
|**※Painter(どれか1つ)**|その他リソースファイル（painterResource(id = R.**.**)で指定）|
|**contentDescription**|Viewの説明（nullでも可）|
|**modifier**|Viewの高さや幅、クリックイベントなどをセット（Modifier.**で指定）|
|**alignment**|Viewの配置向き（Alignment.**で指定）|
|**contentScale**|画像の拡縮をセット（ContentScale.**で指定）|
|**alpha**|画像の透過値、0fで透明・1fで透過なし（Float）|
|**colorFilter**|画像の色とブレンドモード（色の塗り方？）をセット（ColorFilter.tint(color, BlendMode.**)で指定）|

試しに使ってみると、アイコンを表示してみるとこんな感じになります。

<img width="155" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/106030/54992c41-f435-fb22-4763-70d59458668c.png">

```kotlin
@Preview
@Composable
private fun ALLLayout() {
    TestImage()
}

@Composable
private fun TestImage() {
    Image(
        painter = painterResource(id = R.drawable.ic_booking),
        modifier = Modifier
            .width(48.dp)
            .height(48.dp),
        contentDescription = null,
        alignment = Alignment.Center,
        contentScale = ContentScale.Fit,
        alpha = 0.5f,
        colorFilter = ColorFilter.tint(
            Color.Gray,
            BlendMode.SrcIn)
        )
}
```

###■補足：ネットワーク上の画像の読み込みについて
上記の通り、現時点でComposeではリソースを直接指定するパターンしかなく、ネットワークから取得した画像読み込みについて考慮されておりません。
ImageViewなどではPicasso、Glide、Coilなど、ネットワークから画像を取得してセットするライブラリはいくつかあります。
少し前までは [**Accompanist** ](https://google.github.io/accompanist/)というライブラリ群（「Composeにはまだ入っていないが開発者に必要な機能」をサポートしている）の中に存在していたGlide、Coil用のライブラリを使うことが主流だったみたいですが、最近CoilがCompose用の[ **拡張ライブラリ** ](https://coil-kt.github.io/coil/compose/#jetpack-compose)を用意してくれたのでこちらを使うことをおすすめします！！

詳しくはCoilのドキュメントを参照していただきたいのですが、簡単に使い方を書いておきます。
>以下をGradleに追加し、Sync Nowした上でpainterに`rememberImagePainter`で指定する。

```gradle:app/build.gradle
implementation("io.coil-kt:coil-compose:1.4.0")
```

```kotlin
Image(
    painter = rememberImagePainter("https://www.example.com/image.jpg"),
    contentDescription = null,
    modifier = null
)
```


#まとめ
今まではfindViewById()などで参照を追加してからテキストや色などをセットすることが多く、ヒューマンエラーが発生する可能性がたまにありました。
Composeではそれぞれをパーツ単位のコンポーネントとして定義しておくので、そのあたりのミスが減ることは利点かなと個人的に感じました。
あと悪いところではないのですが、問題点をあげるとするのであればComposeはまだ出たばかりなので学習コストが高いこと、現時点でのバージョンですとxmlで出来ていたことを全て表現しきれない可能性があります。
まだまだ日本語の記事も少ない為、今から始める場合は公式ドキュメントを熟読することをお勧めします！（というよりそれしかない笑）

#最後に
この他にもColumnやRowなどのアイテムの配置についてなどいろいろありましたが、今回はコンポーネントのプロパティに限定して書かせていただきました。
Composeに関してはこれからどんどん拡充・修正が行われるはずなので、置いていかれないように今後も少しずつ学習していけたらいいかなと思っております！

明日の記事は @SinceK13さんによる「食べログの内製Pub/Subメッセージング基盤をApache Kafkaにリプレイスした話」です。
お楽しみに！！！