Google地图和ViewPager2滑动冲突

6

有人处理ViewPager2 + Fragments + GoogleMap相关问题吗?

我遇到了一个很有趣的问题,在我的viewpager中,我有两个选项卡,每个选项卡都会渲染片段。

其中一个片段会生成一个google地图。

当我尝试在地图上移动时,会出现viewpager2试图向左或向右滑动的效果,这真的很烦人,所以我解决它的方法不太好。

我通过构造函数将viewpager暴露给一个片段,并向顶层viewpager活动触发回调。

在那里,我使用逻辑执行了一个简单的isUserInputEnabled, 当地图处于活动状态时禁用滑动。

一次片段附加后,此回调注册并调用逻辑。

一旦片段分离,该回调就调用逻辑以重新启用。

这是一个解决方案,但我认为它不太好。还有更好的想法吗?

我认为滑动与视图重叠似乎是一个错误。

/* top activity that hosts viewpager2 and it's tabs(fragments) */
class ViewTap extends AppCompatActivity
all the good stuff about viewpager2: ViewPager2
fun registerUi(){
...TapPagerAdapter(...this)
}
override fun fireSensitivityResolver(fragment: Fragment, flag: Boolean){
 if(fragment is ViewMapLoader){
  this.mViewPager2.isUserInputEnabled = !flag
 }
}

/* callback defintion fo handling events about Tab Capale thingies */
interface TabCapableIf {
 fun fireSensitivityResolver(fragment: Fragment, flag: Boolean)
}

/* the adapter for viewpager2 */
class TapPagerAdapter(...private val vt: ViewTap) : FragmentStateAdapter(fm,lc){
override fun createFragment(position: Int): Fragment {
return when(position) {
 ....
  CROWD_FRAGMENT -> { ViewCrowd(vmf, vt) }
 }
}

/* the fragment where the recycler view shows */
class ViewCrowd(...,val vt: ViewTap) : Fragment(){
 fun subscribeUi(){
   some recycler adapter = ItemViewCrowdsAdapter(...,this)
 }
}

/* the card adapter for the recycler view , when a card is clicked transition to map view */
class ItemViewCrowdsAdapter(...,private val vc: ViewCrowd) : AdapterFragmentRecyclerView(vm) {
 override fun onBindViewHolder(holder: ItemHolder, position: Int){
  ...holder.itemView.setOnClickListener{
   ...                    fragmentTransaction.replace(R.id.layout_view_crowd_root, ViewMapLoader(...,vc.vt))
  }
 }
}

/* the map loader context that shows the map and handles adjusting the sensitivity so that viewpager2 swipe doesn't overlap with map swipe functionality. otherwise as i try swiping on the map the viewpager2 also swipes. */
class class ViewMapLoader(...,private val vt: ViewTap) : Fragment() {
 private lateinit var mTabCapableIf: TabCapableIf
 override fun onAttach(...){
  this.mTabCapableIf = this.vt
  mTabCapableIf.fireSensitivityResolver(this,true)
 }

 override fun onDetach(){
  mTabCapableIf.fireSensitivityResolver(this,false)
 }
}
3个回答

13

尝试重写 MapView 来禁止触摸事件

public class MapViewInScroll extends MapView {
public MapViewInScroll(Context context) {
    super(context);
}

public MapViewInScroll(Context context, AttributeSet attributeSet) {
    super(context, attributeSet);
}

public MapViewInScroll(Context context, AttributeSet attributeSet, int i) {
    super(context, attributeSet, i);
}

public MapViewInScroll(Context context, GoogleMapOptions googleMapOptions) {
    super(context, googleMapOptions);
}

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    getParent().requestDisallowInterceptTouchEvent(true);
    return super.dispatchTouchEvent(ev);
}

然后在你的xml布局中使用它,而不是原始的MapView。

                    <YOUR_PACKAGE.SOME_PATH_TO_VIEW.MapViewInScroll
                    android:id="@+id/map"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent" />

ViewPager2需要成为这里的第一个父级吗,还是您实际上需要遍历整个视图树以找到ViewPager2视图? - Roar Grønmo
1
嗨@Oleg Bozhko,我在xml布局上遇到了问题。我使用片段而不是MapView来显示地图,所以当我放置我的类时,它在技术上可以工作(我可以拦截触摸),但没有显示任何地图。有什么想法吗? - LM_IT
@LM_IT 你需要在你的fragment中实现MapView的生命周期,并使用getMapAsync(OnMapReadyCallback)获取它,如此处所述:https://developers.google.com/android/reference/com/google/android/gms/maps/MapView这里有一个简单的示例:https://github.com/googlemaps/android-samples/blob/main/ApiDemos/java/app/src/gms/java/com/example/mapdemo/RawMapViewDemoActivity.java - Oleg Bozhko
1
@RoarGrønmo 不需要。任何你想要的布局都可以。 - Oleg Bozhko
1
@OlegBozhko 感谢您的回答,准确地说,我已经以那种方式加载了地图。在片段中放置一个透明的ImageView,应用一个onTouch监听器并禁止父级,这样就可以完美解决问题了。transparentImageView.setOnTouchListener(new OnSwipeTouchListener(mMain) { @Override public boolean onTouch(View v, MotionEvent event) { v.getParent().requestDisallowInterceptTouchEvent(true); mapView.dispatchTouchEvent(event); return super.onTouch(v, event); } }); - LM_IT

2

如果您想检测用户是否在屏幕左侧或右侧附近启动滚动以滑动视图页面,则可以修改dispatchTouchEvent()代码(Kotlin):

private var intercept = false
private val slideBorder = 0.1 // width of the slide-border in percent of the screen width

override fun dispatchTouchEvent(ev: MotionEvent): Boolean {
    when (ev.action) {
        MotionEvent.ACTION_DOWN -> {
           intercept = ev.x > width * slideBorder && ev.x < width * (1 - slideBorder)
        }
        MotionEvent.ACTION_MOVE -> {
            parent.requestDisallowInterceptTouchEvent(intercept)                                
        }                                                                                       
    }
    return super.dispatchTouchEvent(ev)
}

1

我通过添加一个像这样的简单视图来解决了这个问题:

<FrameLayout
    android:layout_width="match_parent"
    android:layout_height="200dp">

    <FrameLayout
        android:id="@+id/mapContainer"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <FrameLayout
        android:id="@+id/mapShim"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</FrameLayout>

然后我只是在shim上监听触摸事件,将它们转发到地图容器,并使用requestDisallowInterceptTouchEvent(true)阻止父级被通知。

mapShim.setOnTouchListener { _, event ->
    mapContainer.dispatchTouchEvent(event)
    mapContainer.parent.requestDisallowInterceptTouchEvent(true)
    true
}

在这里,ViewPager2需要成为第一个父级吗?还是你实际上需要遍历整个视图树来找到ViewPager2视图? - Roar Grønmo
上述布局位于ViewPager2片段页面之一内。拦截触摸事件会禁用ViewPager2的滑动。 - Tim Autin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接