$30 off During Our Annual Pro Sale. View Details »

Compose 첫 걸음 || Compose Migration

Ju Hyung Park
September 12, 2023

Compose 첫 걸음 || Compose Migration

DroidKnights 발표자료

Ju Hyung Park

September 12, 2023
Tweet

More Decks by Ju Hyung Park

Other Decks in Programming

Transcript

  1. Compose 첫 걸음 || Compose Migration
    Juhyung Park / NAVERZ

    View Slide

  2. Contents
    ❏ Compose 원리 (Composition, Layout, Drawing, etc)
    ❏ Compose + XML (Compose Migration)
    ❏ Compose + XML에서 BottomSheet 적용기

    View Slide

  3. 들어가기전..

    View Slide

  4. Android 레이아웃 시스템 변천사
    2008 ~ 2010 2013 2014 2015 2016 2017 2018 2019 2020 Future
    findViewById & Casting
    ButterKnife
    Data Binding
    Kotlin Synthetic
    Generic findViewById
    View Binding
    Jetpack
    Compose

    View Slide

  5. Compose 이전에는 보통..
    XML
    view ID
    Source Code
    Activity/Fragment
    Binding
    Generate
    Code
    Bind Inject

    View Slide

  6. Jetpack Compose
    @Composable
    view
    Source Code
    Activity/Fragment
    State
    Variable
    Recompose Update

    View Slide

  7. Jetpack Compose
    @Composable
    view
    Source Code
    Activity/Fragment
    State
    Variable
    Recompose Update

    View Slide

  8. Jetpack Compose
    @Composable
    view
    Source Code
    Activity/Fragment
    State
    Variable
    Recompose Update

    View Slide

  9. View Slide

  10. ViewModel
    private val _suggestedDestinations = MutableStateFlow>(emptyList())
    val suggestedDestinations: StateFlow> =
    _suggestedDestinations.asStateFlow()
    fun updatePeople(people: Int) {
    viewModelScope.launch {
    if (people > MAX_PEOPLE) {
    _suggestedDestinations.value = emptyList()
    } else {
    val newDestinations = withContext(defaultDispatcher) {
    destinationsRepository.destinations
    .shuffled(Random(people * (1..100).shuffled().first()))
    }
    _suggestedDestinations.value = newDestinations
    }
    }
    }
    https://developer.android.com/codelabs/jetpack-compose-advanced-state-side-effects

    View Slide

  11. ViewModel
    private val _suggestedDestinations = MutableStateFlow>(emptyList())
    val suggestedDestinations: StateFlow> =
    _suggestedDestinations.asStateFlow()
    fun updatePeople(people: Int) {
    viewModelScope.launch {
    if (people > MAX_PEOPLE) {
    _suggestedDestinations.value = emptyList()
    } else {
    val newDestinations = withContext(defaultDispatcher) {
    destinationsRepository.destinations
    .shuffled(Random(people * (1..100).shuffled().first()))
    }
    _suggestedDestinations.value = newDestinations
    }
    }
    }
    https://developer.android.com/codelabs/jetpack-compose-advanced-state-side-effects

    View Slide

  12. ViewModel
    private val _suggestedDestinations = MutableStateFlow>(emptyList())
    val suggestedDestinations: StateFlow> =
    _suggestedDestinations.asStateFlow()
    fun updatePeople(people: Int) {
    viewModelScope.launch {
    if (people > MAX_PEOPLE) {
    _suggestedDestinations.value = emptyList()
    } else {
    val newDestinations = withContext(defaultDispatcher) {
    destinationsRepository.destinations
    .shuffled(Random(people * (1..100).shuffled().first()))
    }
    _suggestedDestinations.value = newDestinations
    }
    }
    }
    https://developer.android.com/codelabs/jetpack-compose-advanced-state-side-effects

    View Slide

  13. ViewModel
    private val _suggestedDestinations = MutableStateFlow>(emptyList())
    val suggestedDestinations: StateFlow> =
    _suggestedDestinations.asStateFlow()
    fun updatePeople(people: Int) {
    viewModelScope.launch {
    if (people > MAX_PEOPLE) {
    _suggestedDestinations.value = emptyList()
    } else {
    val newDestinations = withContext(defaultDispatcher) {
    destinationsRepository.destinations
    .shuffled(Random(people * (1..100).shuffled().first()))
    }
    _suggestedDestinations.value = newDestinations
    }
    }
    }
    https://developer.android.com/codelabs/jetpack-compose-advanced-state-side-effects

    View Slide

  14. Activity / Fragment
    @OptIn(ExperimentalMaterialApi::class)
    @Composable
    fun CraneHomeContent(viewModel: MainViewModel = viewModel()) {
    val suggestedDestinations by
    viewModel.suggestedDestinations.collectAsStateWithLifecycle()
    BackdropScaffold(
    modifier = modifier,
    ...
    frontLayerContent = {
    when (tabSelected) {
    CraneScreen.Fly -> {
    ExploreSection(
    title = "Explore Flights by Destination",
    exploreList = suggestedDestinations,
    onItemClicked = onExploreItemClicked
    )
    ...
    https://developer.android.com/codelabs/jetpack-compose-advanced-state-side-effects

    View Slide

  15. Activity / Fragment
    @OptIn(ExperimentalMaterialApi::class)
    @Composable
    fun CraneHomeContent(viewModel: MainViewModel = viewModel()) {
    val suggestedDestinations by
    viewModel.suggestedDestinations.collectAsStateWithLifecycle()
    BackdropScaffold(
    modifier = modifier,
    ...
    frontLayerContent = {
    when (tabSelected) {
    CraneScreen.Fly -> {
    ExploreSection(
    title = "Explore Flights by Destination",
    exploreList = suggestedDestinations,
    onItemClicked = onExploreItemClicked
    )
    ...
    https://developer.android.com/codelabs/jetpack-compose-advanced-state-side-effects

    View Slide

  16. Activity / Fragment
    @OptIn(ExperimentalMaterialApi::class)
    @Composable
    fun CraneHomeContent(viewModel: MainViewModel = viewModel()) {
    val suggestedDestinations by
    viewModel.suggestedDestinations.collectAsStateWithLifecycle()
    BackdropScaffold(
    modifier = modifier,
    ...
    frontLayerContent = {
    when (tabSelected) {
    CraneScreen.Fly -> {
    ExploreSection(
    title = "Explore Flights by Destination",
    exploreList = suggestedDestinations,
    onItemClicked = onExploreItemClicked
    )
    ...
    https://developer.android.com/codelabs/jetpack-compose-advanced-state-side-effects

    View Slide

  17. Activity / Fragment
    @OptIn(ExperimentalMaterialApi::class)
    @Composable
    fun CraneHomeContent(viewModel: MainViewModel = viewModel()) {
    val suggestedDestinations by
    viewModel.suggestedDestinations.collectAsStateWithLifecycle()
    BackdropScaffold(
    modifier = modifier,
    ...
    frontLayerContent = {
    when (tabSelected) {
    CraneScreen.Fly -> {
    ExploreSection(
    title = "Explore Flights by Destination",
    exploreList = suggestedDestinations,
    onItemClicked = onExploreItemClicked
    )
    ...
    https://developer.android.com/codelabs/jetpack-compose-advanced-state-side-effects

    View Slide

  18. 컴포즈를 선택해야하는 이유는..?
    ❏ Kotlin 코드로 UI 작성
    ❏ Adapter, ViewHolder패턴 안녕!
    ❏ 애니메이션 구현을 더 쉽게!
    ❏ 구성요소의 재사용 용이

    View Slide

  19. Compose 원리

    View Slide

  20. 원리를 꼭 알아야 할까..? 🤔

    View Slide

  21. Compose, 일단 작성해봅시다
    ❏ 요구사항
    ❏ 안드로이드 버전별 정보 리스트
    ❏ “선택하기" 버튼 하단 고정
    ❏ 리스트 중 한 개만 선택가능
    ❏ 선택 시 하이라이트 기능
    ❏ 선택 시 “선택하기" 버튼 활성화
    ❏ 같은 아이템 클릭 시 하이라이트 & 버튼 초기화

    View Slide

  22. @Composable

    View Slide

  23. @Composable
    fun AndroidVersionInfoItem(
    info: AndroidVersionInfo
    ) {
    Row {
    ...
    Image(painterResource(id = info.versionImageRes))
    Column(...) {
    Text(text = "Version Name : ${info.versionName}")
    Text(text = "Version Number : ${info.versionNumber}, API ...")
    }
    }
    }

    View Slide

  24. @Composable
    fun AndroidVersionInfoItem(
    info: AndroidVersionInfo
    ) {
    Row {
    ...
    Image(painterResource(id = info.versionImageRes))
    Column(...) {
    Text(text = "Version Name : ${info.versionName}")
    Text(text = "Version Number : ${info.versionNumber}, API ...")
    }
    }
    }

    View Slide

  25. suspend fun foo() { ... }
    val foo = suspend { ... }
    fun foo(param: suspend () -> Unit) { ... }

    View Slide

  26. suspend fun foo() { ... }
    val foo = suspend { ... }
    fun foo(param: suspend () -> Unit) { ... }

    View Slide

  27. @MustBeDocumented
    @Retention(AnnotationRetention.BINARY)
    @Target(
    // val foo = @Composable { ... }
    AnnotationTarget.FUNCTION,
    // foo: @Composable () -> Unit
    AnnotationTarget.TYPE,
    // foo: (@Composable () -> Unit) -> Unit
    AnnotationTarget.TYPE_PARAMETER,
    // composable property getters and setters
    // val foo: Int @Composable get() { ... }
    // var bar: Int
    // @Composable get() { ... }
    AnnotationTarget.PROPERTY_GETTER
    )
    annotation class Composable

    View Slide

  28. suspend fun foo() { ... }
    val foo = suspend { ... }
    fun foo(param: suspend () -> Unit) { ... }

    View Slide

  29. @Composable fun foo() { ... }
    val foo = @Composable { ... }
    fun foo(param: @Composable () -> Unit) { ... }

    View Slide

  30. // Kotlin suspend function
    suspend fun foo(): String { ... }
    // Continuation.kt
    public interface Continuation {
    public val context: CoroutineContext
    public fun resumeWith(result: Result)
    }
    Decompile

    View Slide

  31. // Kotlin suspend function
    suspend fun foo(): String { ... }
    // Continuation.kt
    public interface Continuation {
    public val context: CoroutineContext
    public fun resumeWith(result: Result)
    }
    Decompile
    @Composable fun foo() { /* Context ??? */ }

    View Slide

  32. // StringResources.android.kt
    @Composable
    @ReadOnlyComposable
    fun stringResource(@StringRes id: Int): String {
    val resources = resources()
    return resources.getString(id)
    }
    @Composable
    @ReadOnlyComposable
    internal fun resources(): Resources {
    LocalConfiguration.current
    return LocalContext.current.resources
    }

    View Slide

  33. // StringResources.android.kt
    @Composable
    @ReadOnlyComposable
    fun stringResource(@StringRes id: Int): String {
    val resources = resources()
    return resources.getString(id)
    }
    @Composable
    @ReadOnlyComposable
    internal fun resources(): Resources {
    LocalConfiguration.current
    return LocalContext.current.resources
    }

    View Slide

  34. public fun ComponentActivity.setContent(
    parent: CompositionContext? = null,
    content: @Composable () -> Unit
    ) {
    val existingComposeView = window.decorView
    .findViewById(android.R.id.content)
    .getChildAt(0) as? ComposeView
    if (existingComposeView != null) with(existingComposeView) {
    ...
    setContent(content)
    } else ComposeView(this).apply {
    ...
    setContent(content)
    ...
    }
    }

    View Slide

  35. public fun ComponentActivity.setContent(
    parent: CompositionContext? = null,
    content: @Composable () -> Unit
    ) {
    val existingComposeView = window.decorView
    .findViewById(android.R.id.content)
    .getChildAt(0) as? ComposeView
    if (existingComposeView != null) with(existingComposeView) {
    ...
    setContent(content)
    } else ComposeView(this).apply {
    ...
    setContent(content)
    ...
    }
    }

    View Slide

  36. internal fun AbstractComposeView.setContent(
    parent: CompositionContext,
    content: @Composable () -> Unit
    ): Composition {
    GlobalSnapshotManager.ensureStarted()
    val composeView =
    if (childCount > 0) {
    getChildAt(0) as? AndroidComposeView
    } else {
    removeAllViews(); null
    } ?: AndroidComposeView(context).also { addView(it.view, DefaultLayoutParams) }
    return doSetContent(composeView, parent, content)
    }

    View Slide

  37. internal fun AbstractComposeView.setContent(
    parent: CompositionContext,
    content: @Composable () -> Unit
    ): Composition {
    GlobalSnapshotManager.ensureStarted()
    val composeView =
    if (childCount > 0) {
    getChildAt(0) as? AndroidComposeView
    } else {
    removeAllViews(); null
    } ?: AndroidComposeView(context).also { addView(it.view, DefaultLayoutParams) }
    return doSetContent(composeView, parent, content)
    }

    View Slide

  38. @Composable
    @OptIn(ExperimentalComposeUiApi::class)
    internal fun ProvideAndroidCompositionLocals(
    owner: AndroidComposeView,
    content: @Composable () -> Unit
    ) {
    val view = owner
    val context = view.context
    ...
    CompositionLocalProvider(
    LocalConfiguration provides configuration,
    LocalContext provides context,
    LocalLifecycleOwner provides viewTreeOwners.lifecycleOwner,
    LocalSavedStateRegistryOwner provides viewTreeOwners.savedStateRegistryOwner,
    LocalSaveableStateRegistry provides saveableStateRegistry,
    LocalView provides owner.view,
    LocalImageVectorCache provides imageVectorCache
    ) { ... }
    }

    View Slide

  39. @Composable
    @OptIn(ExperimentalComposeUiApi::class)
    internal fun ProvideAndroidCompositionLocals(
    owner: AndroidComposeView,
    content: @Composable () -> Unit
    ) {
    val view = owner
    val context = view.context
    ...
    CompositionLocalProvider(
    LocalConfiguration provides configuration,
    LocalContext provides context,
    LocalLifecycleOwner provides viewTreeOwners.lifecycleOwner,
    LocalSavedStateRegistryOwner provides viewTreeOwners.savedStateRegistryOwner,
    LocalSaveableStateRegistry provides saveableStateRegistry,
    LocalView provides owner.view,
    LocalImageVectorCache provides imageVectorCache
    ) { ... }
    }

    View Slide

  40. @Composable
    @OptIn(ExperimentalComposeUiApi::class)
    internal fun ProvideAndroidCompositionLocals(
    owner: AndroidComposeView,
    content: @Composable () -> Unit
    ) {
    val view = owner
    val context = view.context
    ...
    CompositionLocalProvider(
    LocalConfiguration provides configuration,
    LocalContext provides context,
    LocalLifecycleOwner provides viewTreeOwners.lifecycleOwner,
    LocalSavedStateRegistryOwner provides viewTreeOwners.savedStateRegistryOwner,
    LocalSaveableStateRegistry provides saveableStateRegistry,
    LocalView provides owner.view,
    LocalImageVectorCache provides imageVectorCache
    ) { ... }
    }

    View Slide

  41. Composition
    Layout
    Draw

    View Slide

  42. Jetpack Compose Layouts
    Composition Layout Drawing

    View Slide

  43. @Composable
    fun AndroidVersionInfoItem(...) {
    Row {
    Image(...)
    Column(...) {
    Text(...)
    Text(...)
    }
    }
    }

    View Slide

  44. @Composable
    fun AndroidVersionInfoItem(...) {
    Row {
    Image(...)
    Column(...) {
    Text(...)
    Text(...)
    }
    }
    }
    // UI Tree 변환작업

    View Slide

  45. @Composable
    fun AndroidVersionInfoItem(...) {
    Row {
    Image(...)
    Column(...) {
    Text(...)
    Text(...)
    }
    }
    }

    View Slide

  46. @Composable
    fun AndroidVersionInfoItem(...) {
    Row {
    Image(...)
    Column(...) {
    Text(...)
    Text(...)
    }
    }
    }
    AndroidVersionInfoItem
    Row
    Image
    Column
    Text
    Text

    View Slide

  47. @Composable
    fun AndroidVersionInfoItem(...) {
    Row {
    Image(...)
    Column(...) {
    Text(...)
    Text(...)
    }
    }
    }
    AndroidVersionInfoItem
    Row
    Image
    Column
    Text
    Text
    AndroidVersionInfoItem
    Row
    Layout
    Image
    Layout
    Column
    Layout
    Text
    BasicText
    CoreText
    Layout
    Text
    BasicText
    CoreText
    Layout

    View Slide

  48. Layout / Drawing
    Composition
    Layout
    Drawing
    Measure Place

    View Slide

  49. Layout / Drawing
    Composition
    Layout
    Drawing
    Measure Place
    Measure Children

    View Slide

  50. Layout / Drawing
    Composition
    Layout
    Drawing
    Measure Place
    Measure Children
    Decide Own Size

    View Slide

  51. Layout / Drawing
    Composition
    Layout
    Drawing
    Measure Place
    Measure Children
    Decide Own Size
    Place Children

    View Slide

  52. AndroidVersionInfoItem
    Row
    Image
    Column
    Text
    Text
    1 measure
    2 measure 3 size
    1
    2

    View Slide

  53. AndroidVersionInfoItem
    Row
    Image
    Column
    Text
    Text
    1 measure
    2 measure
    4 measure
    5 measure
    3 size
    6 size
    7 measure 8 size
    1
    2 4
    5
    7

    View Slide

  54. AndroidVersionInfoItem
    Row
    Image
    Column
    Text
    Text
    1 measure
    2 measure
    4 measure
    5 measure
    7 measure
    10 size
    3 size
    9 size
    6 size
    8 size
    1
    2 4
    5
    7

    View Slide

  55. AndroidVersionInfoItem
    Row
    Image
    Column
    Text
    Text
    1 measure
    2 measure
    4 measure
    5 measure
    7 measure
    10 size
    3 size
    9 size
    6 size
    8 size
    place
    place
    place
    place
    place
    1
    2 4
    5
    7

    View Slide

  56. Modifier

    View Slide

  57. Modifier
    ❏ 컴포저블 크기, 레이아웃, 동작, 및 모양 변경
    ❏ 정보 추가 (ex. 접근성라벨)
    ❏ 사용자 입력 처리
    ❏ Clickable, Scrollable, Draggable, 확대/축소등 상호작용

    View Slide

  58. Modifier
    ❏ 컴포저블 크기, 레이아웃, 동작, 및 모양 변경
    ❏ 정보 추가 (ex. 접근성라벨)
    ❏ 사용자 입력 처리
    ❏ Clickable, Scrollable, Draggable, 확대/축소등 상호작용
    ❏ Tree의 리프노드인 Layout Composable에 Modifier 적용

    View Slide

  59. @Composable
    fun BottomButtons(isEnabled: Boolean = false) {
    Row(
    horizontalArrangement = Arrangement.Center,
    modifier = Modifier
    .fillMaxWidth()
    .background(Color.White)
    .padding(5.dp)
    ) {
    Button(
    onClick = { /*TODO*/ },
    contentPadding = PaddingValues(12.dp),
    modifier = Modifier.size(130.dp, 45.dp),
    enabled = isEnabled
    ) {
    Text("선택하기")
    }
    }
    }

    View Slide

  60. Box (
    modifier = Modifier
    .fillMaxSize()
    .wrapContentSize()
    .size(50.dp)
    .background(Color.Blue)
    )
    https://youtu.be/zMKMwh9gZuI?t=738
    measure
    w:0-200, h:0-300

    View Slide

  61. Box (
    modifier = Modifier
    .fillMaxSize()
    .wrapContentSize()
    .size(50.dp)
    .background(Color.Blue)
    )
    https://youtu.be/zMKMwh9gZuI?t=738
    measure
    w:0-200, h:0-300
    measure
    w:200, h:300

    View Slide

  62. Box (
    modifier = Modifier
    .fillMaxSize()
    .wrapContentSize()
    .size(50.dp)
    .background(Color.Blue)
    )
    https://youtu.be/zMKMwh9gZuI?t=738
    measure
    w:0-200, h:0-300
    measure
    w:200, h:300
    measure
    w:0-200, h:0-300

    View Slide

  63. Box (
    modifier = Modifier
    .fillMaxSize()
    .wrapContentSize()
    .size(50.dp)
    .background(Color.Blue)
    )
    https://youtu.be/zMKMwh9gZuI?t=738
    measure
    w:0-200, h:0-300
    measure
    w:200, h:300
    measure
    w:0-200, h:0-300
    w:50, h:50

    View Slide

  64. Box (
    modifier = Modifier
    .fillMaxSize()
    .wrapContentSize()
    .size(50.dp)
    .background(Color.Blue)
    )
    https://youtu.be/zMKMwh9gZuI?t=738
    measure
    w:0-200, h:0-300
    measure
    w:200, h:300
    measure
    w:0-200, h:0-300
    w:50, h:50 50*50 place
    200*300 place
    50*50 place

    View Slide

  65. Compose Migration

    View Slide

  66. 컴포즈를 도입해볼까요..?
    언제 도입할까요..?
    Stable버전이 나왔나요..?

    View Slide

  67. Compose Migration

    View Slide

  68. 드디어 컴포즈 첫 적용!
    ❏ 당장은 서비스화면 도입은 부담
    ❏ 앱 내 개발자메뉴..?
    ❏ 기존 XML 레이아웃 시스템에서
    Compose를 어떻게 올릴 수 있을까?

    View Slide

  69. 마이그레이션으로 얻을 수 있는것들 (희망편 😍)
    ❏ 컴포즈로 복잡한 View와 로직을 단순화 시킬 수 있었다
    ❏ 컴포즈 라이브러리를 추가하면서 오히려 앱 사이즈가 줄었다?!
    ❏ 커스텀 UI 작성하는것이 매우 편해졌다
    ❏ 컴포즈가 제공해주는 프리뷰기능은 기존 XML 프리뷰보다 훌륭하다
    ❏ 코틀린으로 작성이 가능, 상대적으로 낮은 진입장벽

    View Slide

  70. 마이그레이션으로 얻을 수 있는것들 (절망편 😱)
    ❏ 너무 새로워서 아직 몇몇 UI가 XML View 기능이 구현되지 않았었다
    (지금은 많은 부분이 해소)
    ❏ Debug모드 시 컴포즈 퍼포먼스 차이가 커서 Debug의 어려움이 있음
    ❏ 사용한 UI컴포넌트의 뜻하지 않은 버그 (핫픽스 지름길)
    ❏ 컴포즈 버전을 올리기 위해서는 매칭되는 코틀린 버전 변경 필요
    ❏ 눈으로 일일이 확인할 수 없는 ReComposition 카운트는 오히려 코드를 다 작성한
    후 자꾸 신경쓰이게 됨
    ❏ XML + Compose 구조이다보니 가끔 고려하지 못한 리팩토링으로 개발공수가
    늘어남

    View Slide

  71. Why Chose Jetpack Compose
    for Their Android App UI

    View Slide

  72. //Fragment, Navigation 구성요소 사용중인 경우
    class NewFeatureFragment : Fragment() {
    override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
    ): View {
    return ComposeView(requireContext()).apply {
    setViewCompositionStrategy(ViewCompositionStrategy.
    DisposeOnViewTreeLifecycleDestroyed)
    setContent {
    NewFeatureScreen()
    }
    }
    }
    }
    https://developer.android.com/jetpack/compose/interop/migration-strategy?hl=ko

    View Slide

  73. // ViewBinding 사용, 기존 View Layout 사용 시



    android:id="@+id/compose_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

    View Slide

  74. // ViewBinding 사용, 기존 View Layout 사용 시
    class ExampleFragment : Fragment() {
    ...
    override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
    ): View {
    _binding = FragmentExampleBinding.inflate(inflater, container, false)
    val view = binding.root
    binding.composeView.apply {
    setViewCompositionStrategy(ViewCompositionStrategy
    .DisposeOnViewTreeLifecycleDestroyed)
    setContent { ... }
    }
    return view
    }
    ...
    }

    View Slide

  75. // Compose UI 내 Android View 계층구조 사용 시
    // (Compose에서 아직 사용하지 못하는 View 사용 시)
    @Composable
    fun CustomView() {
    AndroidView(
    modifier = ...
    factory = { context ->
    MyView(context)
    },
    update = { view -> ... }
    )
    }

    View Slide

  76. Compose + XML
    BottomSheet 적용기

    View Slide

  77. XML View Layout
    Compose BottomSheet?

    View Slide

  78. 제약사항
    XML View





    ❏ 당장 기능화면을 ComposeView 로
    변경할 수 없음

    View Slide

  79. 제약사항
    XML View





    ❏ 당장 기능화면을 ComposeView 로
    변경할 수 없음
    ❏ ModalBottomSheetLayout을
    사용할 수 없음

    View Slide

  80. 제약사항
    ❏ 당장 기능화면을 ComposeView 로
    변경할 수 없음
    ❏ ModalBottomSheetLayout을
    사용할 수 없음
    ❏ 다른 화면에서도 사용할 수 있는
    BottomSheet로, 한 화면에 한정X
    (대부분 XML View)
    BottomSheetDialogFragment

    View Slide

  81. 제약사항
    ❏ 당장 기능화면을 ComposeView 로
    변경할 수 없음
    ❏ ModalBottomSheetLayout을
    사용할 수 없음
    ❏ 다른 화면에서도 사용할 수 있는
    BottomSheet로, 한 화면에 한정X
    (대부분 XML View)
    ❏ 어떻게든 컴포즈를 BottomSheet에
    적용하고 싶음
    BottomSheetDialogFragment

    View Slide

  82. 이슈 (BottomSheetDialogFragment + Compose)
    ❏ BottomSheet 상단 Anchor 이슈
    ❏ Compose ModalBottomSheet와 BottomSheetDialogFragment 혼용 이슈

    View Slide

  83. Column {
    ImageView(...)
    Row {
    Text(text = "Title")
    }
    ...
    }
    Title

    View Slide

  84. Column {
    ImageView(...)
    Row {
    Text(text = "Title")
    }
    LazyColumn { ... }
    }
    BottomSheetDialogFragment
    LazyColumn
    Title
    Expandable

    View Slide

  85. Column {
    ImageView(...)
    Row {
    Text(text = "Title")
    }
    LazyColumn { ... }
    }
    BottomSheetDialogFragment
    LazyColumn
    Title
    Expandable

    View Slide

  86. Column {
    ImageView(...)
    Row {
    Text(text = "Title")
    }
    LazyColumn { ... }
    }
    BottomSheetDialogFragment
    LazyColumn
    Title
    Expandable

    View Slide

  87. Column {
    LazyColumn {
    item { ImageView(...) }
    item { Text(text = "Title") }
    }
    LazyColumn { ... }
    }
    BottomSheetDialogFragment
    LazyColumn
    Title Expandable

    View Slide

  88. ModalBottomSheet/BottomSheetDialogFragment 혼용
    ModalBottomSheet
    BottomSheetDialogFragment
    Case A
    1. ModalBottomSheet
    2. BottomSheetDialogFragment.show()

    View Slide

  89. ModalBottomSheet/BottomSheetDialogFragment 혼용
    ModalBottomSheet
    BottomSheetDialogFragment
    Case A
    1. ModalBottomSheet
    2. BottomSheetDialogFragment.show()

    View Slide

  90. ModalBottomSheet/BottomSheetDialogFragment 혼용
    Case B
    1. BottomSheetDialogFragment.show()
    2. ModalBottomSheet
    BottomSheetDialogFragment
    ModalBottomSheet

    View Slide

  91. ModalBottomSheet/BottomSheetDialogFragment 혼용
    Case B
    1. BottomSheetDialogFragment.show()
    2. ModalBottomSheet
    BottomSheetDialogFragment
    ModalBottomSheet

    View Slide

  92. ❏ ModalBottomSheet는 ComposeView의 Window
    즉, 디바이스 Window하위 ComposeView 내 노출하기 때문?
    ❏ BottomSheetDialogFragment는 addFragment 형태로 노출되며
    ComposeView보다 상위 Window에서 노출하기 때문?
    ModalBottomSheet/BottomSheetDialogFragment 혼용

    View Slide

  93. Wrap Up
    ❏ Compose, 지난 레이아웃의 다른 형태로 제공
    ❏ 트렌드의 변화 (명령형 UI -> 선언형 UI)
    ❏ 러닝커브가 낮은 편
    ❏ 생산성 효율 Up
    ❏ 한 번만 써본 사람은 없을정도
    ❏ 원리는 몰라도 좋지만 도움은 된다
    ❏ Compose 첫 걸음은, Migration 부터

    View Slide