일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- 쿠링
- boj
- Python
- 프로그래머스
- Codeforces
- pandas
- 백준
- architecture
- android
- Coroutines
- relay
- textfield
- Compose
- livedata
- ProGuard
- 암호학
- Coroutine
- AWS
- Kotlin
- Hilt
- GitHub
- MyVoca
- 코루틴
- MiTweet
- androidStudio
- 코드포스
- TEST
- Rxjava
- Gradle
- activity
- Today
- Total
이동식 저장소
Compose에서 view 사용하기 본문
Compose는 기존 view 시스템과의 상호 운용을 위해 ``AndroidView`` composable을 제공한다. Compose 네이티브 컴포넌트가 없는 WebView 등을 사용할 때 활용할 수 있다.
내부적으로는 composition 트리 안에 view 노드를 직접 만드는 방식으로 구현되어 있다.
오버로딩 1
@Composable
@UiComposable
fun <T : View> AndroidView(
factory: (Context) -> T,
modifier: Modifier = Modifier,
update: (T) -> Unit = NoOpUpdate
)
``factory``는 view를 만드는 람다이다. ``factory`` 블럭은 composition 단계에서 view를 만들기 위해 단 한 번만 실행되며, 항상 UI 스레드에서 실행된다. 따라서 ``factory`` 블럭 안에서 view를 초기화하거나 property를 지정하는 작업을 수행해도 된다.
공식 문서에서는 ``remember``로 view를 감싸지 말고, ``factory`` 블럭 안에서 모든 걸 해결하라고 권장하고 있다.
Note: Prefer to construct a View in the AndroidView factory lambda instead of using remember to hold a View reference outside of AndroidView.
``update`` 블럭은 recomposition이 발생할 때 UI 스레드에서 실행되며, view의 속성을 업데이트할 때 사용할 수 있다. View가 ``factory``에 의해 초기화된 직후에 한번 실행되며, 이후에도 recomposition 때마다 실행될 수 있다.
``AndroidView``는 레이아웃 경계를 넘어가는 UI를 clip하지 않는다. 경계 바깥으로 넘어가는 UI를 자르고 싶다면, ``View.setClipToOutline`` 함수를 호출해야 한다.
WebView(context).apply {
clipToOutline = true
}
``AndroidView``는 오버로딩이 2개 있다. 이 오버로딩은 view가 재활용되지 않을 때 사용해야 한다. ``LazyRow``나 ``LazyColumn`` 등 컴포넌트를 재사용할 수 있는 container 안에서 사용되더라도, 내부 view는 항상 다시 만들어진다. 즉 ``AndroidView`` composable 자체는 재활용되지만, 내부의 view는 재활용되지 않는 것.
``AndroidView``가 재사용될 수 있는 경우에는 아래에서 소개할 다른 오버로딩을 활용해야 한다. View를 만드는 작업은 매우 비싸기 때문에, 아래의 오버로딩을 사용하는 것을 권장한다.
오버로딩 2
@Composable
@UiComposable
fun <T : View> AndroidView(
factory: (Context) -> T,
modifier: Modifier = Modifier,
onReset: ((T) -> Unit)? = null,
onRelease: (T) -> Unit = NoOpUpdate,
update: (T) -> Unit = NoOpUpdate
): Unit
``onReset``과 ``onRelease`` 람다가 눈에 띈다.
``onReset`` 람다가 null이 아니라면, composable을 재사용할 수 있는 container 안에서 view가 재사용될 수 있다. 위에서 말했던 ``LazyRow``에서 ``AndroidView``를 재사용하는 경우에서, ``AndroidView``와 내부 view가 모두 재사용될 수 있는 것이다.
``onReset``에서는 view를 초기화하는 로직을 실행하면 된다. 다른 블럭과 마찬가지로 UI 스레드에서 실행된다.
물론 상황에 따라 reset된 view가 즉시 재사용되지 않을 수도 있다. 대기 중이던 view가 재사용된다면 ``update`` 블럭이 실행되며, 재사용되지 않고 삭제된다면 대기 상태에서 ``onReset``이 호출된 후 view가 삭제된다.
View가 composition hierarchy에 붙어 있는지 확인하고 싶다면, ``View.addOnAttachStateChangeListener``를 등록할 수 있다. View의 생명주기가 궁금하다면 ``findViewTreeLifecycleOwner`` 함수를 실행하면 된다. 이 함수는 Compose의 ``LocalLifecycleOwner``를 반환한다.
View가 composition에서 완전히 제거되는 경우에는 ``onRelease``가 UI 스레드에서 실행된다. ``onRelease``가 return한 후에는 view가 절대로 재사용되지 않는다. 나중에 view가 다시 필요한 경우에도 새 view를 생성하지, release된 view를 재사용하지는 않는다.
View Binding?
View binding이 필요하다면 ``AndroidViewBinding`` composable을 사용할 수 있다.
@Composable
fun AndroidViewBindingExample() {
AndroidViewBinding(ExampleLayoutBinding::inflate) {
exampleView.setBackgroundColor(Color.GRAY)
}
}
Example: WebView
Compose에 없는 WebView를 사용해 보자. 먼저 다음과 같이 ``WebView``를 생성하는 ``createWebView`` 함수를 정의한다.
private fun createWebView(context: Context, onSetProgress: (Int) -> Unit) = WebView(context).apply {
layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
webChromeClient = object : WebChromeClient() {
override fun onProgressChanged(view: WebView, newProgress: Int) {
onSetProgress(newProgress)
super.onProgressChanged(view, newProgress)
}
}
settings.apply {
builtInZoomControls = false
domStorageEnabled = true
javaScriptEnabled = true
loadWithOverviewMode = true
blockNetworkLoads = false
setSupportZoom(false)
}
}
이제 ``AndroidView``에서 ``createWebView``를 호출하자.
var progress by remember { mutableIntStateOf(0) }
AndroidView(
factory = { context ->
createWebView(context) { progress = it }
},
update = {
it.loadUrl(url)
},
)
공식 문서에서 언급한 대로, view를 remember하지 않고 ``factory`` 안에서 만들었다.
앱을 실행해 보니 웹뷰가 잘 보인다. 성공!
참고문헌
'Primary > Compose' 카테고리의 다른 글
derivedStateOf의 진짜 의미 (1) | 2024.07.16 |
---|---|
viewModel()과 hiltViewModel()의 차이 (0) | 2024.06.21 |
Compose 성능을 개선하기 위한 Best Practices (0) | 2024.05.13 |
Compose Strong Skip (0) | 2024.05.05 |
Modifier로 블러 효과 구현하기 (0) | 2024.05.01 |