基本的にはただのメモです。
特に驚いたところを太字にしておきます。
コードレビューしていたりなどで、あんまり聞けていない部分もあるのでお許しください
Jetpack Compose で Material Design 3
Design Token = デザインの値に名前をつけたもの。
自分の頭の中のイメージをコードにするとこんな感じです。
interface DesignToken
class SystemToken(
// ここのContextはDarkThemeかどうかなどという意味のContext
val refs: Map<Context, ReferenceToken>
): DesignToken
class ReferenceToken(val color: Int): DesignToken
その文字列の例はmd.sys.color.on-secondary
。これを分解すると以下のようになる。
md.sys.color.on-secondary
↑ デザインシステム名
md.sys.color.on-secondary
↑ Design Tokenの種類。これは `sys` なので、システムトークン
md.sys.color.on-secondary
↑ トークンの役割を伝えるための名前(colorとon-secondary)
(Component Tokenは今のところ使われていない)
主にSystem TokenとReference Tokenが使われる。
System Tokenに対して複数のReference Tokenがありえる。
https://m3.material.io/foundations/design-tokens/how-to-read-tokens
System Tokenのほうが抽象的なもので、ダークモードなどのコンテキストによってReference Tokenが変わる。
Primary Key Colorで指定した色からTonal paletteが生成されて、そこからPrimaryのColor Rolesが作られる。
つまり、Primary Key Colorで指定した色が直接アプリの色として使われるわけではない。
https://speakerdeck.com/yanzm/material3-with-jetpack-compose?slide=38 より
Migrationの心得はM2と同じ見た目にしようとしない。
→ 同じにするのは大変
自分で色をいじらない。色の調和などが崩れる
Migration方法
- すべてやる。 大変だけどimport先の間違いが起こらない。 ← 小さいアプリでおすすめ
- 画面ごと。アプリ内で2つM2 M3両方の実装が必要
- コンポーネントごとにM3 ← 大きいアプリでおすすめ
コンポーネント毎のマイグレーション時はM3のMaterialThemeで M2のMaterialThemeでラップする
ただ、M2を外すときに少し色が変わったりする可能性がある。あとはコンポーネントごとに移行していく。
Material3Theme {
Material2Theme {
content()
}
}
詳しくは: https://speakerdeck.com/yanzm/material3-with-jetpack-compose?slide=84
Material3ではContentAlphaはなくなり、alphaではなく色で直接使う。localContentColorを指定するようになった。
disableは?コンテンツの色を変えた色を指定することで行われている。
checkboxがsecondaryがprimaryになったり使われる色が変わった。
Jetpack Composeの状態管理とAPIコール - React Hooksに習う、Apollo + Jetpack Compose
オーバフェッチ サーバーから不要な値も受け取ってしまう
アンダーフェッチ 1サーバーから情報が取れないのでいくつもAPIを叩く
Apollo Clientにはキャッシュ機構があり、Graph構造のままキャッシュされる。キャッシュ管理を勝手にやってくれる。
Jetpack Composeでは現状ちょっとめんどくさいが、いい感じに自分でComposable関数を作るとラップできる。
Compose for Desktopで始めるAndroid開発効率化ツールの作成
Adamというadbクライアントを作るためのライブラリがある。
Pluginを入れるとCompose desktopでもPreviewが見れる。
Androidのモダンな技術選択にあわせて自動テストもアップデートしよう
Flowをテストするときは、launchしてcollectしてから、値を変更したりするが、そのlaunchのときには先にcollectを走らせるためにUnconfinedTestDispatcherを使う。
イメージ
launch(UnconfinedTestDispatcher) {
flow.collect(...)
}
flow.emit(...)
setMain()でUnconfinedTestDispatcherを使うとMain.immediateと近い動きにすることができる。 (つまり、基本的にはsetMain()ではUnconfinedTestDispatcherを使っておくのが良さそう)
ComposeのStateの変更をテストするにはSnapshot.registerGlobalWriteObserverを使ってやる。
Done -> Loading -> DoneみたいなときにdelayをRepositoryのFake実装に入れてあげてadvanceUntilIdleを使ってあげるとLoadingの中間状態のときのUiModelなどをテストで確認することができる。(こちらdelay()を入れたりなどを常にやっているのか気になって聞いたんですが、後で聞いたら必要なところだけやっているそう)
How to Test Your Compose UI
テストで使うsemantics treeについて詳しく確認できます。
図がめちゃくちゃわかりやすいのでスライド見てみてください。
setContent {
Row {
Text("1")
Text("2")
}
}
semantics treeはこうなる
Root
- Text = '[1]'
- Text = '[1]'
https://speakerdeck.com/stewemetal/how-to-test-your-compose-ui-droidkaigi-2022-tokyo?slide=28 より
setContent {
Row(Modifier.semantics { contentDescription = "list" }) {
Text("1")
Text("2")
}
}
semantics treeはこうなる
Root
- ContentDescription = 'list'
- Text = '[1]'
- Text = '[1]'
スライドの中で上記のような色んなパターンが見られます。mergeなど興味深かったです。
semanticsを使うとComposable関数のコンポーネントに情報を追加できる。
semanticsを使わないで例えばBoxなどを使うと、そのBoxはテスト上で無いのと同じ動きになる。
mergeDecentants = trueにするとText = '[100 ml, 2022-10]'みたいに子がマージされた形になる。
Buttonとかはデフォルトでマージされる。
ButtonにclearAndSetSemantics { text = AnnotatedString("xxxx") }みたいにすると子の代わりにxxxxが使われる。
テストでuseUnmergedTree=trueを使うとそれを解除したりできる。
2022年の動画再生アプリの作り方
Media3はExoからの置き換えは基本的にはExoのパッケージを置き換えるだけでかんたん。スクリプトも用意されている。
画面回転を超えてPlayerを生き残らせるにはServiceを使う。
MediaControllerはPlayerのインターフェースを継承しており、サービスにあるPlayerをあたかも画面内にあるかのように扱うことができる。
サービスにExoPlayerを持ってActivityにPlayerViewを持って、MediaControllerでつながるようになる。
-
Service
- ExoPlayer
- MediaSession
-
Activity
- サービスのクラスを指定して、SessionTokenを作成す=し、そこからMediaControllerを取得する
- このMediaControllerには、MediaItemをセットできる。MediaItemのRequestMetadataをつけて動画のUriを指定する。
- サービスのクラスを指定して、SessionTokenを作成す=し、そこからMediaControllerを取得する
addOnPictureInPictureModeChangedListener(pipListener)。 (最近追加された気がする)。
Molecule: Using Compose for presentation logic
Compose. Not Compose UI
MV Whatever
Moleculeで何ができるか
2017。
RxJava。
The State of Managing State with RxJava。
そして、Kotlin Coroutinesになって、Singleなどはsuspendに置き換わった。
2019 Asynchronous Data Streams with Kotlin Flow。
RxJava → Coroutines
CoroutinesでAPIはかなりシンプルになった
onStart{}でいくつものAPIが使わなくて良くなるなど。
検索でたくさん検索しないように、CoroutinesだったらtransformLatestを使って300ms delayを使う。スロットル。Rxだとちょっと複雑になる。
suspend + Flowは便利。
だが、オペレーターにより複雑になりえる。
ComposeをUiModelに使ったらどうなるだろう?
ComposeでPresentationを使ったときにComposeで2つのFlowをどうcombineする?
両方collectAsStateする。
なぜMoleculeか?
UIのComposeとpresentation-logicを分離できる。presentation-logicをJUnitでテストできる。
指定するRecompositionClockは2種類ある。(この違いよく分かっていなかったので助かった)
- ContextClock
- フレーム単位なので、フレーム内で2回状態変えたりしても1回だけUiModelなどが変わる
- テストで時間を変更するようにする必要がある
- Immediate
- 状態変わったらすぐに実行される。
- テストで時間を変更する必要がない
turbineでテストする (ターバイン)
WhileSubscribedについてどう思うというのを質問させていただいた。(Broken Englishで) サーバーでも使えるようにもう少しジェネリックに作っているので、対応していない。
Android アプリの内と外をつなぐ UI
androidx.core-splashscreenを使う場合。
setKeepVisibleCondition()を使うと、普通は最初のフレームを描画したタイミングでスプラッシュ画面を消すのをそのタイミングを調整できる。
OnBackInvokedDispatcher
Android 13の新しいAPI。
アプリが戻るジェスチャーを捕捉するためのAPI。
捕捉 = 開いているドロワーを閉じたりとか。
結論: onBackPressedDispatcherに移行する。
戻るボタンの場合は指について戻ることができる。今は三角がでてそれで戻っている。
〜12 これまでは戻るイベントが起こってから必要かアプリに聞く。
onBackPressed()
13〜 これからはアプリが次の戻るイベントを使うよって先にいって、それで終了する。
OnBackInvokedDispatcher
AndroidXのOnBackPressedDispatcherでは勝手にAndroid 13のAPIを使ってくれる
ComposeのBackHandlerでも対応される。
まとめ
今年も学びがたくさんありました。