본 글은 개인적으로 Jetpack AndroidX Compose의 스터디한 내용을 정리하는 아카이브용입니다.
지극히 개인적인
의견입니다.
테스트 전제 조건
실험하는 소스 : https://github.com/Pluu/WebToon/compare/develop-compose
IncompatibleClassChangeError: Found class org.jetbrains.kotlin.ir.declarations.IrClass, but interface was expected
JVM / IR: “IncompatibleClassChangeError: Found class org.jetbrains.kotlin.ir.declarations.IrClass, but interface was expected” with Compose
https://youtrack.jetbrains.com/issue/KT-43350
해결법 : Kotlin plugin 1.4.10 사용
Merged
<!-- drawable/check_circle.xml -->
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="oval">
<solid android:color="#CC222222" />
</shape>
</item>
<!-- vector Image -->
<item android:drawable="@drawable/ic_check_white_24" />
</layer-list>
Image(asset = imageResource(id = R.drawable.check_circle))
java.lang.NullPointerException: BitmapFactory.decodeResource(res, resId) must not be null
at androidx.compose.ui.graphics.AndroidImageAssetKt.imageFromResource(AndroidImageAsset.kt:37)
at androidx.compose.ui.res.ImageResourcesKt.imageResource(ImageResources.kt:44)
at com.pluu.webtoon.episode.ui.EpisodeItemUiKt$EpisodeItemUiOverlayUi$1.invoke(EpisodeItemUi.kt:105)
Image(asset = vectorResource(id = R.drawable.check_circle))
java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:602)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
Caused by: java.lang.reflect.InvocationTargetException
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
Caused by: org.xmlpull.v1.XmlPullParserException: Binary XML file line #2<VectorGraphic> tag requires viewportWidth > 0
at androidx.compose.ui.graphics.vector.compat.XmlVectorParserKt.createVectorImageBuilder(XmlVectorParser.kt:168)
at androidx.compose.ui.res.VectorResourcesKt.loadVectorResource(VectorResources.kt:97)
at androidx.compose.ui.res.VectorResourcesKt.vectorResource(VectorResources.kt:49)
at com.pluu.webtoon.episode.ui.EpisodeItemUiKt$EpisodeItemUiOverlayUi$1.invoke(EpisodeItemUi.kt:105)
at com.pluu.webtoon.episode.ui.EpisodeItemUiKt$EpisodeItemUiOverlayUi$1.invoke(Unknown Source:13)
Modifier.drawBehind
사용Image(
asset = vectorResource(id = R.drawable.ic_check_white_24),
modifier = modifier.drawBehind {
drawCircle(color = /** ... **/ )
}
)
// Draw into a Canvas behind the modified content.
fun Modifier.drawBehind(onDraw: DrawScope.() -> Unit): Modifier
Modifier 기능을 통해서 Content의 뒷부분에 원하는 형태를 Canvas로 그릴 수 있다.
ViewModel로부터 Item이 추가되는 형태에서 무한 스크롤 처리 대응하는 것이 나을지 고민.
전체 리스트 관리를 ContentUi
를 호출하는 곳에서 하는 것이 편할지 고민. 데이터 관리가 한쪽으로 집중되는 형태가 되므로 remember 처리를 적절한 곳에서 하는 것이 관리하기 편할 것으로 생각됨.
@Composable
fun ContentUi(
list: List<Item>
) {
val rememberItems = remember { mutableStateListOf<Item>() }
onCommit(list) {
rememberItems.addAll(list)
}
LazyColumn(items = item, ...) {
...
}
}
Issues : [Jetsnack] Layer is redrawn for LayoutNode in state NeedsRelayout | https://github.com/android/compose-samples/issues/236#issue-724049464
왠지 모를 수정 PR
1500671: Fix LayoutNode not marking children as not placed | https://android-review.googlesource.com/c/platform/frameworks/support/+/1500671
기존 API : https://cs.android.com/androidx/platform/frameworks/support/+/androidx-master-dev:compose/material/material/src/commonMain/kotlin/androidx/compose/material/ProgressIndicator.kt
불편함 점
해결 방법
currentRotationAngleOffset
가 변경되었을 때 기존 API에 전달할 Color 값 변경하도록 대응결과 화면
@Composable
fun xxxx(vm : ViewModel) {
val livedata_test by viewModel.liveDataTest.observeAsState(...)
val flow_test by viewModel.flowTest.collectAsState(...)
}
remember
가 존재하므로 1번만 소비하는 Event같은 패턴은 사용하기 어려울 수 있다.@Suppress("NOTHING_TO_INLINE")
@Composable
inline fun <T : R, R> Flow<T>.collectAsState(
initial: R,
context: CoroutineContext = EmptyCoroutineContext
): State<R> = produceState(initial, this, context) {
if (context == EmptyCoroutineContext) {
collect { value = it }
} else withContext(context) {
collect { value = it }
}
}
@Composable
fun <T> produceState(
initialValue: T,
subject1: Any?,
subject2: Any?,
@BuilderInference producer: suspend ProduceStateScope<T>.() -> Unit
): State<T> {
val result = remember { mutableStateOf(initialValue) }
LaunchedEffect(subject1, subject2) {
ProduceStateScopeImpl(result, coroutineContext).producer()
}
return result
}
https://github.com/androidx/androidx/blob/androidx-master-dev/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/FlowAdapter.kt
https://github.com/androidx/androidx/blob/androidx-master-dev/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/ProduceState.kt
@Composable
fun <R, T : R> LiveData<T>.observeAsState(initial: R): State<R> {
val lifecycleOwner = LifecycleOwnerAmbient.current
val state = remember { mutableStateOf(initial) }
onCommit(this, lifecycleOwner) {
val observer = Observer<T> { state.value = it }
observe(lifecycleOwner, observer)
onDispose { removeObserver(observer) }
}
return state
}
https://github.com/androidx/androidx/blob/androidx-master-dev/compose/runtime/runtime-livedata/src/main/java/androidx/compose/runtime/livedata/LiveDataAdapter.kt
https://github.com/Gurupreet/ComposeCookBook
Home | Search & Detail |
---|---|
https://github.com/Gurupreet/ComposeSpotifyDesktop
comments powered by Disqus
Subscribe to this blog via RSS.