RecyclerView下的交互式MapView

问题描述 投票:0回答:1

假设我有这样的布局:

<androidx.core.widget.NestedScrollView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        >

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/recyclerView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            />

        <com.google.android.gms.maps.MapView
            android:id="@+id/mapView"
            android:layout_width="match_parent"
            android:layout_height="300dp"
            app:uiMapToolbar="false"
            />
    </LinearLayout>

</androidx.core.widget.NestedScrollView>

将满足我的需求,就是

  • 以列表的最后一项显示地图的方式在列表下显示地图
  • 让地图接收来自用户的触摸输入,以便可以平移,缩放等(没有精简模式)

Example

尽管我看到了两个问题:

  • 没有回收,这意味着呈现列表所需的内存将随着列表中项目的数量而增加。我预计清单上不会有200多个相对简单的项目,分析之后,我可能会说这不是一个大问题,但我当然希望继续回收。
  • 列表必须具有可扩展的项目,这意味着用户可以单击某些项目,然后回收者将从列表中添加/删除项目。如果展开的组中有很多项目,则测量会花费大量时间,从而在展开的动画中造成明显的刺痛,这是不可接受的。

我也尝试过使地图成为适配器的一项,但是在滚动列表时会引入很多麻烦,而且很尴尬,因为您需要将转发生命周期与地图的回收期适配器中的项的“生命周期”协调起来。

那么您如何在列表下显示地图而不丢失回收或平滑滚动?

android android-recyclerview android-mapview android-maps-v2
1个回答
0
投票

NestedScrollView解决方案存在另一个问题,即:

  • 崩溃的动画外观不正确(在项目实际折叠之前,地图遮盖了列表)

所以我最终得到了另一个使地图成为适配器项的解决方案:

  • 滚动时证明是颠簸并不是在onViewCreated中扩大地图时的问题,而不是当适配器确定在滚动期间需要地图时,这不是问题
  • 适配器必须对地图视图持有者具有可为空的引用,以便它可以懒惰地扩展视图并在两个位置返回对其的引用:适配器中的onCreateViewHolder和片段中的onViewCreated
  • 我确实必须转发onViewCreated -> onCreateonDestroyView -> onDestroyonStartonStoponResumeonPauseonSaveInstanceStateonLowMemory生命周期回调
  • 为了避免崩溃,我不得不清除onDestroyView中的引用(当分离片段后试图保存已破坏的映射的状态时)

片段代码:

private var mapView: MapView? = null

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    mapView = adapter.inflateMapView(recyclerView)
    mapView?.onCreate(savedInstanceState?.getBundle(GOOGLE_MAPS_BUNDLE))
}

override fun onDestroyView() {
    mapView?.onDestroy()
    mapView = null
    adapter.clearViewReferences()
    super.onDestroyView()
}

override fun onStart() {
    super.onStart()
    mapView?.onStart()
}

override fun onStop() {
    mapView?.onStop()
    super.onStop()
}

override fun onResume() {
    super.onResume()
    mapView?.onResume()
}

override fun onPause() {
    mapView?.onPause()
    super.onPause()
}

override fun onSaveInstanceState(outState: Bundle) {
    super.onSaveInstanceState(outState)
    if (GOOGLE_MAPS_BUNDLE !in outState.keySet()) {
        outState.putBundle(GOOGLE_MAPS_BUNDLE, Bundle())
    }
    mapView?.onSaveInstanceState(outState.getBundle(GOOGLE_MAPS_BUNDLE))
}

override fun onLowMemory() {
    super.onLowMemory()
    mapView?.onLowMemory()
}

适配器代码:

private var mapViewHolder: MapViewHolder? = null

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = when (viewType) {
    VIEW_TYPE_FOO -> FooViewHolder(parent)
    VIEW_TYPE_MAP -> getMapViewHolder(parent)
    else -> error("Unknown viewType $viewType")
}

fun inflateMapView(parent: ViewGroup): MapView = getMapViewHolder(parent).mapView

fun clearViewReferences() {
    mapViewHolder = null
}

private fun getMapViewHolder(parent: ViewGroup): MapViewHolder {
    return mapViewHolder ?: MapViewHolder(parent).also { mapViewHolder = it }
}

此外,布局使用的是MapView子类,它将在可滚动容器中工作:

override fun dispatchTouchEvent(event: MotionEvent): Boolean {
    if (event.action == MotionEvent.ACTION_DOWN && parent is ScrollingView) {
        parent.requestDisallowInterceptTouchEvent(true)
    }
    return super.dispatchTouchEvent(event)
}
© www.soinside.com 2019 - 2024. All rights reserved.