Jetpack Compose PlayGround

Posted by kwmt27 on Sun, Mar 28, 2021

はじめに

※こちらは更新予定です。

確認環境は下記です。

  • compose_version: 1.0.0-beta02
  • kotlin version: 1.4.31
  • Android Studio version: Android Studio Arctic Fox | 2020.3.1 Canary 12

確認したコードはGitHubにおいてます。

https://github.com/kwmt/JetpackComposePlayGround

縦方向(Vertical)のリスト表示するには

縦方向(Vertical)のリストを表示するには、LazyColumn を使います。

import androidx.compose.foundation.lazy.LazyColumn
@Composable
fun SampleList() {
    LazyColumn {
        items(5) { index ->
            // 各要素で表示したいUIを書く。
            ListItem(index)     
        }
    }
}

多くのアイテムやサイズのわからないリストを表示する必要がある場合、Columnのようなレイアウトを使用すると、全てのアイテムが表示されるかどうかに関わらずレイアウトされるので、パフォーマンスに影響があります。 LazyColumnLazyRowは表示されるアイテムのみをレイアウトするコンポーネントになります。(RecyclerViewと同じ概念)

ついでに、リストの各要素はComposableListItemは自作のComposable関数で、リストアイテムのUIを定義しています。

@Composable
fun ListItem(index: Int = 0) {
    Column {
        Box(
            modifier = Modifier
                .height(200.dp)
                .background(Color.Blue)
                .fillMaxWidth()
        ) {
            Text(
                modifier = Modifier.align(Alignment.Center),
                text = "index: $index",
                style = TextStyle(color = Color.White)
            )
        }
        Divider(modifier = Modifier.height(8.dp))
    }
}

LazyColumnの引数には、content: LazyListScope.() -> Unit をとるようになっていて、LazyListScopeは、単一のアイテムを追加するitemメソッドと、複数のアイテムを追加するitemsメソッドと、これはまだExperimentalですが、スティッキーヘッダーを追加するstickyHeaderメソッドがあります。

横方向(Horizontal)のリスト表示するには

LazyRowを使う。

@Composable
fun SampleHorizontalList() {
    LazyRow{
        items(5) { index ->
            ListHorizontalItem(index)
        }
    }
}
@Composable
fun ListHorizontalItem(index: Int = 0) {
    Column {
        Box(
            modifier = Modifier
                .width(200.dp)
                .background(Color.Blue)
                .height(200.dp)
        ) {
            Text(
                modifier = Modifier.align(Alignment.Center),
                text = "index: $index",
                style = TextStyle(color = Color.White)
            )
        }
        Divider(modifier = Modifier.height(8.dp))
    }
}

スティッキーヘッダー(StickyHeader)を使うには

stickyHeaderメソッドを使うようです。(2021/03/28現在Experimental 1.0.0-beta02時点)

@OptIn(ExperimentalFoundationApi::class)
@Composable
fun StickyListSample() {
    LazyColumn {
        grouped.forEach { (initial, contactsForInitial) ->
            stickyHeader {
                Text(
                    modifier = Modifier
                        .fillMaxWidth()
                        .background(Color.Gray)
                        .padding(horizontal = 8.dp),
                    text = initial.toString()
                )
            }

            items(contactsForInitial) { contact ->
                ListItem2(contact.firstName)
            }
        }
    }
}

下記はFakeデータです。

private data class Contact(val firstName: String)

private val contacts =
    listOf<Contact>(
        Contact("AAA"),
        Contact("ABB"),
        Contact("Bbb"),
        Contact("Bbb2"),
        Contact("Bbb3"),
        Contact("Bbb3"),
        Contact("Bbb4"),
        Contact("Ccc"),
        Contact("Ccc2"),
        Contact("Ccc3"),
        Contact("Ccc4"),
        Contact("Ccc5"),
    )
private val grouped = contacts.groupBy { it.firstName[0] }

適当なアイテムです。

@Composable
fun ListItem2(text: String = "") {
    Column {
        Box(
            modifier = Modifier
                .height(200.dp)
                .background(Color.Blue)
                .fillMaxWidth()
        ) {
            Text(
                modifier = Modifier.align(Alignment.Center),
                text = "$text",
                style = TextStyle(color = Color.White)
            )
        }
        Divider()
    }
}

View(Composable)を重ねて表示するには

Boxを使います。

たとえば、上の画像のようにグレーの四角(縦横100dp)とブルーの四角(縦横50dp)を重ねて表示するには、次のようなコードになります。

