20211129のAndroidに関する記事は3件です。

launchWhen〇〇〇は止めてrepeatOnLifecycleを使おう!

はじめに 皆さんはlaunchWhenCreated, launchWhenStarted, launchWhenResumedを使ってますか?coroutinesでライフサイクルを意識して書く場合、必須とでも言うべきメソッドです。 ですが、Flowでこれらを利用するのは避けたほうが良いです。なぜか? launchWhen〇〇〇ではFlowが完全には停止せず、リソースが消費される可能性があるからです。 Lifecycle が STOPPED の場合、前者の API はコルーチンをキャンセルするのではなく停止するため、アップストリーム フローはバックグラウンドでアクティブのままにされ、新しいアイテムが出力される可能性やリソースが浪費される可能性があります。 launchWhen〇〇〇は将来的に廃止予定 また、launchWhen〇〇〇は将来的に下記リンクにある通りでThis API will be removed in a future release. とのこと。 今後はrepeatOnLifecycleを使おう では何を使うべきか。androidx.lifecycle:lifecycle-runtime-ktx:2.4.0-alpha01 からrepeatOnLifecycle APIが利用できるようになっており、これを使うように推奨されています。2.4.0は2021/10/27にStableになりました。 書き方は非常に簡単。 launchWhen〇〇〇でこう書いていたのを... launchWhenStarted override fun onCreate(savedInstanceState: Bundle?) { lifecycleScope.launchWhenStarted { latestNewsViewModel.uiState.collect { uiState -> ... } } } ↓ こう書くだけ! repeatOnLifecycle override fun onCreate(savedInstanceState: Bundle?) { lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { latestNewsViewModel.uiState.collect { uiState -> ... } } } } 上記の例だと、repeatOnLifecycleはLifecycle が STOPPEDの際にJobをキャンセルしてくれて、STARTEDになったら再度Jobをlaunchしてくれるのでリソースの消費を防げます。 実は、他に細かい動作の違いとかあるので書こうと思ったんですが、moriさんのブログがとてもきれいにまとまっていたためこちらにリンク置いときます。非常に勉強になります。 https://at-sushi.work/blog/35/ 参考
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Jetpack Compose】Composeをオーバーレイ表示する

