Upgrade to Pro — share decks privately, control downloads, hide ads and more …

[우아콘2021] 배달의민족에 JetpackCompose 저...

강경완
December 15, 2021

[우아콘2021] 배달의민족에 JetpackCompose 적용을 시도해보았다

Jetpack Compse가 1.0 버전으로 공개되었습니다. 배달의민족 프로젝트에 Jetpack Compose를 사용해보고 느낀점을 공유합니다.

발표영상 : https://www.youtube.com/watch?v=BX0rQvYgf1I

강경완

December 15, 2021
Tweet

More Decks by 강경완

Other Decks in Programming

Transcript

  1. ٜযоӝ੹ী - Jetpack Compose بੑ दب೧ࠄ ҃೷ਸ ҕਬ - ੸ਊӝ

    పझ౟ӝ - Jetpack Compose بੑਸ ೧ࠅө..? 
 - যڃ ޙઁо ੓ਸө..?
  2. ݺ۸ഋ೐۽Ӓې߁ val layout = LinearLayout(context) layout.setBackgroundColor(red) layout.removeAllViews() val childTextView =

    TextView(context) childTextView.text = "childTextView" layout.addView(childTextView) Column( modi fi er = Modi fi er .background(red) ) { Text("childText") } ࢶ঱ഋ೐۽Ӓې߁
  3. അ੤ߓ޹জ ӂ੢ࢎ೦ MVVM ١ ױߑೱ ؘ੉ఠ ൒ܴ ইఃఫ୊ ӂ੢ targetSdkVersion

    30 Gradle 7.0 Kotlin Version 1.5.21 minSdkVersion 21 * Using Android Studio Arctic Fox MVP ಁఢ ࢎਊ઺
  4. 5FYU7JFX$PNQPTF binding?.locationTextComposeView?.setContent { LocationText(viewModel.getTitle()) } @Composable fun LocationText(title: LiveData<String>) {

    val text = title.observeAsState() title.value?.let { Text( text = it, fontSize = dpToSp(dp = 14.dp), fontWeight = FontWeight.Bold, color = colorResource(id = R.color.text…), textAlign = TextAlign.Center, over fl ow = TextOver fl ow.Ellipsis, ... ) } }
  5. -BZPVU$PNQPTF @Composable fun LocationLayout(title: LiveData<String>) { Row( modi fi er

    = Modi fi er. fi llMaxSize(), verticalAlignment = Alignment.CenterVertically, ) { LocationText(text = title) Spacer(modi fi er = Modi fi er.size(4.dp)) LocationIconImage() } } @Composable fun LocationIconImage() { Image( painter = painterResource(R.drawable.icon_open), ... ) }
  6. 3FDZDMFS7JFX$PNQPTF abstract class ComposeListAdapterDelegate <T: DisplayModel, VH: ComposeViewHolder<T>> : AbsListItemAdapterDelegate<T,

    DisplayModel, ComposeViewHolder<T>>() { override fun onViewRecycled( holder: RecyclerView.ViewHolder) { (holder as VH)?.let { it.composeView.disposeComposition() } super.onViewRecycled(holder) } } abstract class ComposeViewHolder<T>( val composeView: ComposeView ) : RecyclerView.ViewHolder(composeView) { @Composable abstract fun ViewHolder(input: T) init { composeView.setViewCompositionStrategy( ViewCompositionStrategy.DisposeOnViewTreeLifecycleDe stroyed) } fun bindViewHolder(input: T) { composeView.setContent { ViewHolder(input) } } }
  7. ૑بചݶਸ੹ജ೧ࠁও׮ ௏٘ଵҊ : https://github.com/Moop-App/Moop-Android/ val coroutineScope = rememberCoroutineScope() val savedInstanceState

    = rememberSavedInstanceState() val mapView = rememberMapViewWithLifecycle(savedInstanceState) AndroidView( factory = { mapView.apply { coroutineScope.launch { val naverMap = suspendCoroutine<NaverMap> { continuation -> getMapAsync { continuation.resume(it) } } naverMap.apply { minZoom = Constants.ZOOM_LEVEL_MIN.toDouble() maxZoom = Constants.ZOOM_LEVEL_MAX.toDouble() uiSettings.defaultSetting() } naverMap.addOnCameraIdleListener { onCameraIdle() } naverMap.addOnCameraChangeListener { i, b -> onCameraChange(i, b) } onMapReady(naverMap) } } } )
  8. ૑بചݶਸ੹ജ೧ࠁও׮ ௏٘ଵҊ : https://github.com/Moop-App/Moop-Android/ val coroutineScope = rememberCoroutineScope() val savedInstanceState

    = rememberSavedInstanceState() val mapView = rememberMapViewWithLifecycle(savedInstanceState) AndroidView( factory = { mapView.apply { coroutineScope.launch { val naverMap = suspendCoroutine<NaverMap> { continuation -> getMapAsync { continuation.resume(it) } } naverMap.apply { minZoom = Constants.ZOOM_LEVEL_MIN.toDouble() maxZoom = Constants.ZOOM_LEVEL_MAX.toDouble() uiSettings.defaultSetting() } naverMap.addOnCameraIdleListener { onCameraIdle() } naverMap.addOnCameraChangeListener { i, b -> onCameraChange(i, b) } onMapReady(naverMap) } } } )
  9. ૑بചݶਸ੹ജ೧ࠁও׮ ௏٘ଵҊ : https://github.com/Moop-App/Moop-Android/ val coroutineScope = rememberCoroutineScope() val savedInstanceState

    = rememberSavedInstanceState() val mapView = rememberMapViewWithLifecycle(savedInstanceState) AndroidView( factory = { mapView.apply { coroutineScope.launch { val naverMap = suspendCoroutine<NaverMap> { continuation -> getMapAsync { continuation.resume(it) } } naverMap.apply { minZoom = Constants.ZOOM_LEVEL_MIN.toDouble() maxZoom = Constants.ZOOM_LEVEL_MAX.toDouble() uiSettings.defaultSetting() } naverMap.addOnCameraIdleListener { onCameraIdle() } naverMap.addOnCameraChangeListener { i, b -> onCameraChange(i, b) } onMapReady(naverMap) } } } )
  10. ૑بചݶਸ੹ജ೧ࠁও׮ ௏٘ଵҊ : https://github.com/Moop-App/Moop-Android/ val coroutineScope = rememberCoroutineScope() val savedInstanceState

    = rememberSavedInstanceState() val mapView = rememberMapViewWithLifecycle(savedInstanceState) AndroidView( factory = { mapView.apply { coroutineScope.launch { val naverMap = suspendCoroutine<NaverMap> { continuation -> getMapAsync { continuation.resume(it) } } naverMap.apply { minZoom = Constants.ZOOM_LEVEL_MIN.toDouble() maxZoom = Constants.ZOOM_LEVEL_MAX.toDouble() uiSettings.defaultSetting() } naverMap.addOnCameraIdleListener { onCameraIdle() } naverMap.addOnCameraChangeListener { i, b -> onCameraChange(i, b) } onMapReady(naverMap) } } } )
  11. ૑بചݶਸ੹ജ೧ࠁও׮ ௏٘ଵҊ : https://github.com/Moop-App/Moop-Android/ val coroutineScope = rememberCoroutineScope() val savedInstanceState

    = rememberSavedInstanceState() val mapView = rememberMapViewWithLifecycle(savedInstanceState) AndroidView( factory = { mapView.apply { coroutineScope.launch { val naverMap = suspendCoroutine<NaverMap> { continuation -> getMapAsync { continuation.resume(it) } } naverMap.apply { minZoom = Constants.ZOOM_LEVEL_MIN.toDouble() maxZoom = Constants.ZOOM_LEVEL_MAX.toDouble() uiSettings.defaultSetting() } naverMap.addOnCameraIdleListener { onCameraIdle() } naverMap.addOnCameraChangeListener { i, b -> onCameraChange(i, b) } onMapReady(naverMap) } } } )
  12. ૑بചݶਸ੹ജ೧ࠁও׮ ௏٘ଵҊ : https://github.com/Moop-App/Moop-Android/ val lifecycle = LocalLifecycleOwner.current.lifecycle DisposableEffect(lifecycle, mapView,

    savedInstanceState) { val lifecycleObserver = LifecycleEventObserver { _, event -> when (event) { Lifecycle.Event.ON_CREATE -> mapView.onCreate( savedInstanceState.takeUnless { it.isEmpty }) Lifecycle.Event.ON_START -> mapView.onStart() Lifecycle.Event.ON_RESUME -> mapView.onResume() Lifecycle.Event.ON_PAUSE -> mapView.onPause() Lifecycle.Event.ON_STOP -> mapView.onStop() Lifecycle.Event.ON_DESTROY -> mapView.onDestroy() else -> throw IllegalStateException() } } lifecycle.addObserver(lifecycleObserver) onDispose { mapView.onSaveInstanceState(savedInstanceState) lifecycle.removeObserver(lifecycleObserver) } }
  13. -PUUJF7JFXJO7JFX ӝઓҳઑ class FloatingCartView { fun updateCartCount() { getCartItemCountUseCase?.execute(onNext =

    { newCartCount -> val foodCount = cartCount.count val newCount = newCartCount.count val differenceCount = newCount - foodCount setCartCount(newCount) playAddAnimation(differenceCount) cartCount = newCartCount }) } private fun playAddAnimation(differenceCount: Int) { if ( fl oatingCartImageView.isAnimating) { fl oatingCartImageView.cancelAnimation() } fl oatingCartImageView.setAnimation("cart_ani.json") fl oatingCartImageView.playAnimation() animateCountPlusCount(differenceCount) } }
  14. -PUUJF7JFXJO$PNQPTF @Composable fun FloatingCartViewCompose( getCartItemCountUseCase = GetCartItemCountUseCase(), clickable: () ->

    Unit = {}, ) { Box( modi fi er = Modi fi er ... .clickable { clickable() } ) { ... val eventHandler = remember { mutableStateOf(Lifecycle.Event.ON_ANY) } val lifecycleOwner = rememberUpdatedState(LocalLifecycleOwner.current) DisposableEffect(lifecycleOwner.value) { val lifecycle = lifecycleOwner.value.lifecycle val observer = LifecycleEventObserver { owner, event -> eventHandler.value = event } lifecycle.addObserver(observer) onDispose { lifecycle.removeObserver(observer) } } eventHandler.value.let { AndroidView(factory = {
  15. val eventHandler = remember { mutableStateOf(Lifecycle.Event.ON_ANY) } val lifecycleOwner =

    rememberUpdatedState(LocalLifecycleOwner.current) DisposableEffect(lifecycleOwner.value) { val lifecycle = lifecycleOwner.value.lifecycle val observer = LifecycleEventObserver { owner, event -> eventHandler.value = event } lifecycle.addObserver(observer) onDispose { lifecycle.removeObserver(observer) } } eventHandler.value.let { AndroidView(factory = { val view = LottieAnimationView(it).apply {} return@AndroidView view }, update = { if (event == Lifecycle.Event.ON_RESUME) { getCartItemCountUseCase.execute(onNext = { newCartCount -> ... playAddAnimation(differenceCount, it) }) } }) } } } -PUUJF7JFXJO$PNQPTF
  16. -PUUJF$PNQPTF val composition by rememberLottieComposition( LottieCompositionSpec.Asset("cart_ani.json")) val state = remember

    { CartAniState() } LaunchedEffect(eventHandler.value) { if (eventHandler.value == Lifecycle.Event.ON_RESUME) { state.animatable.animate( composition, iterations = 1, initialProgress = 0f, speed = 1f, continueFromPreviousAnimate = false, ) } } LottieAnimation( composition, progress = state.animatable.progress )
  17. ݻݻইए਍੼ٜ ݻݻ ۄ੉࠳۞ܻח ࢎਊೡ ࣻ হ਺ - ExoPlayer - ੉޷૑

    ۄ੉࠳۞ܻ (Glide ١) - MotionLayout - ViewPager..? যରೖ ੹ࠗ ߄۽ ߸ജೞӝח ൨ٜѷ૑݅..
  18. ݻݻইए਍੼ٜ Resource ҙ۲ ࢎਊೡ ࣻ হח ੼ٜ - Shape Drawable

    ࢎਊ ࠛоמ (xml) - Selector Drawable ࢎਊ ࠛоמ - ColorStateList ࢎਊ ࠛоמ - աੋಁ஖ ੉޷૑ ࢎਊ ࠛоמ যରೖ ੹ࠗ ߄۽ ߸ജೞӝח ൨ٜѷ૑݅..
  19. ݻݻইए਍੼ٜ ѐߊ ࢤ࢑ࢿ਷?? - MVVM ਵ۽ ؀୓ ঱ઁ ৤ѹ тө

    - ۞׬ ழ࠳о ഛप൤ ઓ੤ - Preview ݅ਵ۽ח xml ীࢲ ੘ࢿೞח Ѫࠁ׮ח ࠛಞೞ׮ -> ࢤ࢑ࢿ ੷ ೞ - Build दр, apk ࢎ੉ૉ যରೖ
  20. Ӓېࢲখਵ۽חਃ Ӓېب ੉ઁח ઑӘঀ بੑೡ ٸ - ౱ ղীࢲ ೟णೞݴ

    ۞׬ழ࠳ܳ ӓࠂ - рױೠ ചݶࠗఠ ੸ਊ೧ ࠅ ৘੿ - рѾೞҊ UI ਤ઱੄ ௏٘ח 👍 - ইఃఫ୛ࠗఠ ୌୌ൤ Ҋ޹೧ࠁ੗