@Composable
fun BoxSample() {
    Box {
        // グレーの四角(縦横100dp)
        Spacer(
            modifier = Modifier
                .background(Color.Gray)
                .size(100.dp)
        )

        // ブルーの四角(縦横50dp)
        Box(
            modifier = Modifier
                .size(50.dp)
                .offset(x = 10.dp, y = 10.dp)
                .background(Color.Blue),
            contentAlignment = Alignment.Center
        ) {
            Text(
                "test",
                modifier = Modifier
                    .fillMaxWidth()
                    .align(Alignment.Center)
                    .background(Color.Red),
                textAlign = TextAlign.Center,
                style = TextStyle(color = Color.White),
            )
        }
    }
}

※グレーの四角とブルーの四角の順番を入れ替えると、ブルーの四角が見えなくなってしまいます。これまでのXMLと同様ですね。

戻るボタン(Upボタン)を表示するには

@Composable
fun Up(
    upPress: () -> Unit,
) {
    IconButton(
        onClick = upPress,
        modifier = Modifier
            .padding(horizontal = 16.dp, vertical = 16.dp)
            .size(36.dp)
            .background(
                color = Color.White.copy(alpha = 0.32f),
                shape = CircleShape
            )
    ) {
        Icon(
            imageVector = Icons.Rounded.ArrowBack,
            contentDescription = "back"
        )
    }
}

val Shadow4 = Color(0xff7057f5)

@Preview
@Composable
fun PreviewUp() {
    Box(modifier = Modifier.background(Shadow4)) {
        Up {}
    }
}

画像をWeb上からダウンロードして表示するには(URL)

Android Viewで使われていたGlideやCoilをCompose用にラップしているaccompanistというライブラリを使うのがデファクトスタンダードになりそうな気がしますので、それを使います。

implementation "com.google.accompanist:accompanist-coil:0.7.0"
import com.google.accompanist.coil.CoilImage

@Composable
fun NetworkImageSample(
) {
    CoilImage(
        data = "https://picsum.photos/300/300",
        contentDescription = null,
    )
}

これを実行すると、次のようになります。

インターネットにアクセスするので、パーミッションの追加を忘れずに。

<uses-permission android:name="android.permission.INTERNET" />

こちらのサンプルのようなものを作っておくと便利だと感じました。 https://github.com/android/compose-samples/blob/main/Owl/app/src/main/java/com/example/owl/ui/utils/NetworkImage.kt#L44

画像を円形にするには

@Composable
fun CircleImageSample() {
    Surface(
        shape = CircleShape,
        modifier = Modifier
            .fillMaxWidth()
            .aspectRatio(1f)
    ) {
        CoilImage(
            data = "https://picsum.photos/300/300",
            contentDescription = null,
        )
    }
}

画面遷移するには

Jetpack ComposeにもNavigation Componentをサポートしています。

implementation "androidx.navigation:navigation-compose:1.0.0-alpha09"

NavControllerはNavigation Componentの中心的なAPIです。 このAPIはステートフルであり、アプリ内の画面を構成するコンポーザブルのバックスタックと各画面の状態を追跡します。

NavControllerを作成するには、rememberNavController()メソッドを使用します。

val navController = rememberNavController()

NavControllerは1つのNavHostコンポーザブルに関連付ける必要があります。

NavHost(navController, startDestination = "animals") {
    composable("animals") { Animals(...) }
    composable("animal/{animalId}") { AnimalDetail(...) }
}

これは動物一覧を最初の画面(startDestination)として、リストの要素をタップしたら、動物詳細画面(“animal_detail”)に遷移することを想定しています。

このようにNavHostを定義したら、コンポーザブル間を移動するには、navigate()メソッドを使用します。

fun Animals(navController: NavController) {
    //省略
    val animal: Animal  = // 省略
    Button(onClick = { navController.navigate("animal/{animal.id}")}) {
        // 省略
    }
}

data class Animal(val id: String)

これだと、各画面がNavControllerを知ることになってしまうので、画面はクリックしたら何を渡すのかわたさないのかに関心があって、どこに遷移するかはNavGraph側で管理したいので、それを解決するのが、こちらのMainActionsが参考になるかと思います。

前の画面にもどるには

NavController#navigateUpメソッドを使用します。

https://github.com/kwmt/android-dev-challenge-compose/blob/main/app/src/main/java/com/example/androiddevchallenge/NavGraph.kt#L44

ローカルの画像を表示するには

TODO

角丸にするには

TODO



comments powered by Disqus