运项目难点之ScrollView中嵌套百度地图(BaiduMap)的解决方案

本文主要是介绍运项目难点之ScrollView中嵌套百度地图(BaiduMap)的解决方案,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

由于产品的需求,有时候不得不在ScrollView中嵌套百度地图(BaiduMap)。但是,嵌套之后会存在一些问题,两个比较突出的问题是:1)ScrollView中事件处理与BaiduMap存在冲突。2)在BaiduMap随着ScrollView拖动的时候,存在黑影问题。很多人遇到过这两个问题,也比较棘手,所以希望百度能给出官方的解决方案。下面说说我的处理办法。

1)ScrollView中事件处理与BaiduMap存在冲突

想要了解产生事件冲突的原因,就必须明白安卓的事件传递与处理机制。http://blog.csdn.net/theone10211024/article/details/43270455 这片文章是我见过讲的最好的。还不明白的同学不妨移步这里。这里我只简单说一下传递流程:

Events->Activity.dispatchTouchEvent()->(顶层)ViewGroup.dispatchTouchEvent()->(顶层)ViewGroup.onInterceptTouchEvent()->childView1.dispatchTouchEvent()->childView1.OnTouchListener.OnTouch(如果定义了)->childView1.onTouchEvent()->childView2.....->childView n.....->(顶层)ViewGroup.onTouchListener.onTouch()(如果定义了)->(顶层)ViewGroup.onTouchEvent()->Activity.onTouchEvent();

中间任何一步如果事件被消费了,就会停止传递。

下面,我们来看看ScrollView的源代码

[html]  view plain copy
  1. @Override  
  2.   public boolean onInterceptTouchEvent(MotionEvent ev) {  
  3.       /*  
  4.        * This method JUST determines whether we want to intercept the motion.  
  5.        * If we return true, onMotionEvent will be called and we do the actual  
  6.        * scrolling there.  
  7.        */  
  8.   
  9.       /*  
  10.       * Shortcut the most recurring case: the user is in the dragging  
  11.       * state and he is moving his finger.  We want to intercept this  
  12.       * motion.  
  13.       */  
  14.       final int action = ev.getAction();  
  15.       if ((action == MotionEvent.ACTION_MOVE) && (mIsBeingDragged)) {  
  16.           return true;  
  17.       }  
  18.   
  19.       switch (action & MotionEvent.ACTION_MASK) {  
  20.           case MotionEvent.ACTION_MOVE: {  
  21.               /*  
  22.                * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check  
  23.                * whether the user has moved far enough from his original down touch.  
  24.                */  
  25.   
  26.               /*  
  27.               * Locally do absolute value. mLastMotionY is set to the y value  
  28.               * of the down event.  
  29.               */  
  30.               final int activePointerId = mActivePointerId;  
  31.               if (activePointerId == INVALID_POINTER) {  
  32.                   // If we don't have a valid id, the touch down wasn't on content.  
  33.                   break;  
  34.               }  
  35.   
  36.               final int pointerIndex = ev.findPointerIndex(activePointerId);  
  37.               final float y = ev.getY(pointerIndex);  
  38.               final int yDiff = (int) Math.abs(y - mLastMotionY);  
  39.               if (yDiff > mTouchSlop) {  
  40.                   mIsBeingDragged = true;  
  41.                   mLastMotionY = y;  
  42.                   initVelocityTrackerIfNotExists();  
  43.                   mVelocityTracker.addMovement(ev);  
  44.                   if (mScrollStrictSpan == null) {  
  45.                       mScrollStrictSpan = StrictMode.enterCriticalSpan("ScrollView-scroll");  
  46.                   }  
  47.               }  
  48.               break;  
  49.           }  
  50.   
  51.           case MotionEvent.ACTION_DOWN: {  
  52.               final float y = ev.getY();  
  53.               if (!inChild((int) ev.getX(), (int) y)) {  
  54.                   mIsBeingDragged = false;  
  55.                   recycleVelocityTracker();  
  56.                   break;  
  57.               }  
  58.   
  59.               /*  
  60.                * Remember location of down touch.  
  61.                * ACTION_DOWN always refers to pointer index 0.  
  62.                */  
  63.               mLastMotionY = y;  
  64.               mActivePointerId = ev.getPointerId(0);  
  65.   
  66.               initOrResetVelocityTracker();  
  67.               mVelocityTracker.addMovement(ev);  
  68.               /*  
  69.               * If being flinged and user touches the screen, initiate drag;  
  70.               * otherwise don't.  mScroller.isFinished should be false when  
  71.               * being flinged.  
  72.               */  
  73.               mIsBeingDragged = !mScroller.isFinished();  
  74.               if (mIsBeingDragged && mScrollStrictSpan == null) {  
  75.                   mScrollStrictSpan = StrictMode.enterCriticalSpan("ScrollView-scroll");  
  76.               }  
  77.               break;  
  78.           }  
  79.   
  80.           case MotionEvent.ACTION_CANCEL:  
  81.           case MotionEvent.ACTION_UP:  
  82.               /* Release the drag */  
  83.               mIsBeingDragged = false;  
  84.               mActivePointerId = INVALID_POINTER;  
  85.               recycleVelocityTracker();  
  86.               if (mScroller.springBack(mScrollX, mScrollY, 0, 0, 0, getScrollRange())) {  
  87.                   invalidate();  
  88.               }  
  89.               break;  
  90.           case MotionEvent.ACTION_POINTER_UP:  
  91.               onSecondaryPointerUp(ev);  
  92.               break;  
  93.       }  
  94.   
  95.       /*  
  96.       * The only time we want to intercept motion events is if we are in the  
  97.       * drag mode.  
  98.       */  
  99.       return mIsBeingDragged;  
  100.   }  

