Google Map + ViewPager2滑动冲突
Google Map + ViewPager2 swiping conflict
有人处理 ViewPager2 + Fragments + GoogleMap 吗?
我 运行 遇到了一个非常有趣的问题,在我的 viewpager 中,我有两个选项卡,每个选项卡都呈现片段。
其中一个碎片生成 google 地图。
当我尝试在该地图上移动时,我得到了 viewpager2 试图左右滑动的效果,这真的很烦人,所以我解决它的方法并不是很好。
我通过构造函数将 viewpager 暴露给其中一个片段,并触发了对顶部 viewpager 的回调 activity。
我在那里执行了一个简单的 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)
}
}
尝试覆盖 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" />
如果你想检测用户在屏幕左边框或右边框附近启动滚动以滑动 viewpager,在这种情况下你可以将 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)
}
我通过在地图上方添加一个哑视图解决了这个问题,如下所示:
<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 + Fragments + GoogleMap 吗?
我 运行 遇到了一个非常有趣的问题,在我的 viewpager 中,我有两个选项卡,每个选项卡都呈现片段。
其中一个碎片生成 google 地图。
当我尝试在该地图上移动时,我得到了 viewpager2 试图左右滑动的效果,这真的很烦人,所以我解决它的方法并不是很好。
我通过构造函数将 viewpager 暴露给其中一个片段,并触发了对顶部 viewpager 的回调 activity。
我在那里执行了一个简单的 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)
}
}
尝试覆盖 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" />
如果你想检测用户在屏幕左边框或右边框附近启动滚动以滑动 viewpager,在这种情况下你可以将 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)
}
我通过在地图上方添加一个哑视图解决了这个问题,如下所示:
<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
}