android - 将 mapbox 地图附加到 livedata
问题描述
我的目标是使用 MapBox 并将视图连接到包含 GeoJSON 多边形的数据集。我已经能够通过 LiveData<List> 从数据集中获取更改,其中 Case 其中包含 GeoJSON 区域。
现在我想从 ViewModel 监听这个数据集的变化,并将结果绑定到地图中的特定层。我找不到任何有关如何执行此操作的示例,因为大多数示例不使用数据绑定或带有 LiveData 的 ViewModel。
data class Case (
var id : String,
var feature : Feature,
var note : String?
)
注意:Feature 是实现 GeoJSON 的 MapBox.Feature,它是一个多边形。
我已经能够听到案例列表上的变化,但还没有弄清楚如何连接剩余的部分。你能帮我完成这里的步骤,或者指出一个很好的例子吗?一个可能的答案可能是一些解释以及一些伪代码。
<com.mapbox.mapboxsdk.maps.MapView
android:id="@+id/MyMapView"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@id/jobSelectionJobTitle"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:mapbox_cameraZoom="12" />
还有一个额外的问题:我不确定在活动中投入多少以及在模型视图中投入什么。
解决方案
此示例使用:
- 数据绑定
- 绑定适配器
- 实时数据
- Kotlin 协程
com.mapbox.mapboxsdk.style.sources.GeoJsonSource
com.mapbox.mapboxsdk.style.layers.Layer
目的是将对象绑定到via 数据绑定GeoJsonSource
和. 还提供一种通过“id”删除层的机制。我故意将 LiveData 的实现留空,因为我不知道如何提供这些数据,但提供了实现这一点的机制。此机制可根据您的需要进行调整。Layer
MapView
LiveData
数据类:
data class GeoJsonLayer(val source: GeoJsonSource, val layer: Layer)
绑定适配器(放置在单独的 kotlin 文件中以供全局访问)
@BindingAdapter(value = ["addGeoJsonLayers", "addCoroutineScope"], requireAll = true)
fun MapView.addGeoJsonLayers(layers: List<GeoJsonLayer>?, scope: CoroutineScope?) {
layers?.let { list ->
scope?.launch {
getStyle()?.run {
list.filter { getSource(it.source.id) == null }
.forEach { jsonLayer ->
addSource(jsonLayer.source)
addLayer(jsonLayer.layer)
}
}
}
}
}
@BindingAdapter(value = ["removeLayers", "removeCoroutineScope"], requireAll = true)
fun MapView.removeLayers(ids: List<String>?, scope: CoroutineScope?) {
ids?.let { list ->
scope?.launch {
getStyle()?.run {
list.forEach { id ->
removeLayer(id)
removeSource(id)
}
}
}
}
}
协程扩展函数(放置在单独的 kotlin 文件中以供全局访问)
suspend fun MapView.getMap(): MapboxMap = suspendCoroutine { cont -> getMapAsync { cont.resume(it) } }
suspend fun MapView.getStyle(): Style? = getMap().style
示例 ViewModel 合同
abstract class MapViewModel : ViewModel() {
abstract val addGeoJsonLayers: LiveData<List<GeoJsonLayer>>
abstract val removeGeoJsonLayers: LiveData<List<String>>
}
XML 布局( layout/map_view.xml
)
<data>
<import type="android.view.View" />
<variable
name="mapVm"
type="your.package.MapViewModel" />
<variable
name="scope"
type="kotlinx.coroutines.CoroutineScope" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/constraintLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.mapbox.mapboxsdk.maps.MapView
android:id="@+id/map_view"
android:layout_width="0dp"
android:layout_height="0dp"
app:addCoroutineScope="@{scope}"
app:addGeoJsonLayers="@{mapVm.addGeoJsonLayers}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:removeCoroutineScope="@{scope}"
app:removeLayers="@{mapVm.removeGeoJsonLayers}" />
</androidx.constraintlayout.widget.ConstraintLayout>
示例活动
class MapActivity : AppCompatActivity() {
private lateinit var binding: MapViewBinding
private val viewModel: ConcreteMapViewModel by viewModels() // implementation of MapViewModel
override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
super.onCreate(savedInstanceState, persistentState)
binding = MapViewBinding.inflate(layoutInflater).apply {
setContentView(root)
lifecycleOwner = lifecycleOwner
scope = lifecycleOwner.lifecycleScope
mapView.onCreate(savedInstanceState)
lifecycleOwner.lifecycleScope.launch {
mapView.getMap().setStyle(Style.MAPBOX_STREETS)
}
mapVm = viewModel
}
}
override fun onStart() {
super.onStart()
binding.mapView.onStart()
}
override fun onResume() {
super.onResume()
binding.mapView.onResume()
}
override fun onPause() {
super.onPause()
binding.mapView.onPause()
}
override fun onStop() {
super.onStop()
binding.mapView.onStop()
}
override fun onDestroy() {
super.onDestroy()
binding.mapView.onDestroy()
}
override fun onLowMemory() {
super.onLowMemory()
binding.mapView.onLowMemory()
}
}
Gradle 文件设置(kts - kotlin DSL):
plugins {
id("com.android.application")
id("kotlin-android")
id("kotlin-kapt")
}
android {
compileSdk = 30
buildToolsVersion = "30.0.3"
defaultConfig {
applicationId = "com.package.name"
minSdk = 24
targetSdk = 30
versionCode = 1
versionName = "1.0.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
named("release") {
isMinifyEnabled = false
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
kotlinOptions {
jvmTarget = "1.8"
}
buildFeatures {
dataBinding = true
}
}
dependencies {
// Kotlin
implementation("org.jetbrains.kotlin:kotlin-stdlib:1.5.10")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.0")
// AndroidX
val lifecycleVersion = "2.3.1"
val navVersion = "2.3.5"
implementation("androidx.core:core-ktx:1.5.0")
implementation("androidx.appcompat:appcompat:1.3.0")
implementation("androidx.constraintlayout:constraintlayout:2.0.4")
implementation("androidx.lifecycle:lifecycle-livedata-ktx:$lifecycleVersion")
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycleVersion")
// MapBox
implementation("com.mapbox.mapboxsdk:mapbox-android-sdk:9.6.1")
implementation("com.google.code.gson:gson:2.8.7")
// Logging
implementation("com.jakewharton.timber:timber:4.7.1")
testImplementation("junit:junit:4.13.2")
}
推荐阅读
- amazon-web-services - 云端 https 请求的 Aws S3 重定向规则问题
- xaml - Xamarin.Forms 为 WPF 嵌入本机控件
- android - Recyclerview中POJO和Retrieve的房间插入列表
- grails - OR 条件 Grails 嵌套对象
- android - 方法不会从其超类错误中覆盖方法
- python - 使用子字符串打印出多个列表
- nivo-slider - Nivo 滑块为每个图像提供不同的过渡效果
- c - C函数查找彼此不同的行
- java - 更改由 Java 父进程启动的“C”子进程的进程名称
- jquery - 单击锚标记时如何在服务器端获取href值