从该函数中可以看出,当处于拖动中(BeingDragged)的时候,ScrollView.onInterceptTouchEvent()返回true。事件就被父View即ScrollView消费了。ScrollView中的BaiduMap(实际上是其中的MapView)就不会再接收到该事件了。所以,解决该问题的核心思路就是:当ScrollView处于拖动事件中,且拖动区域在BaiduMap中的时候,让事件不要被父View消费,而是交给BaiduMap处理。

解决方案:

1)重写ScrollView.onInterceptTouchEvent()函数。当发现手指在BaiduMap中时,返回false(即未被消费)其他时候交由super.onInterceptTouchEvent()处理。我没有采用这类解决方案,原因之一是不好控制手指是否在BaiduMap中,大家可以试一试

2)我们发现android给view提供了一个函数requestDisallowInterceptTouchEvent().它的定义是这样的

[html]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. Called when a child does not want this parent and its ancestors to intercept touch events with ViewGroup.onInterceptTouchEvent(MotionEvent).   
  2.   
  3. This parent should pass this call onto its parents. This parent must obey this request for the duration of the touch (that is, only clear the flag after this parent has received an up or a cancel.  
意思是当child View不想它的父view消费事件,而是传递给自己的时候,可以调用该函数说“你不要把事件消费了,传给我再处理吧”。然后,我就按照http://blog.csdn.net/catoop/article/details/14233419这个博客写的做了,具体代码如下

[html]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. //       重写onTouch()事件,在事件里通过requestDisallowInterceptTouchEvent(boolean)方法来设置父类的不可用,true表示父类的不可用  
  2.         //解决地图的touch事件和scrollView的touch事件冲突问题  
  3.         mMapView.setOnTouchListener(new View.OnTouchListener() {  
  4.               
  5.             @Override  
  6.             public boolean onTouch(View v, MotionEvent event) {  
  7.                 if(event.getAction() == MotionEvent.ACTION_UP){  
  8.                     scrollView.requestDisallowInterceptTouchEvent(false);  
  9.                 }else{  
  10.                     scrollView.requestDisallowInterceptTouchEvent(true);  
  11.                 }  
  12.                 return false;  
  13.             }  
  14.         });  

但是,当我打了断点调试的时候才发现,触发事件的时候根本没有走到MapView的onTouch()中,换句话说就是MapView.OnTouchListener.onTouch()根本没有被调用。如果你熟悉事件的传递顺序,这时候你就会猜到,MapView是继承至ViewGroup,事件一定是被MapView中的某个childView消费了才不会传递给MapView。最后我找出了消费该事件的view。最后代码改进成了这样

[html]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. 重写onTouch()事件,在事件里通过requestDisallowInterceptTouchEvent(boolean)方法来设置父类的不可用,true表示父类的不可用  
  2.     //解决地图的touch事件和scrollView的touch事件冲突问题  
[html]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. View v = mMapView.getChildAt(0);//这个view实际上就是我们看见的绘制在表面的地图图层  
[html]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. v.setOnTouchListener(new View.OnTouchListener() {  
  2.       
  3.     @Override  
  4.     public boolean onTouch(View v, MotionEvent event) {  
  5.         if(event.getAction() == MotionEvent.ACTION_UP){  
  6.             scrollView.requestDisallowInterceptTouchEvent(false);  
  7.         }else{  
  8.             scrollView.requestDisallowInterceptTouchEvent(true);  
  9.         }  
  10.         return false;  
  11.     }  
  12. });  
改进之后,顺利达到效果。

其实,当你一头雾水的时候,发现问题并解决是比较难的。当时我就反编译了百度的jar包,在半写半蒙中才找到了正确答案。

2)在BaiduMap随着ScrollView拖动的时候,存在黑影问题