要素の上に半透明で表示するオーバーレイ表示をしてみました。 サイズが固定の場合はとても簡単なのですが、サイズが不定の場合はちょっとコツ(?)が必要でした。 オーバーレイ表示はこんな感じのものです。 固定サイズの場合 固定サイズの場合は、そのComposeの上に fillMaxSize() で半透明のComposeを重ねるだけです。 例えば、こんなカード表示のComposeの場合です。 横幅は引数のModifierで指定できますが、縦幅は80dpで固定指定になっています。 @Composable fun Card(modifier: Modifier = Modifier) { Card(modifier = modifier.height(80.dp), shape = RoundedCornerShape(8.dp), elevation = 4.dp) { Row(modifier = Modifier.fillMaxWidth()) { Image( painter = rememberImagePainter("https://placehold.jp/3d4070/ffffff/80x80.png?text=Image"), contentDescription = null, modifier = Modifier.size(80.dp) ) Column(modifier = Modifier .fillMaxSize() .padding(8.dp)) { Text("Title", fontSize = 22.sp) Text("description", fontSize = 16.sp) } } } } こういうサイズが中身の要素に関わらず固定の場合は、 fillMaxSize() で上乗せしてあげればすんなり実装できます。 @Composable fun Card(modifier: Modifier = Modifier, isOverlay: Boolean = false) { Card(modifier = modifier.height(80.dp), shape = RoundedCornerShape(8.dp), elevation = 4.dp) { Row(modifier = Modifier.fillMaxWidth()) { Image( painter = rememberImagePainter("https://placehold.jp/3d4070/ffffff/80x80.png?text=Image"), contentDescription = null, modifier = Modifier.size(80.dp) ) Column(modifier = Modifier .fillMaxSize() .padding(8.dp)) { Text("Title", fontSize = 22.sp) Text("description", fontSize = 16.sp) } } // オーバーレイ if (isOverlay) { Box(modifier = Modifier .fillMaxSize() .background(Color(0x88FFFFFF))) } } } 半透明は白に対してアルファ値を下げてあげれば透過します。白透過でなくて黒透過にしたければ、適当に 0x80000000 とか指定すればできます。 サイズが不定の場合 今度はサイズが不定の場合です。こういう感じで、テキストによってサイズが高さが可変するケースです。 (Imageあたりのデザインがあれなのは目をつぶります) @Composable fun Card(modifier: Modifier = Modifier, description: String) { Card( modifier = modifier.wrapContentHeight(), shape = RoundedCornerShape(8.dp), elevation = 4.dp ) { Row(modifier = Modifier.fillMaxWidth()) { Image( painter = rememberImagePainter("https://placehold.jp/3d4070/ffffff/80x80.png?text=Image"), contentDescription = null, modifier = Modifier.size(80.dp) ) Column( modifier = Modifier .wrapContentHeight() .padding(8.dp) ) { Text("Title", fontSize = 22.sp) Text(description, fontSize = 16.sp, softWrap = true) } } } } この場合、先程と同様に fillMaxSize() で上乗せしてみると上手くいかないことがわかります。 こんな感じで、子のBoxのサイズが親のCardのサイズを広げてしまうような挙動になってしまっています。 Overlay表示するBoxのサイズは、親のサイズと同じサイズになってほしいところです。汎用的なModifierには残念ながらそういうものはないのですが、BoxScope限定で使えるModifierに matchParentSize() を使えば実現できます。 Box { // Boxの子要素限定で使えるModifier // Box サイズに影響を与えずに子を親のBoxと同じサイズにする Box(modifier = Modifier.matchParentSize()) {} } しれっと下記のドキュメントにも記載されてます。 https://developer.android.com/jetpack/compose/layouts/basics?hl=ja#type-safety このModifierを使うにはBoxScopeである必要があるので、Cardの直下にBoxを置いてあげます。そしてOverlay表示しているBoxのmodifierに matchParentSize() を追加してあげます。 @Composable fun Card(modifier: Modifier = Modifier, description: String, isOverlay: Boolean = false) { Card( modifier = modifier.wrapContentHeight(), shape = RoundedCornerShape(8.dp), elevation = 4.dp ) { Box(Modifier.fillMaxWidth()) { Row(modifier = Modifier.fillMaxWidth()) { Image( painter = rememberImagePainter("https://placehold.jp/3d4070/ffffff/80x80.png?text=Image"), contentDescription = null, modifier = Modifier.size(80.dp) ) Column( modifier = Modifier .wrapContentHeight() .padding(8.dp) ) { Text("Title", fontSize = 22.sp) Text(description, fontSize = 16.sp, softWrap = true) } } if (isOverlay) { Box( modifier = Modifier // 親のサイズいっぱいに広げる .matchParentSize() .background(Color(0x88FFFFFF)) ) } } } } いい感じにできました?
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Jetpack Compose】ColumnとRowで知っていると便利なこと3選

はじめに 本記事は with Advent Calendar 2021 1日目の記事です。 こんにちは with でAndroid エンジニアをしている石田です。withでは Jetpack Compose を段階的にプロダクトに導入しています。 本記事では、Compose の基本レイアウトである ColumnとRow で個人的によく使う 便利だなと感じたユースケースを3つ 紹介します。 Weightを設定しつつ可能ならば詰めてレイアウトする Column/Row のスコープでは Modifier.weight で Weight を指定することができ、指定した値の比率で子の高さもしくは幅が決定されます。通常は内容に関係なくその比率の分だけ領域が確保されるますが、可能ならば詰めてレイアウトしたい場合はどうすればいいでしょうか。 実は Modifier.weight() は 第2引数に fill: Boolean 取ることができて 、 fill=false を指定することで必要なだけ高さもしくは幅を取るという挙動にすることができます。これは ConstraintLayout で言うところの app:layout_constraintHeight_default="wrap" に相当します。 サンプルコード 以下の要件を満たすサンプルを示します。 名前の幅は伸縮し、年齢は名前の横に詰めてレイアウトする 名前が長い場合は末尾を省略する 年齢は常に省略されない @Composable fun WeightFillFalseExample() { Column { UserItem("なおと", 26) UserItem("なおと@Qiita Advent Calendar 2021参加中", 26) } } @Composable private fun UserItem( name: String, age: Int, ) { Row( verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(16.dp) ) { Text( text = name, modifier = Modifier.weight(weight = 1f, fill = false), // < -- ココ! style = MaterialTheme.typography.bodyMedium, maxLines = 1, overflow = TextOverflow.Ellipsis, ) Spacer(modifier = Modifier.width(16.dp)) Text( text = "${age}歳", style = MaterialTheme.typography.bodySmall, ) } } 子の間に均等にスペースを空けたい ColumnやRowで子をスペースを空けながら等間隔に並べたい場合は Arrangement.spacedBy を使うと便利です。これは ConstraintLayout の Flow で言うところのapp:flow_horizontalGap / app:flow_verticalGap に相当します。 Modifier.padding や Spacer を使う方法に比べて コンポーザブルのツリー構造をシンプルに保つことができ、コードの見通しも良くなると思います。 サンプルコード サンプルを示します。 LazyRowに対して Arrangement.spacedBy を使ってますが、 Row でも同様です。Columnの場合は horizontalArrangement の部分が verticalArrangement になります。 @Composable fun ArrangementSpacedbyExample() { LazyRow( contentPadding = PaddingValues(all = 16.dp), horizontalArrangement = Arrangement.spacedBy(16.dp), // < -- ココ! ) { item { Tag(text = "焼き肉?") } item { Tag(text = "お寿司?") } item { Tag(text = "ピザ?") } item { Tag(text = "お弁当?") } } } @Composable private fun Tag(text: String) { Box( modifier = Modifier .clip(RoundedCornerShape(100)) .background(MaterialTheme.colorScheme.secondaryContainer) .padding(horizontal = 12.dp, vertical = 4.dp), ) { Text( text = text, style = MaterialTheme.typography.bodySmall, color = MaterialTheme.colorScheme.onSecondaryContainer, ) } } 子を右から左に配置したい Rowは通常 子を左から右に配置していきますが、右から左に配置することも可能です。配置の方向を変えるには LocalLayoutDirection の値を設定します。 サンプルコード サンプルを示します。 LocalLayoutDirection の値は CompositionLocalProvider を使って指定します。 @Composable fun LayoutDirectionExample() { Column { ProvideLayoutDirection(layoutDirection = LayoutDirection.Ltr) { Seasons() } ProvideLayoutDirection(layoutDirection = LayoutDirection.Rtl) { Seasons() } } } @Composable private fun ProvideLayoutDirection( layoutDirection: LayoutDirection, content: @Composable () -> Unit, ) { CompositionLocalProvider( LocalLayoutDirection provides layoutDirection, // < -- ココ! content = content, ) } @Composable private fun Seasons() { Row( modifier = Modifier.padding(16.dp), horizontalArrangement = Arrangement.spacedBy(16.dp), ) { Text(text = "春") Text(text = "夏") Text(text = "秋") Text(text = "冬") } } まとめ ColumnとRow で個人的によく使う便利だなと感じたユースケースを3つ紹介しました。 動作可能なサンプルコードをGitHubで公開している のでこちらも併せてご参照ください。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む