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

NAVER Map in Jetpack Compose

Sungyong An
November 19, 2022

NAVER Map in Jetpack Compose

11/19 (토) DevFest 2022 - GDG Songdo, Incheon & GDSC Inha, TUKorea 행사에서 발표한 자료입니다.
https://festa.io/events/2758

아래 오픈소스 라이브러리에 대한 내용입니다.
https://github.com/fornewid/naver-map-compose

Sungyong An

November 19, 2022
Tweet

More Decks by Sungyong An

Other Decks in Programming

Transcript

  1. Songdo, Incheon
    NAVER Map

    in Jetpack Compose
    Sungyong An

    Android GDE, NAVER WEBTOON

    View Slide

  2. Sungyong An

    NAVER WEBTOON

    Android GDE

    @fornewid
    Link: h
    tt
    ps://github.com/fornewid

    View Slide

  3. Sungyong An

    NAVER WEBTOON

    Android GDE

    @fornewid
    Link: h
    tt
    ps://github.com/fornewid
    Slide Template
    ठۄ੉٘ ೞױী ݂௼о ੓णפ׮.

    View Slide

  4. Code Template
    View Compose
    ਋ஏ ࢚ױী যڃ ௏٘ੋ૑ ই੉௑ਸ ಴द೤פ׮.
    Compose ௏٘݅ ઝஏী ֣࢝ ߄ܳ ಴द೤פ׮.

    View Slide

  5. Link: h
    tt
    ps://developersonair.withgoogle.com/events/composecamp_22kr
    Jetpack Compose ೟ण੉ ೙ਃೠ ࠙਷ Compose Camp ೯ࢎী ଵৈ೧ࠁࣁਃ!

    View Slide

  6. Link: h
    tt
    ps://developer.android.com/develop/ui/views/layout/declaring-layout





    android:layout_height="match_parent"


    android:orientation="vertical" >




    android:layout_width="wrap_content"


    android:layout_height="wrap_content"


    android:text="Hello, I am a TextView" />




    android:layout_width="wrap_content"


    android:layout_height="wrap_content"


    android:text="Hello, I am a Button" />



    View System
    അসীࢲח View Systemਵ۽ ѐߊೞח Ѫ੉ ੊ࣼೡ Ѫ эणפ׮.

    ୊਺ উ٘۽੉٘ ѐߊਸ ੽ೡ ٸب ࠁా Layout XMLࠗఠ द੘ೞભ.

    View Slide

  7. Link: h
    tt
    ps://developer.android.com/jetpack/compose
    Jetpack Compose (Modern
    Android
    Development
    )
    ਃ્ীח Jetpack Composeо ݆੉ ࣗѐغҊ ੓ҳਃ.

    ࢜۽਍ ࣗध ੺߈਷ Composeੌ ੿ب۽ ҙबਸ ߉Ҋ ੓णפ׮.

    View Slide

  8. Link: h
    tt
    ps://developer.android.com/jetpack/compose/interop/migration-strategy
    Migrate from View to Compose
    🤔
    Ӓ۞׮ࠁפ ݆਷ ٜ࠙੉ Viewܳ Compose۽ ੹ജೞҊ ੓Ѣա,

    ੹ജਸ Ҋ۰ೞҊ ੓ਸѢۄ ࢤп೤פ׮.

    View Slide

  9. Toy Project
    Link: h
    tt
    ps://github.com/Moop-App/Moop-Android
    য়ט਷ അসীࢲ ੸ਊೠ ղਊ਷ ইפҊ,

    ష੉ ೐۽ં౟ী ؀ೠ ղਊੑפ׮.

    View Slide

  10. Link: h
    tt
    ps://github.com/navermaps/android-map-sdk
    Official SDK

    based on View
    🤔
    ֎੉ߡ૑ب SDKח View Systemਵ۽ ҳഅغয ੓णפ׮.

    Ӓېࢲ ੉Ѧ ஹನૉীࢲ যڌѱ ॶ ࣻ ੓ਸ૑ Ҋ޹೮ભ.

    View Slide

  11. How to use in Jetpack Compose?
    -(1) ݽٚ ௏٘ܳ Compose۽ ׮द ੘ࢿೠ׮

    -(2) ೨ब ௏٘ܳ ܻ࠙ೞৈ Composeীࢲ ੤ഝਊೠ׮

    -(3) ࢚ഐ ਍ਊࢿ APIܳ ࢎਊೞৈ ӝઓ ௏٘ܳ ېೝೠ׮
    ݢ੷ ݽٚ ௏٘ܳ Compose۽ ৮੹൤ ࢜۽ ҳഅೞח ߑߨ੉ ੓णפ׮.

    ׮਺ਵ۽ ೨ब ௏٘ܳ ܻ࠙ೞৈ, ੌࠗ࠙ ࠗ࠙݅ Compose۽ ੘ࢿೞח ߑߨ੉ ੓णפ׮.

    ݃૑݄ਵ۽ ࢚ഐ਍ਊࢿ APIܳ ੉ਊೞৈ, ӝઓ ௏٘ܳ ېೝೞח ߑߨب ੓णפ׮.

    View Slide

  12. How to use in Jetpack Compose?
    -(1) ݽٚ ௏٘ܳ Compose۽ ׮द ੘ࢿೠ׮ (X)

    -(2) ೨ब ௏٘ܳ ܻ࠙ೞৈ Composeীࢲ ੤ഝਊೠ׮ (X)

    -(3) ࢚ഐ ਍ਊࢿ APIܳ ࢎਊೞৈ ӝઓ ௏٘ܳ ېೝೠ׮ ✅
    NAVER Map SDK != Open Source Project
    ইऔѱب ֎੉ߡ૑ب SDKח য়೑ࣗझо ইפۄࢲ ࣻ੿ೡ ߑߨ੉ হणפ׮.

    Ӓېࢲ ݃૑݄ ߑߨੋ ӝઓ ௏٘ܳ ېೝೞח ߑधਵ۽ ૓೯೮णפ׮.

    View Slide

  13. Link: h
    tt
    ps://github.com/Moop-App/Moop-Android/pull/120
    (1) Migrate using Compose API
    ୊਺ীח Compose APIܳ ੉ਊ೧ࢲ

    ష੉ ೐۽ં౟ী ࢤ૞۽ ٸ۰߅ח(?) ੘সਸ ೮णפ׮.

    View Slide

  14. Link: h
    tt
    ps://github.com/fornewid/naver-map-compose
    (2) For Jetpack Compose

    Unofficial wrapper library
    Ӓ ׮਺ীח ખ ؊ ҕਊചػ ਬૉா੉झী ݏ୾

    Compose wrapper ۄ੉࠳۞ܻܳ ٜ݅঻णפ׮.

    View Slide

  15. Jetpack Composeীࢲ ֎੉ߡ૑ب SDK ࢎਊೞӝ
    ݾ಴
    ࠂ੟ೠ Widgetਸ Composeীࢲ ੤ഝਊೞח ߑߨ

    View Slide

  16. Toy Project੄

    UseCase ҳഅೞӝ
    Link: h
    tt
    ps://github.com/Moop-App/Moop-Android/pull/120
    ݢ੷ ష੉ ೐۽ં౟ী ҳഅೠ 5о૑ ਬૉா੉झܳ ࢓ಝࠁѷणפ׮.

    View Slide

  17. Use Case 1:

    ૑ب ࠁৈ઱ӝ

    View Slide

  18. MapView


    android:id="@+id/map_view"


    android:layout_width="match_parent"


    android:layout_height="match_parent" />
    View Stystemীࢲח ۨ੉ইਓ XMLী MapViewܳ ࢶ঱ೞҊ

    View Slide

  19. MapView
    class MapViewActivity : AppCompatActivity() {


    private var mapView: MapView? = null


    override fun onCreate(savedInstanceState: Bundle?) {


    super.onCreate(savedInstanceState)


    setContentView(R.layout.activity_map_view)


    mapView = findViewById(R.id.map_view)


    }


    }
    Activityীࢲ ࢸ੿೧઱ݶ ؾפ׮

    View Slide

  20. MapView
    class MapComposeActivity : ComponentActivity() {


    override fun onCreate(savedInstanceState: Bundle?) {


    super.onCreate(savedInstanceState)


    setContent { ... }


    }


    }
    Link: h
    tt
    ps://developer.android.com/jetpack/compose/interop/interop-apis#compose-in-views
    Composeীࢲח Layout XML ؀न

    setContentী Composableਸ ੹׳ೞݶ ؾפ׮

    View Slide

  21. MapView
    val context = LocalContext.current


    AndroidView(factory = { context -> MapView(context) })
    Link: h
    tt
    ps://developer.android.com/jetpack/compose/interop/interop-apis#views-in-compose
    ੉ۧѱ AndroidViewۄח APIо ઁҕغҊ ੓যࢲ

    factoryীࢲ MapViewܳ ૒੽ ࢤࢿೞݶ ؾפ׮

    View Slide

  22. Link: h
    tt
    ps://developer.android.com/guide/components/activities/activity-lifecycle
    Lifecycle
    Ӓۧ૑݅ Lifecycleী ؀೧ ઑӘ ؊ ׮ܞঠೞח ࠗ࠙੉ ੓णפ׮

    View Slide

  23. MapView Lifecycle
    override fun onCreate(...) {


    super.onCreate(...)


    mapView?.onCreate(...)


    }


    override fun onStart() {


    super.onStart()


    mapView?.onStart()


    }


    override fun onResume() {


    super.onResume()


    mapView?.onResume()


    }
    override fun onPause() {


    super.onPause()


    mapView?.onPause()


    }


    override fun onStop() {


    super.onStop()


    mapView?.onStop()


    }


    override fun onDestroy() {


    super.onDestroy()


    mapView?.onDestroy()


    }
    🤔
    View Systemীࢲח ੉۠ ഋక۽ ഐ୹ೞҊ ੓঻ਸ ѩפ׮

    View Slide

  24. MapView Lifecycle
    val context = LocalContext.current


    AndroidView(factory = { context -> MapView(context) })
    ׮द ੉੹ ௏٘۽ جইоࠁѷणפ׮

    View Slide

  25. MapView Lifecycle
    val context = LocalContext.current


    val mapView = remember { MapView(context) }


    AndroidView(factory = { mapView })
    Link: h
    tt
    ps://developer.android.com/jetpack/compose/pe
    rf
    ormance/bestpractices#use-remember
    MapView੄ ೣࣻܳ ഐ୹೧ঠ ೞפө,

    ݢ੷ remember ೣࣻܳ ੉ਊ೧ࢲ MapViewܳ ܻ࠙೧յ ѩפ׮

    View Slide

  26. Link: h
    tt
    ps://developer.android.com/jetpack/compose/lifecycle
    remember
    Recompositionীࢲ ੉੹ ࢚కܳ ਬ૑ೠ׮.
    ઺р઺р ੉ۧѱ Compose APIܳ ࢸݺೞח ठۄ੉٘о ੓णפ׮

    View Slide

  27. MapView Lifecycle
    val context = LocalContext.current


    val mapView = remember(context){ MapView(context) } // (X)


    AndroidView(factory = { mapView })
    Link: h
    tt
    ps://developer.android.com/jetpack/compose/pe
    rf
    ormance/bestpractices#use-remember
    ੉ ٸ remember ೣࣻী keyܳ ֍ਵݶ,

    keyо ߸҃ؼ ٸ݃׮ MapViewܳ ׮द ࢤࢿೞ޲۽ ઱੄೧ঠ ೤פ׮

    View Slide

  28. MapView Lifecycle
    val context = LocalContext.current


    val mapView = remember { MapView(context) }


    AndroidView(factory = { mapView })
    Link: h
    tt
    ps://developer.android.com/jetpack/compose/pe
    rf
    ormance/bestpractices#use-remember
    ৈӝࢲח MapViewо Compositionীࢲ݅ ೠߣ ࢤࢿغب۾ keyܳ ֍૑ ঋणפ׮

    View Slide

  29. MapView Lifecycle
    val mapView = rememberMapViewWithLifecycle()


    AndroidView(factory = { mapView })


    @Composable


    fun rememberMapViewWithLifecycle(): MapView {


    val context = LocalContext.current


    val mapView = remember { MapView(context) }


    ...


    return mapView


    }
    MapViewܳ ܻ࠙೮ਵפ, ੉ઁח Lifecycleਸ ୊ܻ೧ࠅөਃ?

    View Slide

  30. MapView Lifecycle
    private fun getMapLifecycleObserver(


    mapView: MapView,


    savedInstanceState: Bundle?


    ) = LifecycleEventObserver { _, event ->


    when (event) {


    Lifecycle.Event.ON_CREATE -> mapView.onCreate(savedInstanceState)


    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()


    }


    }
    LifecycleEventObserverܳ ੉ਊ೧ࢲ

    пп੄ ੉߮౟݃׮ MapView੄ ೣࣻܳ ഐ୹೧સפ׮

    View Slide

  31. MapView Lifecycle
    fun rememberMapViewWithLifecycle(): MapView {


    ...


    val lifecycle = LocalLifecycleOwner.current.lifecycle


    DisposableEffect(lifecycle, mapView) {


    val lifecycleObserver = getMapLifecycleObserver(mapView, null)


    lifecycle.addObserver(lifecycleObserver)


    onDispose {


    lifecycle.removeObserver(lifecycleObserver)


    }


    }


    return mapView


    }
    ӒܻҊ Observerܳ Lifecycleী োѾ/೧ઁ೧઱ח ௏٘ܳ ੘ࢿೞݶ ؾפ׮

    View Slide

  32. MapView Lifecycle
    fun rememberMapViewWithLifecycle(): MapView {


    ...


    val lifecycle = LocalLifecycleOwner.current.lifecycle


    DisposableEffect(lifecycle, mapView) {


    val lifecycleObserver = getMapLifecycleObserver(mapView, null)


    lifecycle.addObserver(lifecycleObserver)


    onDispose {


    lifecycle.removeObserver(lifecycleObserver)


    }


    }


    return mapView


    }
    Link: h
    tt
    ps://developer.android.com/jetpack/compose/side-e
    ff
    ects#disposablee
    ff
    ect
    ੉ ٸ, DisposableEffect৬ onDisposeۄח Ѫਸ ੉ਊ೧ࢲ

    Observer੄ োѾҗ ೧ઁ द੼ਸ ੿੄೧઱ݶ غחؘਃ

    View Slide

  33. Compositionਸ ઙܐೠ റ, ੿ܻ೧ঠ ೡ ٸ ࢎਊೠ׮.
    Source: h
    tt
    ps://medium.com/mobile-app-development-publication/a4867f876928
    DisposableEffect

    View Slide

  34. ഐ୹೧ঠ ೞח Lifecycle ೣࣻо ই૒ թই੓׮.

    Lifecycle
    Link: h
    tt
    ps://navermaps.github.io/android-map-sdk/guide-ko/2-1.html

    View Slide

  35. SavedInstanceState
    class MapViewActivity : AppCompatActivity() {


    override fun onCreate(savedInstanceState: Bundle?) {


    ...


    mapView?.onCreate(savedInstanceState)


    }


    override fun onSaveInstanceState(outState: Bundle) {


    super.onSaveInstanceState(outState)


    mapView?.onSaveInstanceState(outState)


    }


    }
    View Systemীࢲח onCreate৬ onSaveInstanceStateীࢲ ೣࣻܳ ഐ୹೧઱ݶ غ঻ҳਃ

    View Slide

  36. SavedInstanceState
    val savedInstanceState = rememberSavedInstanceState()


    val mapView = rememberMapViewWithLifecycle(savedInstanceState)


    AndroidView(factory = { mapView })


    @Composable


    fun rememberSavedInstanceState(): Bundle {


    return rememberSaveable { Bundle() } // Survives config changes


    }
    Composeীࢲח Bundleਸ ૒੽ ҙܻೞח ߑߨਸ ࢎਊೡ ࣻ ੓যਃ

    ੉ ٸ, Bundleਸ rememberSaveable۽ ੷੢೤פ׮

    View Slide

  37. Recomposition, Configuration Changesীࢲ ੉੹ ࢚కܳ ਬ૑ೠ׮.
    Link: h
    tt
    ps://developer.android.com/jetpack/compose/state#restore-ui-state
    rememberSaveable
    Configuration Changes

    View Slide

  38. SavedInstanceState
    @Composable


    fun rememberMapViewWithLifecycle(savedInstanceState: Bundle): MapView {


    ...


    DisposableEffect(lifecycle, mapView, savedInstanceState) {


    val lifecycleObserver = getMapLifecycleObserver(


    mapView, savedInstanceState.takeUnless { it.isEmpty }


    )


    onDispose {


    ...


    mapView.onSaveInstanceState(savedInstanceState)


    }


    }


    return mapView


    }
    Ӓېࢲ ૒੽ ҙܻೞח Bundle ё୓ܳ ੹׳೧઱ݶ ৮ܐؾפ׮

    View Slide

  39. SavedInstanceState
    private fun getMapLifecycleObserver(


    mapView: MapView,


    savedInstanceState: Bundle?


    ) = LifecycleEventObserver { _, event ->


    when (event) {


    Lifecycle.Event.ON_CREATE -> mapView.onCreate(savedInstanceState)


    ...


    }


    }
    LifecycleEventObserverীࢲب োѾ೧઱ݶ غѷભ

    View Slide

  40. onLowMemory
    override fun onLowMemory() {


    super.onLowMemory()


    mapView?.onLowMemory()


    }
    ݃૑݄ਵ۽ onLowMemory ੑפ׮

    Viewীࢲח ೣࣻо ઁҕغҊ ੓যࢲ рױೠؘਃ

    View Slide

  41. onLowMemory
    fun MapView.componentCallbacks(): ComponentCallbacks {


    return object : ComponentCallbacks {


    override fun onConfigurationChanged(config: Configuration) {}


    override fun onLowMemory() {


    [email protected]()


    }


    }


    }
    Composeীࢲח LifecycleObserver୊ۢ

    ComponentCallbacksܳ ٜ݅যࢲ ഐ୹ೞݶ ؾפ׮

    View Slide

  42. onLowMemory
    fun rememberMapViewWithLifecycle(): MapView {


    ...


    val lifecycle = LocalLifecycleOwner.current.lifecycle


    DisposableEffect(lifecycle, mapView) {


    val callbacks = mapView.componentCallbacks()


    context.registerComponentCallbacks(callbacks)


    onDispose {


    context.unregisterComponentCallbacks(callbacks)


    }


    }


    return mapView


    }
    ١۾/೧ઁೞח ࠗ࠙਷ খࢶ җ੿җ زੌ೤פ׮

    View Slide

  43. MapViewח View ৉ೡ݅ਸ ׸׼ೠ׮.

    APIܳ ഐ୹ೞ۰ݶ ੋఠಕ੉झ ৉ೡਸ ೞח NaverMap ё୓о ೙ਃೞ׮.
    OnMapReadyCallback
    Link: h
    tt
    ps://navermaps.github.io/android-map-sdk/guide-ko/2-1.html
    MapView۽ ૑ب ࠁৈ઱ח ੘স਷ ՘լ૑݅

    খਵ۽ ૑بܳ ஶ౟܀ೞח ੘সٜ੉ ೙ਃ೤פ׮

    View Slide

  44. OnMapReadyCallback
    private val callback = object : OnMapReadyCallback {


    override fun onMapReady(naverMap: NaverMap) { ... }


    }


    mapView?.getMapAsync(callback)
    NaverMap ё୓ܳ ঳ਵ۰ݶ,

    OnMapReadyCallbackਸ ࢎਊೞৈ Callback ഋక۽ оઉয়ݶ ؾפ׮

    View Slide

  45. OnMapReadyCallback
    suspend fun MapView.awaitMap(): NaverMap {


    return suspendCoroutine { continuation ->


    getMapAsync { continuation.resume(it) }


    }


    }


    val coroutineScope = rememberCoroutineScope()


    val mapView = rememberMapViewWithLifecycle(...)


    coroutineScope.launch {


    val naverMap: NaverMap = mapView.awaitMap()


    }
    Link: h
    tt
    ps://developer.android.com/jetpack/compose/side-e
    ff
    ects#remembercoroutinescope
    Composeীࢲח ௏ܖ౯ਵ۽ оઉয়ח ೣࣻܳ ٜ݅঻ҳਃ

    ೙ਃೡ ٸ CoroutineScopeܳ ࢤࢿ೧ NaverMap ё୓ܳ оઉৢ ࣻ ੓णפ׮

    View Slide

  46. Compositionਸ ઙܐೠ റ ੗زਵ۽ ஂࣗغب۾ ߧਤо ૑੿ػ ௏ܖ౯ਸ प೯ೠ׮.
    Source: h
    tt
    ps://medium.com/mobile-app-development-publication/a4867f876928
    rememberCoroutineScope

    View Slide

  47. OnMapReadyCallback
    suspend fun MapView.awaitMap(): NaverMap {


    return suspendCoroutine { continuation ->


    getMapAsync { continuation.resume(it) }


    }


    }


    val coroutineScope = rememberCoroutineScope()


    val mapView = rememberMapViewWithLifecycle(...)


    coroutineScope.launch {


    val naverMap: NaverMap = mapView.awaitMap()


    }

    ੉ۧѱ NaverMap ё୓ܳ оઉয়ח ؘө૑ ޖࢎ൤ ݃ଢ଼णפ׮

    View Slide

  48. Use Case 2:

    ૑ب ২࣌

    View Slide

  49. ૑ب ২࣌
    mapView.getMapAsync { naverMap ->


    if (isDarkTheme) {


    naverMap.mapType = NaverMap.MapType.Navi


    naverMap.isNightModeEnabled = true


    } else {


    naverMap.mapType = NaverMap.MapType.Basic


    naverMap.isNightModeEnabled = false


    }


    }
    ঠрݽ٘ա ૑ب੄ ఋੑ ١ਸ ߸҃ೞҊ र׮ݶ,

    NaverMap ё୓ী׮о ૒੽ mapType ١ਸ ࢸ੿೧઱ݶ ؾפ׮

    View Slide

  50. ૑ب ২࣌
    AndroidView(factory = {


    mapView.apply {


    coroutineScope.launch {


    val naverMap = awaitMap()


    if (isDarkTheme) {


    naverMap.mapType = NaverMap.MapType.Navi


    naverMap.isNightModeEnabled = true


    } else {


    naverMap.mapType = NaverMap.MapType.Basic


    naverMap.isNightModeEnabled = false


    }


    }


    }


    })
    Composeীࢲب Ѣ੄ زੌೠ ௏٘ܳ Ӓ؀۽ ࢎਊೞݶ غѷणפ׮

    View Slide

  51. ૑ب ࣘࢿ


    android:id="@+id/map_view"


    android:layout_width="match_parent"


    android:layout_height="match_parent"


    app:navermap_extent="31.43,122.37,44.35,132"


    app:navermap_locationButtonEnabled="true"


    app:navermap_scaleBarEnabled="false"


    app:navermap_minZoom="6"


    app:navermap_zoom="12" />
    ׮਺ਵ۽ Layout XMLীࢲ ݻ о૑ ӝࠄ੸ੋ ࣘࢿਸ ੿੄ೡ ࣻ ੓঻חؘਃ

    View Slide

  52. ૑ب ࣘࢿ
    val context = LocalContext.current


    val mapView = remember { MapView(context, naverMapOptions()) }


    AndroidView(factory = { mapView })


    private fun naverMapOptions() = NaverMapOptions()


    .extent(LatLngBounds.from(LatLng(31.43, 122.37),LatLng(44.35, 132.0)))


    .locationButtonEnabled(true)


    .scaleBarEnabled(false)


    .minZoom(6.0)


    .camera(CameraPosition(LatLng.INVALID, 12.0))
    ੉۠ ٜࠗ࠙ب ௏٘۽ ੿੄ೞҊ, MapView ࢤࢿ੗ী ੹׳ೞݶ ؾפ׮

    ل ׮ ࠺तೠ ௏٘о ٜ݅য઎ભ?

    View Slide

  53. Use Case 3:

    ૑ب ੉߮౟

    View Slide

  54. ૑ب ੉߮౟
    mapView.getMapAsync { naverMap ->


    naverMap.setOnMapClickListener { _, _ -> /* Do domething*/ }


    }
    NaverMapী Click Listenerܳ ١۾ೞח ௏٘ੑפ׮

    View Slide

  55. ૑ب ੉߮౟
    AndroidView(factory = {


    mapView.apply {


    coroutineScope.launch {


    val naverMap = awaitMap()


    naverMap.setOnMapClickListener { _, _ -> /* Do domething*/ }


    }


    }


    })
    Composeীࢲب زੌೠ ௏٘ܳ ੘ࢿೞݶ ؾפ׮

    View Slide

  56. Use Case 4:

    ݃ழ ࠁৈ઱ӝ

    View Slide

  57. ݃ழ
    val marker = Marker().apply {


    captionText = "caption"


    position = LatLng(..., ...)


    icon = OverlayImage.fromResource(R.drawable.marker_icon)


    setOnClickListener {


    ...


    true


    }


    }


    Viewٚ Composeٚ ࢚ҙহ੉ ݃ழܳ ੉ۧѱ ࢤࢿ೤פ׮

    View Slide

  58. Example: ӓ੢ ݃ழ ҙܻೞӝ
    var markers = listOf()


    fun render(theaters: List) {


    markers.forEach { it.map = null }


    markers = theaters.map { theater ->


    Marker().apply {


    ...


    map = naverMap


    }


    }


    } ӓ੢ ݾ۾ਸ ݃ழ۽ ಴दೞח ৘ઁ ௏٘ੑפ׮

    Markerী NaverMapਸ ࢸ੿ೞҊ, null۽ ೧ઁ೧ঠ ೤פ׮

    View Slide

  59. Example: ӓ੢ ݃ழ ҙܻೞӝ
    var markers by remember { mutableStateOf>(emptyList()) }


    LaunchedEffect(theaters) {


    val naverMap = mapView.awaitMap()


    markers.forEach { it.map = null }


    markers = theaters.map { theater ->


    Marker().apply {


    ...


    map = naverMap


    }


    }


    }
    Link: h
    tt
    ps://developer.android.com/jetpack/compose/side-e
    ff
    ects#launchede
    ff
    ect
    Composeীࢲب زੌೠ ௏٘ܳ ੘ࢿ೮ҳਃ

    LaunchedEffectܳ ࢎਊ೧ࢲ theaters੉ ߄Չ ٸ݅ ݃ழܳ ׮द Ӓܻب۾ ೮णפ׮

    View Slide

  60. Composable ߧਤীࢲ suspend funܳ ഐ୹ೡ ٸ ࢎਊೠ׮.

    Composition द੼ ژח keyо ߸҃ؼ ٸ݃׮ ׮द ഐ୹ػ׮.
    LaunchedEffect
    Source: h
    tt
    ps://medium.com/mobile-app-development-publication/a4867f876928

    View Slide

  61. Use Case 5:

    ஠ݫۄ ੉ز

    View Slide

  62. ஠ݫۄ ੉ز
    // move


    naverMap.moveCamera(CameraUpdate.zoomTo(12.0))


    // animation


    naverMap.moveCamera(


    CameraUpdate


    .scrollAndZoomTo(LatLng(..., ...), 16.0)


    .animate(CameraAnimation.Fly)


    )
    Link: h
    tt
    ps://navermaps.github.io/android-map-sdk/guide-ko/3-2.html
    ஠ݫۄܳ ੉زೞח ௏٘۽, গפݫ੉࣌ ৈࠗী ٮۄ ࢎਊߑߨ੉ ઑӘ ׮ܵפ׮

    ׮নೠ APIо ੓ਵפ, ֎੉ߡ૑ب SDK੄ ޙࢲܳ ଵҊೞݶ ب਑ؼ Ѫ эणפ׮

    View Slide

  63. Example: ݃ழ ࢶఖೞӝ
    Marker().apply {


    setOnClickListener {


    /* Marker Selected */


    naverMap.moveCamera(...)


    }


    }


    mapView.getMapAsync { naverMap ->


    naverMap.setOnMapClickListener { _, _ ->


    /* Marker Unselected */


    naverMap.moveCamera(...)


    }


    }
    ݃ழ৬ ૑بܳ ௿ܼೞݶ ஠ݫۄܳ ੉زೞח ৘ઁ ௏٘ੑפ׮

    View Slide

  64. Example: ݃ழ ࢶఖೞӝ
    var selectedTheater by remember { mutableStateOf)(null) }


    Marker().apply {


    setOnClickListener {


    /* Marker Selected */


    selectedTheater = theater


    }


    }


    mapView.getMapAsync { naverMap ->


    naverMap.setOnMapClickListener { _, _ ->


    /* Marker Unselected */


    selectedTheater = null


    }


    }
    Composeীࢲب زੌೞѱ ೡ ࣻ ੓૑݅,

    ࢶఖػ ӓ੢ਸ ࢚క۽ ੿੄೧فҊ ࢚కо ߸҃ؼ ٸ ஠ݫۄܳ ੉زೞח ߑߨਸ ࢎਊ೮णפ׮

    View Slide

  65. Composeীࢲ ҙ଴ೡ ࣻ ੓ח MutableStateܳ ࢤࢿೞח ೣࣻ.

    valueо ߸҃غݶ, valueܳ ੍ח Composable ೣࣻ੄ Recomposition੉ ৘ডػ׮.

    mutableStateOf
    Link: h
    tt
    ps://developer.android.com/jetpack/compose/state#state-in-composables
    interface MutableState : State {


    override var value: T


    }
    var value by remember { mutableStateOf(default) }
    Recompositionীࢲ ੉੹ valueܳ ਬ૑ೞ۰ݶ rememberܳ ࢎਊ೧ঠ ೠ׮.

    View Slide

  66. Example: ݃ழ ࢶఖೞӝ
    var selectedTheater by remember { mutableStateOf)(null) }


    LaunchedEffect(selectedTheater) {


    val naverMap = mapView.awaitMap()


    if (selectedTheater != null) {


    naverMap.moveCamera(...)


    } else {


    naverMap.moveCamera(...)


    }


    }


    LaunchedEffectܳ ੉ਊೞৈ, ࢶఖػ ӓ੢ ч੉ ߸҃ؼ ٸ݃׮

    ੸׼ೠ ਤ஖۽ ஠ݫۄܳ ੉زೡ ࣻ ੓ѷणפ׮

    View Slide

  67. ৈӝࢲ ਫ਼Ӭ!

    Architecture: Unidirectional Data Flow
    var name by remember { mutableStateOf("") }


    OutlinedTextField(


    value = name,


    onValueChange = { name = it },


    label = { Text("Name") }


    )
    Link: h
    tt
    ps://developer.android.com/jetpack/compose/architecture

    ࢚కח ইې۽ ੉زೞҊ ੉߮౟ח ਤ۽ ੉زೞח ٣੗ੋ ಁఢ.

    View Slide

  68. val savedInstanceState = rememberSavedInstanceState()


    val mapView = rememberMapViewWithLifecycle(savedInstanceState, naverMapOptions())


    AndroidView(factory = {


    mapView.apply {


    coroutineScope.launch {


    val naverMap = awaitMap()


    naverMap.mapType = NaverMap.MapType.Navi


    naverMap.isNightModeEnabled = true


    naverMap.setOnMapClickListener { _, _ -> /* Do domething*/ }


    }


    }


    })


    খীࢲ ੘ࢿ೮؍ ௏٘о ࢶ঱ഋ UIী ੸೤ೞѱ ҳഅغ঻ח૑ ࢤп೧ࠇद׮

    View Slide

  69. @Composable


    fun NaverMap(


    modifier: Modifier = Modifier,


    mapOptions: NaverMapOptions = ...,


    mapProperties: MapProperties = ...,


    onMapClick: (PointF, LatLng) -> Unit = { _, _ -> },


    ) {


    val savedInstanceState = rememberSavedInstanceState()


    val mapView = rememberMapViewWithLifecycle(savedInstanceState, mapOptions)


    AndroidView(factory = {


    mapView.apply {


    coroutineScope.launch {


    val naverMap = awaitMap()


    naverMap.mapType = mapProperties.mapType


    naverMap.isNightModeEnabled = mapProperties.isNightModeEnabled


    naverMap.setOnMapClickListener(onMapClick)


    }


    }


    })


    }
    ੉ ௏٘ܳ хऱࢲ NaverMap੉ۄח Composableਸ ٜ݅ ࣻ ੓णפ׮

    View Slide

  70. @Composable


    fun NaverMap(


    modifier: Modifier = Modifier,


    mapOptions: NaverMapOptions = ...,


    mapProperties: MapProperties = ...,


    onMapClick: (PointF, LatLng) -> Unit = { _, _ -> },


    ) {


    val savedInstanceState = rememberSavedInstanceState()


    val mapView = rememberMapViewWithLifecycle(savedInstanceState, mapOptions)


    AndroidView(factory = {


    mapView.apply {


    coroutineScope.launch {


    val naverMap = awaitMap()


    naverMap.mapType = mapProperties.mapType


    naverMap.isNightModeEnabled = mapProperties.isNightModeEnabled


    naverMap.setOnMapClickListener(onMapClick)


    }


    }


    })


    }
    NaverMap(


    mapOptions = naverMapOptions(),


    mapProperties = MapProperties(


    mapType = NaverMap.MapType.Navi,


    isNightModeEnabled = true,


    ),


    onMapClick = { _, _ -> /* Do domething*/ }


    )
    ࢎਊೞח ௏٘ ৘दੑפ׮

    Ӓۢ ࢶ঱ഋ UIܳ ҳഅ೮׮Ҋ ೡ ࣻ ੓ਸөਃ?

    View Slide

  71. @Composable


    fun NaverMap(


    modifier: Modifier = Modifier,


    mapOptions: NaverMapOptions = ...,


    mapProperties: MapProperties = ...,


    onMapClick: (PointF, LatLng) -> Unit = { _, _ -> },


    ) {


    val savedInstanceState = rememberSavedInstanceState()


    val mapView = rememberMapViewWithLifecycle(savedInstanceState, mapOptions)


    AndroidView(factory = {


    mapView.apply {


    coroutineScope.launch {


    val naverMap = awaitMap()


    naverMap.mapType = mapProperties.mapType


    naverMap.isNightModeEnabled = mapProperties.isNightModeEnabled


    naverMap.setOnMapClickListener(onMapClick)


    }


    }


    })


    }
    🤔
    ࢚కо ߸҃ػ׮ݶ?
    🫠
    Recompositionীࢲ factoryח ׮द ഐ୹غ૑ ঋח׮.

    updateীࢲ јनೡ ࣻח ੓૑݅, ݒߣ side-e
    ff
    ectܳ ݅٘ח ޙઁ.

    View Slide

  72. Library۽

    ҕਊചೞӝ
    Link: h
    tt
    ps://github.com/fornewid/naver-map-compose
    ૑Әө૑ ੘ࢿೠ ௏٘ח ೞա੄ জীࢲ ࢎਊೞӝীח ୽࠙೮૑݅,

    ۄ੉࠳۞ܻ۽ ٜ݅ٸח ҕਊചػ ਬૉா੉झী ݏ୾ࢲ ੘ࢿ೧ঠѷભ?

    Ӓېࢲ ੉ߣীח খীࢲ ҳഅ೮؍ 5о૑ ਬૉா੉झܳ ѐࢶ೧ࠁ۰Ҋ ೤פ׮

    View Slide

  73. Use Case 1:

    ૑ب ࠁৈ઱ӝ

    View Slide

  74. ૑ب ࠁৈ઱ӝ ✅
    NaverMap(...)
    ੉ ࠗ࠙਷ ௼ѱ Ҋச ࠗ࠙੉ হ੉

    ইө ੘ࢿ೮؍ ௏ٜ٘ਸ Ӓ؀۽ ࢎਊೞݶ ؾפ׮

    View Slide

  75. Use Case 2:

    ૑ب ২࣌

    View Slide

  76. ૑ب ২࣌
    @Composable


    fun NaverMap(properties: MapProperties = ...) {


    ...


    AndroidView(factory = {


    mapView.apply {


    coroutineScope.launch {


    val naverMap = awaitMap()


    naverMap.mapType = properties.mapType


    naverMap.isNightModeEnabled = properties.isNightModeEnabled


    }


    }


    })
    ইө ੘ࢿ೮؍ NaverMap ё୓ী mapType ١ਸ ࢸ੿ೞח ௏٘ੑפ׮

    View Slide

  77. ૑ب ২࣌
    @Composable


    fun NaverMap(properties: MapProperties = ...) {


    ...


    AndroidView(factory = { mapView })


    LaunchedEffect(Unit) {


    val naverMap = mapView.awaitMap()


    naverMap.mapType = properties.mapType


    naverMap.isNightModeEnabled = properties.isNightModeEnabled


    }


    }
    Side-effect APIੋ LaunchedEffectܳ ੉ਊ೧ࢲ

    Compositionীࢲ݅ ഐ୹غب۾ ߸҃ೡ ࣻ ੓णפ׮

    ੉۞ݶ Recompositionীࢲח ׮द ࢸ੿غ૑ ঋѷભ

    View Slide

  78. ૑ب ২࣌
    @Composable


    fun NaverMap(properties: MapProperties = ...) {


    ...


    AndroidView(factory = { mapView })


    LaunchedEffect(Unit) {


    val naverMap = mapView.awaitMap()


    naverMap.mapType = properties.mapType


    naverMap.isNightModeEnabled = properties.isNightModeEnabled


    }


    } prope
    rt
    iesо ߸҃غযب ߈৔غ૑ ঋח ޙઁ.
    ೞ૑݅ propertiesо ߸҃غযب ߈৔غ૑ ঋח ޙઁо ࢤӤפ׮

    View Slide

  79. ૑ب ২࣌
    @Composable


    fun NaverMap(properties: MapProperties = ...) {


    ...


    AndroidView(factory = { mapView })


    val currentProperties by rememberUpdatedState(properties)


    LaunchedEffect(Unit) {


    val naverMap = mapView.awaitMap()


    naverMap.mapType = currentProperties.mapType


    naverMap.isNightModeEnabled = currentProperties.isNightModeEnabled


    }


    }
    Link: h
    tt
    ps://developer.android.com/jetpack/compose/side-e
    ff
    ects#rememberupdatedstate
    rememberUpdatedState۽ propertiesܳ хऱࢲ ੹׳ೞݶ

    ݃૑݄ী ੹׳߉਷ propertiesী ੽Ӕೡ ࣻ ੓णפ׮

    View Slide

  80. rememberUpdatedState
    ч੉ ߸҃غযب ੤द੘ೞ૑ ঋইঠ ೞח ബҗ੄ чਸ ଵઑೡ ٸ ࢎਊೠ׮.

    Source: h
    tt
    ps://medium.com/mobile-app-development-publication/a4867f876928

    View Slide

  81. ૑ب ২࣌
    val currentProperties by rememberUpdatedState(properties)


    LaunchedEffect(Unit) {


    val naverMap = mapView.awaitMap()


    naverMap.mapType = currentProperties.mapType


    naverMap.isNightModeEnabled = currentProperties.isNightModeEnabled


    }


    ௏٘۽ ׮द جইоࠇद׮

    ੉ ௏٘ীࢲ side-effectо ߊࢤೞח द੼੉ হભ?

    View Slide

  82. ૑ب ২࣌
    val currentProperties by rememberUpdatedState(properties)


    val parentComposition = rememberCompositionContext()


    LaunchedEffect(Unit) {


    val naverMap = mapView.awaitMap()


    val composition = Composition(MapApplier(naverMap), parentComposition).apply {


    setContent { MapUpdater(mapProperties = currentProperties) }


    }


    try {


    awaitCancellation()


    } finally {


    composition.dispose()


    }


    }
    ࣁࠗ੸ੋ ࠗ࠙੉ ࡅ઎חؘ, पઁ۽ח ੉ۧѱ ੘ࢿ೮णפ׮

    View Slide

  83. ૑ب ২࣌
    val currentProperties by rememberUpdatedState(properties)


    val parentComposition = rememberCompositionContext()


    LaunchedEffect(Unit) {


    val naverMap = mapView.awaitMap()


    val composition = Composition(MapApplier(naverMap), parentComposition).apply {


    setContent { MapUpdater(mapProperties = currentProperties) }


    }


    try {


    awaitCancellation()


    } finally {


    composition.dispose()


    }


    }
    Link: h
    tt
    ps://developer.android.com/reference/kotlin/androidx/compose/runtime/#Composition
    ࢜۽਍ Compositionਸ द੘ೞח ߑߨ.
    rememberCompositionContext۽ parentCompositionਸ ٜ݅Ҋ

    Compositionਵ۽ child compositionਸ ٜ݅঻णפ׮

    setContent উীࢲח Composableਸ ࢶ঱ೡ ࣻ ੓Ҋ, ݽف ՘աݶ disposeܳ ഐ୹೤פ׮

    View Slide

  84. ૑ب ২࣌
    val currentProperties by rememberUpdatedState(properties)


    val parentComposition = rememberCompositionContext()


    LaunchedEffect(Unit) {


    val naverMap = mapView.awaitMap()


    val composition = Composition(MapApplier(naverMap), parentComposition).apply {


    setContent { MapUpdater(mapProperties = currentProperties) }


    }


    try {


    awaitCancellation()


    } finally {


    composition.dispose()


    }


    }
    Link: h
    tt
    ps://developer.android.com/reference/kotlin/androidx/compose/runtime/Applier
    ૑ب ࣘࢿਸ Composable উীࢲ ҙܻೠ׮.
    ૑ب ࣘࢿ਷ MapUpdaterۄח Composable উীࢲ ҙܻೞҳਃ

    Child Compositionਸ ࢤࢿೡ ٸ, Composable Treeܳ ҙܻೞח Applierܳ ֍णפ׮

    View Slide

  85. MapApplierח Composition ઺ী MapNode ౟ܻܳ ҙܻೞח ৉ೡ.

    MapUpdaterח ૑ب ࣘࢿਸ ҙܻೞח ComposeNodeܳ ࢤࢿೠ׮.

    MapUpdater
    NaverMap
    MapApplier, MapUpdater
    MapApplier
    MapPropertiesNode
    Listeners
    getMap()
    MapNode
    MapNode
    MapNode
    = ComposeNode
    NaverMap
    @Composable
    @Composable

    View Slide

  86. MapApplier
    class MapApplier(


    val map: NaverMap,


    ) : AbstractApplier(MapNodeRoot) {


    private val decorations = mutableListOf()


    ...


    }


    interface MapNode


    object MapRootNode : MapNode


    class MapPropertiesNode(val map: NaverMap) : MapNode
    MapApplierীࢲ MapNode Listܳ ҙܻೞҊ,

    MapNode ҳഅ୓ ઺ী MapPropertiesNodeо ੓णפ׮

    View Slide

  87. MapUpdater
    @Composable


    fun MapUpdater(mapProperties: MapProperties) {


    val map = (currentComposer.applier as MapApplier).map


    ComposeNode(


    factory = { MapPropertiesNode(map = map) },


    update = {


    set(mapProperties.mapType) { map.mapType = it.value }


    set(mapProperties.isNightModeEnabled) {


    map.isNightModeEnabled = it


    }


    }


    )
    Link: h
    tt
    ps://developer.android.com/reference/kotlin/androidx/compose/runtime/#ComposeNode
    MapPropertiesNodeח MapUpdater Composable ղࠗীࢲ

    ComposeNode APIܳ ੉ਊ೧ࢲ ࢤࢿೞҳਃ

    AndroidView API୊ۢ factoryীࢲ ࢤࢿ, updateীࢲ јन೧઱ח ഋక۽ غয ੓णפ׮

    View Slide

  88. MapProperties
    data class MapProperties(


    public val mapType: MapType = MapType.Basic,


    public val isNightModeEnabled: Boolean = false,


    )


    @Immutable


    enum class MapType(


    public val value: com.naver.maps.map.NaverMap.MapType


    ) {


    Basic(com.naver.maps.map.NaverMap.MapType.Basic),


    ...


    }
    Link: h
    tt
    ps://medium.com/androiddevelopers/jetpack-compose-stability-explained-79c10db270c8
    MapPropertiesח mapType э਷ ૑ب੄ ࣘࢿҗ ݒடغח data classੑפ׮

    ৈӝࢲ MapType਷ ֎੉ߡ૑ب SDK੄ MapTypeਸ хऱח ഋక۽ غয ੓חؘਃ

    View Slide

  89. MapProperties
    data class MapProperties(


    public val mapType: MapType = MapType.Basic,


    public val isNightModeEnabled: Boolean = false,


    )


    @Immutable


    enum class MapType(


    public val value: com.naver.maps.map.NaverMap.MapType


    ) {


    Basic(com.naver.maps.map.NaverMap.MapType.Basic),


    ...


    }
    Link: h
    tt
    ps://medium.com/androiddevelopers/jetpack-compose-stability-explained-79c10db270c8
    Compose ஹ౵ੌ۞о प೯غ૑ ঋח ݽٕ੄ ௿ېझח ࠛউ੿ೠ Ѫਵ۽ ୶ۿػ׮.

    wrapper classী ࠛ߸ਸ ੿੄ೞৈ Recompositionਸ ୭ࣗചೡ ࣻ ੓׮.
    Ӓ ੉ਬח, ֎੉ߡ૑ب SDKח Compose ஹ౵ੌ۞о प೯غ૑ ঋই

    Composeীࢲח ੉۠ ௿ېझܳ unstable۽ ୶ۿೞӝ ٸޙੑפ׮

    @immutableਸ ୶оೞৈ ࠛ೙ਃೠ recompositionਸ ઴ੌ ࣻ ੓णפ׮

    View Slide

  90. ૑ب ২࣌ ✅
    NaverMap(


    ...


    mapProperties = MapProperties(


    mapType = NaverMap.MapType.Navi,


    isNightModeEnabled = true,


    ),


    ...


    )
    ૑ب ২࣌ਸ ࢚క۽ ੹׳ೞח ࠗ࠙ਸ ৮ࢿ೮णפ׮

    View Slide

  91. Use Case 3:

    ૑ب ੉߮౟
    ੉ઁ ૑ب ੉߮౟۽ ֈযоࠁѷणפ׮

    View Slide

  92. ૑ب ২࣌
    @Composable


    fun NaverMap(properties: MapProperties = ...) {


    ...


    val currentProperties by rememberUpdatedState(properties)


    val parentComposition = rememberCompositionContext()


    LaunchedEffect(Unit) {


    val naverMap = mapView.awaitMap()


    val composition = Composition(


    MapApplier(naverMap),


    parentComposition


    ).apply {


    setContent { MapUpdater(mapProperties = currentProperties) }


    }
    ߑӘө૑ ࠄ ૑ب ২࣌ ௏٘۽, propertiesܳ ੹׳೧઱ח ࠗ࠙ਸ ੘ࢿ೮חؘਃ

    View Slide

  93. ૑ب ੉߮౟
    @Composable


    fun NaverMap(onMapClick: (PointF, LatLng) -> Unit = { _, _ -> }) {


    ...


    val mapClickListeners = remember { MapClickListeners() }


    .also { it.onMapClick = onMapClick }


    val parentComposition = rememberCompositionContext()


    LaunchedEffect(Unit) {


    val naverMap = mapView.awaitMap()


    val composition = Composition(


    MapApplier(naverMap),


    parentComposition


    ).apply {


    setContent { MapUpdater(clickListeners = mapClickListeners) }


    }
    ૑ب ੉߮౟ীࢲח onMapClick੉ۄח ۈ׮ܳ ੹׳೤פ׮

    View Slide

  94. ૑ب ੉߮౟
    @Composable


    fun NaverMap(onMapClick: (PointF, LatLng) -> Unit = { _, _ -> }) {


    ...


    val mapClickListeners = remember { MapClickListeners() }


    .also { it.onMapClick = onMapClick }


    val parentComposition = rememberCompositionContext()


    LaunchedEffect(Unit) {


    val naverMap = mapView.awaitMap()


    val composition = Composition(


    MapApplier(naverMap),


    parentComposition


    ).apply {


    setContent { MapUpdater(clickListeners = mapClickListeners) }


    }
    class MapClickListeners {


    var onMapClick: (PointF, LatLng) -> Unit by mutableStateOf({ _, _ -> })


    }
    MapClickListenersח onMapClick ۈ׮ܳ MutableState۽ ҙܻೞҊ,

    recompositionীࢲ MapClickListenersо ߄Շ૑ ঋب۾ remember۽ ӝর೧સפ׮

    View Slide

  95. ૑ب ੉߮౟
    @Composable


    fun NaverMap(onMapClick: (PointF, LatLng) -> Unit = { _, _ -> }) {


    ...


    val mapClickListeners = remember { MapClickListeners() }


    .also { it.onMapClick = onMapClick }


    val parentComposition = rememberCompositionContext()


    LaunchedEffect(Unit) {


    val naverMap = mapView.awaitMap()


    val composition = Composition(


    MapApplier(naverMap),


    parentComposition


    ).apply {


    setContent { MapUpdater(clickListeners = mapClickListeners) }


    }
    Ӓېࢲ MapUpdater ղࠗীࢲ ੉߮౟о ߊࢤೞݶ ੉۠ ࣽࢲ۽ ੉߮౟о ࢚ਤ۽ ੹౵ؾפ׮

    View Slide

  96. // Composition
    ૑ب ੉߮౟
    @Composable


    fun MapUpdater(clickListeners: MapClickListeners) {


    ComposeNode(


    factory = { MapPropertiesNode(clickListeners = clickListeners) },


    update = { update(clickListeners) { this.clickListeners = it } },


    )


    }


    class MapPropertiesNode(


    var map: NaverMap,


    var clickListeners: MapClickListeners,


    ) : MapNode {


    override fun onAttached() {


    map.setOnMapClickListener { point, coord -> clickListeners.onMapClick(point, coord) }


    }
    MapUpdaterীࢲח Composition द੼ী clickListenersܳ ੹׳೧઱Ҋ

    View Slide

  97. // Recomposition
    ૑ب ੉߮౟
    @Composable


    fun MapUpdater(clickListeners: MapClickListeners) {


    ComposeNode(


    factory = { MapPropertiesNode(clickListeners = clickListeners) },


    update = { update(clickListeners) { this.clickListeners = it } },


    )


    }


    class MapPropertiesNode(


    var map: NaverMap,


    var clickListeners: MapClickListeners,


    ) : MapNode {


    override fun onAttached() {


    map.setOnMapClickListener { point, coord -> clickListeners.onMapClick(point, coord) }


    }
    Recomposition द੼ীח ۈ׮о ߄Շ঻ਸ ࣻ ੓ਵפ

    MapClickListenersܳ ׮द Ү୓೧સפ׮

    View Slide

  98. ૑ب ੉߮౟
    @Composable


    fun MapUpdater(clickListeners: MapClickListeners) {


    ComposeNode(


    factory = { MapPropertiesNode(clickListeners = clickListeners) },


    update = { update(clickListeners) { this.clickListeners = it } },


    )


    }


    class MapPropertiesNode(


    var map: NaverMap,


    var clickListeners: MapClickListeners,


    ) : MapNode {


    override fun onAttached() {


    map.setOnMapClickListener { point, coord -> clickListeners.onMapClick(point, coord) }


    }
    MapPropertiesNodeо attachغݶ NaverMapী Listenerܳ ١۾೤פ׮

    View Slide

  99. ૑ب ੉߮౟
    @Composable


    fun MapUpdater(clickListeners: MapClickListeners) {


    ComposeNode(


    factory = { MapPropertiesNode(clickListeners = clickListeners) },


    update = { update(clickListeners) { this.clickListeners = it } },


    )


    }


    class MapPropertiesNode(


    var map: NaverMap,


    var clickListeners: MapClickListeners,


    ) : MapNode {


    override fun onAttached() {


    map.setOnMapClickListener { point, coord -> clickListeners.onMapClick(point, coord) }


    }
    ֎੉ߡ૑بীࢲ ੉߮౟о ߊࢤೞݶ ੉۠ ࣽਵ۽ ࢚ਤ۽ ੹౵ؾפ׮

    View Slide

  100. ૑ب ੉߮౟ ✅
    NaverMap(


    ...


    onMapClick = { _, _ -> /* Do domething*/ }


    ...


    )
    ੉ۧѱ ֎੉ߡ૑بীࢲ ੉߮౟ܳ ׮ܖח ࠗ࠙ب ҳഅ೧ࠌणפ׮

    View Slide

  101. Use Case 4:

    ݃ழ ࠁৈ઱ӝ
    ݃ழܳ ࠁৈ઱ח ࠗ࠙ਸ ׮द ࢓ಝࠁѷणפ׮

    View Slide

  102. // State
    ݃ழ
    val marker = Marker().apply {


    captionText = "caption"


    position = LatLng(..., ...)


    icon = OverlayImage.fromResource(R.drawable.marker_icon)


    setOnClickListener {


    ...


    true


    }


    map = naverMap


    }


    ੉ۧѱ ࢚కܳ ੿੄ೞח ࠗ࠙੉ ੓Ҋ

    View Slide

  103. // Event
    ݃ழ
    val marker = Marker().apply {


    captionText = "caption"


    position = LatLng(..., ...)


    icon = OverlayImage.fromResource(R.drawable.marker_icon)


    setOnClickListener {


    ...


    true


    }


    map = naverMap


    }


    ੉߮౟ী ؀ೠ ࠗ࠙੉ ੓Ҋ

    View Slide

  104. // Display
    ݃ழ
    val marker = Marker().apply {


    captionText = "caption"


    position = LatLng(..., ...)


    icon = OverlayImage.fromResource(R.drawable.marker_icon)


    setOnClickListener {


    ...


    true


    }


    map = naverMap


    }


    ֎੉ߡ૑بী ࠁৈ઱ח ࠗ࠙੉ ੓णפ׮

    View Slide

  105. ݃ழ
    Marker(


    captionText = "caption",


    position = LatLng(..., ...),


    icon = OverlayImage.fromResource(R.drawable.marker_icon),


    onClick = {


    ...


    true


    },


    )


    Composeীࢲח ੉۠ ഋక۽ ࢎਊೡ ࣻ ੓ѱ ٜ݅঻णפ׮

    View Slide

  106. ݃ழ
    Marker(


    captionText = "caption",


    position = LatLng(..., ...),


    icon = OverlayImage.fromResource(R.drawable.marker_icon),


    onClick = {


    ...


    true


    },


    )


    @Composable


    fun Marker(


    captionText: String? = ...,


    position: LatLng = ...,


    icon: OverlayImage = ...,


    onClick: (Marker) -> Boolean = { false },


    ) { ... }
    ੉੹୊ۢ ComposeNodeܳ ഝਊ೧ࢲ

    Marker Composableਸ ٜ݅ݶ ؾפ׮

    View Slide

  107. ComposeNodeܳ ࢤࢿೞৈ ࢚కܳ সؘ੉౟. MapApplierܳ ా೧ ૑بী ಴दೠ׮.

    Marker
    NaverMap
    Marker ҳઑ
    MapApplier
    MarkerNode
    Marker
    onClick
    getMap()
    MapNode
    MapNode
    MapNode
    = ComposeNode
    NaverMap
    @Composable
    @Composable
    Link: h
    tt
    ps://github.com/fornewid/naver-map-compose/.../com/naver/maps/map/compose/Marker.kt

    View Slide

  108. ݃ழ
    Marker(


    captionText = "caption",


    position = LatLng(..., ...),


    icon = OverlayImage.fromResource(R.drawable.marker_icon),


    onClick = {


    ...


    true


    },


    )


    // map = naverMap ?
    NaverMapী ಴द೧઱ח ࠗ࠙਷

    ௏٘۽ যڌѱ ಴അೞݶ જਸөਃ?

    View Slide

  109. ݃ழ
    NaverMap(...) {


    Marker(


    captionText = "caption",


    position = LatLng(..., ...),


    icon = OverlayImage.fromResource(R.drawable.marker_icon),


    onClick = {


    ...


    true


    },


    )


    }
    ੉ۧѱ NaverMap Composable ղࠗী

    Child Composable۽ ࢶ঱೧઱ݶ ؾפ׮

    View Slide

  110. Example: ӓ੢ ݃ழ ҙܻೞӝ
    var markers by remember { mutableStateOf>(emptyList()) }


    LaunchedEffect(theaters) {


    val naverMap = mapView.awaitMap()


    markers.forEach { it.map = null }


    markers = theaters.map { theater ->


    Marker().apply {


    ...


    map = naverMap


    }


    }


    } ష੉ ೐۽ં౟ীࢲ ੘ࢿ೮؍ ௏٘ח

    UIܳ Side-effect۽ ҙܻೞח ו՝੉঻חؘਃ

    View Slide

  111. Example: ӓ੢ ݃ழ ҙܻೞӝ
    NaverMap(...) {


    theaters.forEach { theater ->


    Marker(


    ...


    )


    }


    }
    ੉ઁ ખ ؊ ࢶ঱ഋ UIী оӰѱ ݃ழܳ ಴दೡ ࣻ ੓णפ׮

    View Slide

  112. ݃ழ ࠁৈ઱ӝ ✅
    NaverMap {


    Marker(


    position = LatLng(..., ...),


    ...


    onClick = {


    /* Do domething*/


    true


    },


    )


    }

    View Slide

  113. Use Case 5:

    ஠ݫۄ ੉ز
    ࢎਊ੗о ૑بܳ झ௼܀೧ࢲ ஠ݫۄ ਤ஖ܳ ੉زೠ ׮਺

    View Slide

  114. Use Case 5:

    ஠ݫۄ ੉ز
    ചݶਸ ഥ੹ೞݶ

    View Slide

  115. Use Case 5:

    ஠ݫۄ ੉ز
    ഥ੹ द, ஠ݫۄ ਤ஖о ୡӝചغח ޙઁ!

    View Slide

  116. Use Case 5:

    ஠ݫۄ ੉ز
    Con
    fi
    g Changes द੼ী ࢚కܳ ਬ૑ೡ ࣻ হਸө?
    rememberSaveable

    View Slide

  117. ஠ݫۄী ؀ೠ ࢚క, ੉߮౟ܳ ҙܻೞח State Holder.
    MapUpdater
    CameraPositionState
    NaverMap MapPropertiesNode
    CameraPositionState
    CameraPosition NaverMap Listeners
    Composition

    only
    State Holder
    Event
    New CameraPosition
    @Composable
    Compositionীࢲ݅ ݃૑݄ী ੷੢ೠ ਤ஖۽ ஠ݫۄܳ ੉ز೤פ׮

    View Slide

  118. ࢚క ҙܻ: State Holder
    Link: h
    tt
    ps://developer.android.com/jetpack/compose/state#state-holder-source-of-truth
    ࠂ੟ೠ UI੄ ࢚క৬ UI ۽૒ਸ ನೣೞח ௿ېझ. Composable੄ ࠂ੟ࢿਸ ઴ৈળ׮.

    View Slide

  119. internal class MapPropertiesNode(


    val map: NaverMap,


    cameraPositionState: CameraPositionState,


    ) : MapNode {


    init {


    cameraPositionState.setMap(map)


    }


    private val cameraIdleListener = NaverMap.OnCameraIdleListener {


    cameraPositionState.rawPosition = map.cameraPosition


    }


    private val cameraChangeListener = NaverMap.OnCameraChangeListener { _, _ ->


    cameraPositionState.rawPosition = map.cameraPosition


    }


    override fun onAttached() {


    map.addOnCameraIdleListener(cameraIdleListener)


    map.addOnCameraChangeListener(cameraChangeListener)


    }


    }
    MapPropertiesNode ௏٘ܳ ࢓ಝࠇद׮

    View Slide

  120. internal class MapPropertiesNode(


    val map: NaverMap,


    cameraPositionState: CameraPositionState,


    ) : MapNode {


    init {


    cameraPositionState.setMap(map)


    }


    private val cameraIdleListener = NaverMap.OnCameraIdleListener {


    cameraPositionState.rawPosition = map.cameraPosition


    }


    private val cameraChangeListener = NaverMap.OnCameraChangeListener { _, _ ->


    cameraPositionState.rawPosition = map.cameraPosition


    }


    override fun onAttached() {


    map.addOnCameraIdleListener(cameraIdleListener)


    map.addOnCameraChangeListener(cameraChangeListener)


    }


    } cameraPositionStateী NaverMap ё୓ܳ ࢸ੿೧઱Ҋ

    Listenerܳ ా೧, ߄Ո ஠ݫۄ ਤ஖о ٜযয়ݶ rawPositionਸ সؘ੉౟ ೧સפ׮

    View Slide

  121. public class CameraPositionState(position: CameraPosition = ...) {


    internal var rawPosition by mutableStateOf(position)


    public var position: CameraPosition


    get() = rawPosition


    set(value) { ... }


    internal fun setMap(map: NaverMap?) {


    this.map = map


    if (map != null) {


    map.moveCamera(CameraUpdate.toCameraPosition(position))


    }


    }


    }
    NaverMap੉ ׮द ࢸ੿غݶ(=Composition), ݃૑݄ ਤ஖۽ ੉زೠ׮.
    NaverMap੉ ׮द ࢸ੿غݶ Compositionਵ۽ ౸ױೞҊ,

    ݃૑݄ ਤ஖۽ ੉زदெ઱ח ୊ܻо ٜযо ੓णפ׮

    View Slide

  122. @Composable


    public inline fun rememberCameraPositionState(


    key: String? = null,


    crossinline init: CameraPositionState.() -> Unit = {},


    ): CameraPositionState = rememberSaveable(


    key = key,


    saver = CameraPositionState.Saver


    ) {


    CameraPositionState().apply(init)


    }


    public val Saver: Saver = Saver(


    save = { it.position },


    restore = { CameraPositionState(it) }


    )
    Con
    fi
    g Changes द੼ী

    ݃૑݄ ࢚కܳ ੷੢೧ળ׮.
    Link: h
    tt
    ps://developer.android.com/jetpack/compose/state#restore-ui-state
    rememberSaveableܳ ੉ਊ೧ࢲ Config Changes द੼ী ݃૑݄ ࢚కܳ ੷੢/ࠂҳ ೧સפ׮

    View Slide

  123. val cameraPositionState = rememberCameraPositionState()


    NaverMap(cameraPositionState = cameraPositionState)


    val coroutineScope = rememberCoroutineScope()


    coroutineScope.launch {


    cameraPositionState.animate(


    CameraUpdate.scrollTo(position),


    animation = CameraAnimation.Fly,


    durationMs = 1_000


    )


    }
    ஠ݫۄ ੉ز ✅
    ࢎਊೡ ٸח NaverMap Composableী State Holderܳ ੹׳೧઱Ҋ

    ੉زೡ ٸח ௏ܖ౯ਸ ੉ਊೞৈ গפݫ੉࣌ ୊ܻܳ ೡ ࣻ ੓णפ׮

    View Slide

  124. ● Composable Lifecycle

    - Composition

    - Recomposition

    ● Architecture

    - Unidirectional Data Flow

    - State Holder

    ● State

    - remember

    - rememberSaveable

    - mutableStateOf

    - @Immutable
    Keywords
    ● Side-effects

    - LaunchedEffect

    - DisposableEffect

    - rememberCoroutineScope

    - rememberUpdatedState

    ● Interoperability APIs

    - AndroidView

    - setContent

    ● Custom Composition

    - rememberCompositionContext

    - Composition

    - Applier, ComposeNode

    View Slide

  125. ೙ਃೠ ӝמ੉ ੓ਵݶ, ੉गܳ ١۾೧઱ࣁਃ!
    👆

    View Slide

  126. Q & A

    хࢎ೤פ׮!

    View Slide