据我分析,由于百度地图是用openGl绘制的,黑影可能是在拖动过程中不断重绘才导致的。其实,百度工程师是不建议在ScrollView中使用百度地图,除非你逼不得已。

这个如果非得用动态的百度map,那解决可能还得等百度工程师的佳音了 


原文地址:http://blog.csdn.net/theone10211024/article/details/44649289

这篇关于运项目难点之ScrollView中嵌套百度地图(BaiduMap)的解决方案的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1130166

相关文章

一文教你如何将maven项目转成web项目

《一文教你如何将maven项目转成web项目》在软件开发过程中,有时我们需要将一个普通的Maven项目转换为Web项目,以便能够部署到Web容器中运行,本文将详细介绍如何通过简单的步骤完成这一转换过程... 目录准备工作步骤一:修改​​pom.XML​​1.1 添加​​packaging​​标签1.2 添加

tomcat多实例部署的项目实践

《tomcat多实例部署的项目实践》Tomcat多实例是指在一台设备上运行多个Tomcat服务,这些Tomcat相互独立,本文主要介绍了tomcat多实例部署的项目实践,具有一定的参考价值,感兴趣的可... 目录1.创建项目目录,测试文China编程件2js.创建实例的安装目录3.准备实例的配置文件4.编辑实例的

找不到Anaconda prompt终端的原因分析及解决方案

《找不到Anacondaprompt终端的原因分析及解决方案》因为anaconda还没有初始化,在安装anaconda的过程中,有一行是否要添加anaconda到菜单目录中,由于没有勾选,导致没有菜... 目录问题原因问http://www.chinasem.cn题解决安装了 Anaconda 却找不到 An

Spring定时任务只执行一次的原因分析与解决方案

《Spring定时任务只执行一次的原因分析与解决方案》在使用Spring的@Scheduled定时任务时,你是否遇到过任务只执行一次,后续不再触发的情况?这种情况可能由多种原因导致,如未启用调度、线程... 目录1. 问题背景2. Spring定时任务的基本用法3. 为什么定时任务只执行一次?3.1 未启用

MySQL新增字段后Java实体未更新的潜在问题与解决方案

《MySQL新增字段后Java实体未更新的潜在问题与解决方案》在Java+MySQL的开发中,我们通常使用ORM框架来映射数据库表与Java对象,但有时候,数据库表结构变更(如新增字段)后,开发人员可... 目录引言1. 问题背景:数据库与 Java 实体不同步1.1 常见场景1.2 示例代码2. 不同操作

springboot集成Deepseek4j的项目实践

《springboot集成Deepseek4j的项目实践》本文主要介绍了springboot集成Deepseek4j的项目实践,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价... 目录Deepseek4j快速开始Maven 依js赖基础配置基础使用示例1. 流式返回示例2. 进阶

java常见报错及解决方案总结

《java常见报错及解决方案总结》:本文主要介绍Java编程中常见错误类型及示例,包括语法错误、空指针异常、数组下标越界、类型转换异常、文件未找到异常、除以零异常、非法线程操作异常、方法未定义异常... 目录1. 语法错误 (Syntax Errors)示例 1:解决方案:2. 空指针异常 (NullPoi

SpringBoot项目启动报错"找不到或无法加载主类"的解决方法

《SpringBoot项目启动报错找不到或无法加载主类的解决方法》在使用IntelliJIDEA开发基于SpringBoot框架的Java程序时,可能会出现找不到或无法加载主类com.example.... 目录一、问题描述二、排查过程三、解决方案一、问题描述在使用 IntelliJ IDEA 开发基于

使用DrissionPage控制360浏览器的完美解决方案

《使用DrissionPage控制360浏览器的完美解决方案》在网页自动化领域,经常遇到需要保持登录状态、保留Cookie等场景,今天要分享的方案可以完美解决这个问题:使用DrissionPage直接... 目录完整代码引言为什么要使用已有用户数据?核心代码实现1. 导入必要模块2. 关键配置(重点!)3.

SpringBoot项目使用MDC给日志增加唯一标识的实现步骤

《SpringBoot项目使用MDC给日志增加唯一标识的实现步骤》本文介绍了如何在SpringBoot项目中使用MDC(MappedDiagnosticContext)为日志增加唯一标识,以便于日... 目录【Java】SpringBoot项目使用MDC给日志增加唯一标识,方便日志追踪1.日志效果2.实现步