(3) 探索 Jetpack Compose × 高德地图
本文最后更新于 84 天前,其中的信息可能已经有所发展或是发生改变。

如标题所见,目前高德官方并未适配 Jetpack Compose,因此如果想要在 Compose 中使用高德地图的话,就必须自行适配原来的 xml 布局。

引入依赖

以下是高德官方给出的 gradle 依赖:

Android Studio 配置工程-创建工程-开发指南-Android 地图SDK|高德地图API (amap.com)

SDK引入代码
3D地图compile ‘com.amap.api:3dmap:latest.integration’
2D地图compile ‘com.amap.api:map2d:latest.integration’
导航compile ‘com.amap.api:navi-3dmap:latest.integration’
搜索compile ‘com.amap.api:search:latest.integration’
定位compile ‘com.amap.api:location:latest.integration’
导航 SDK 已经包含地图 SDK,请不要重复引入。

但是经过测试,如果你想同时使用地图和搜索,引入导航和搜索 SDK 会发生依赖冲突,寻求官方的解决办法之后,只需要引入下面的依赖即可:

implementation("com.amap.api:navi-3dmap-location-search:10.0.800_3dmap10.0.800_loc6.4.5_sea9.7.2")

应用权限

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>

生命周期

先看看官方给出的实例:


public class MainActivity extends Activity {
  MapView mMapView = null;
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.activity_main);
    //获取地图控件引用
    mMapView = (MapView) findViewById(R.id.map);
    //在activity执行onCreate时执行mMapView.onCreate(savedInstanceState),创建地图
    mMapView.onCreate(savedInstanceState);
  }
  @Override
  protected void onDestroy() {
    super.onDestroy();
    //在activity执行onDestroy时执行mMapView.onDestroy(),销毁地图
    mMapView.onDestroy();
  }
 @Override
 protected void onResume() {
    super.onResume();
    //在activity执行onResume时执行mMapView.onResume (),重新绘制加载地图
    mMapView.onResume();
    }
 @Override
 protected void onPause() {
    super.onPause();
    //在activity执行onPause时执行mMapView.onPause (),暂停地图的绘制
    mMapView.onPause();
    }
 @Override
 protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    //在activity执行onSaveInstanceState时执行mMapView.onSaveInstanceState (outState),保存地图当前的状态
    mMapView.onSaveInstanceState(outState);
  } 
}

观察发现,上面的 MapView 和 Activity 一样,也有对应的生命周期事件。

现在解决第一个问题,在 Compose 中监听生命周期并且调用 MapView 的相关方法。

首先由 LocalLifecycleOwner.current.lifecycle 可以得到当前的生命周期,然后添加观察者来感知生命周期的变化。

于是可以得到下面的关于 MapView 的拓展函数:

private fun MapView.lifecycleObserver(
    onCreate: (() -> Unit)? = null,
    onResume: (() -> Unit)? = null,
    onPause: (() -> Unit)? = null,
    onDestroy: (() -> Unit)? = null,
): LifecycleObserver {
    return LifecycleEventObserver { _, event ->
        when(event) {
            Lifecycle.Event.ON_CREATE -> {
                this.onCreate(Bundle())
                onCreate?.invoke()
            }
            Lifecycle.Event.ON_RESUME -> {
                this.onResume()
                onResume?.invoke()
            }
            Lifecycle.Event.ON_PAUSE -> {
                this.onPause()
                onPause?.invoke()
            }
            Lifecycle.Event.ON_DESTROY -> {
                this.map.setOnCameraChangeListener(null)
                this.onDestroy()
                onDestroy?.invoke()
            }
            else -> {}
        }
    }
}
private fun MapView.componentCallbacks(
    onLowMemory: (() -> Unit)? = null,
): ComponentCallbacks {
    return object : ComponentCallbacks {
        override fun onConfigurationChanged(newConfig: Configuration) {}
        override fun onLowMemory() {
            this@componentCallbacks.onLowMemory()
            onLowMemory?.invoke()
        }
    }
}

接下来可以通过 Compose 提供的 AndroidView 组件来使用原生的 View:

val context = LocalContext.current
val mapView = remember {
    MapView(context, AMapOptions()).apply {
        onCreate(null)
    }
}
AndroidView(modifier = Modifier, factory = { mapView })

组件封装

写到这里,好像就可以用了,但是别忘了我们还没有给 MapView 绑定生命周期事件:

@Composable
private fun AMapViewLifecycle(mapView: MapView) {
    val context = LocalContext.current
    val lifecycle = LocalLifecycleOwner.current.lifecycle
    DisposableEffect(context, lifecycle, mapView) {
        val mapLifecycleObserver = mapView.lifecycleObserver()
        val callbacks = mapView.componentCallbacks()
        lifecycle.addObserver(mapLifecycleObserver)
        context.registerComponentCallbacks(callbacks)
        onDispose {
            lifecycle.removeObserver(mapLifecycleObserver)
            context.unregisterComponentCallbacks(callbacks)
        }
    }
}

然后把这个组件结合起来,就得到最终封装好的高德地图组件了:

@Composable
fun AMapView(modifier: Modifier = Modifier) {
    val context = LocalContext.current
    val mapView = remember {
        MapView(context, AMapOptions()).apply {
            onCreate(null)
        }
    }
    AndroidView(modifier = modifier, factory = { mapView })
    AMapViewLifecycle(mapView = mapView)
}

组件通信

现在还有最后一个问题,在这个组件内部怎么与外部进行交流呢?

最常用的方法应该是自定义一个状态类和 remember 函数,然后传递给这个组件,下面来实践一下吧:

状态类

class GenericMapViewState(
    initialPosition: LatLngPosition = LatLngPosition(),
    @FloatRange(from = 0.0, to = 1.0)
    initialZoom: Float = 0.6f
) {
    private val _centerPosition: MutableState<LatLngPosition> = mutableStateOf(initialPosition)
    private val _zoom: MutableState<Float> = mutableFloatStateOf(initialZoom)
    val centerPosition: State<LatLngPosition> get() = _centerPosition
    val zoom: State<Float> get() = _zoom

    fun updateCenterPosition(position: LatLngPosition) {
        _centerPosition.value = position
    }

    fun updateZoom(zoom: Float) {
        _zoom.value = zoom
    }
}

remember

下面给出一种可以保存状态的方法,可以避免切换页面后地图又回到最初的状态,但需要配合组件使用:

@Composable
fun rememberSavableGenericMapViewState(
    initialPosition: LatLngPosition = LatLngPosition(),
    initialZoom: Float = 0.6f
): GenericMapViewState {
    return rememberSaveable(
        saver = Saver(
            save = { it.toJSONString() },
            restore = { it.toExplicitObject<String, GenericMapViewState?>() }
        )
    ) { GenericMapViewState(
        initialPosition = initialPosition,
        initialZoom = initialZoom
    ) }
}

这里的 toJSONString() toExplicitObject() 是我自己用 fastjson2 封装的函数:

fun Any?.toJSONString(): String = JSON.toJSONString(this)

inline fun <T : CharSequence, reified R> T.toExplicitObject(): R = JSON.parseObject(this.toString(), R::class.java)

组件内部监听

现在来改造一下刚才封装好的组件,首先添加一个 state 参数:

@Composable
fun AMapView(
    modifier: Modifier = Modifier,
    state: GenericMapViewState
) {
    val context = LocalContext.current
    val mapView = remember {
        MapView(context, AMapOptions()).apply {
            onCreate(null)
        }
    }
    AndroidView(modifier = modifier, factory = { mapView })
    AMapViewLifecycle(mapView = mapView)
}

接着在地图初始化的时候,将摄像机指向 GenericMapViewState 中保存的中心点位置:

@Composable
fun AMapView(
    modifier: Modifier = Modifier,
    state: GenericMapViewState
) {
    val context = LocalContext.current
    val mapView = remember {
        MapView(context, AMapOptions()).apply {
            onCreate(null)
            map.apply {
                // Initialize
                moveCamera(CameraUpdateFactory.changeLatLng(state.centerPosition.value.fitAMap()))
                moveCamera(CameraUpdateFactory.zoomTo(getRealZoom(state.zoom.value)))
            }
        }
    }
    AndroidView(modifier = modifier, factory = { mapView })
    AMapViewLifecycle(mapView = mapView)
}

由于 GenericMapViewState 中保存的是 State<T> 变量,因此你也可以直接在组件内部和外部监听它们值的变化,从而实现共享状态。

总结

要在 Compose 中封装地图组件其实并不复杂,如果有任何错误,欢迎指出。

未经允许禁止转载本站内容,经允许转载后请严格遵守CC-BY-NC-ND知识共享协议4.0,代码部分则采用GPL v3.0协议
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