运项目难点之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

相关文章

Python中将嵌套列表扁平化的多种实现方法

《Python中将嵌套列表扁平化的多种实现方法》在Python编程中,我们常常会遇到需要将嵌套列表(即列表中包含列表)转换为一个一维的扁平列表的需求,本文将给大家介绍了多种实现这一目标的方法,需要的朋... 目录python中将嵌套列表扁平化的方法技术背景实现步骤1. 使用嵌套列表推导式2. 使用itert

Java 线程安全与 volatile与单例模式问题及解决方案

《Java线程安全与volatile与单例模式问题及解决方案》文章主要讲解线程安全问题的五个成因(调度随机、变量修改、非原子操作、内存可见性、指令重排序)及解决方案,强调使用volatile关键字... 目录什么是线程安全线程安全问题的产生与解决方案线程的调度是随机的多个线程对同一个变量进行修改线程的修改操

深度解析Java项目中包和包之间的联系

《深度解析Java项目中包和包之间的联系》文章浏览阅读850次,点赞13次,收藏8次。本文详细介绍了Java分层架构中的几个关键包:DTO、Controller、Service和Mapper。_jav... 目录前言一、各大包1.DTO1.1、DTO的核心用途1.2. DTO与实体类(Entity)的区别1

全面解析MySQL索引长度限制问题与解决方案

《全面解析MySQL索引长度限制问题与解决方案》MySQL对索引长度设限是为了保持高效的数据检索性能,这个限制不是MySQL的缺陷,而是数据库设计中的权衡结果,下面我们就来看看如何解决这一问题吧... 目录引言:为什么会有索引键长度问题?一、问题根源深度解析mysql索引长度限制原理实际场景示例二、五大解决

如何在Spring Boot项目中集成MQTT协议

《如何在SpringBoot项目中集成MQTT协议》本文介绍在SpringBoot中集成MQTT的步骤,包括安装Broker、添加EclipsePaho依赖、配置连接参数、实现消息发布订阅、测试接口... 目录1. 准备工作2. 引入依赖3. 配置MQTT连接4. 创建MQTT配置类5. 实现消息发布与订阅

springboot项目打jar制作成镜像并指定配置文件位置方式

《springboot项目打jar制作成镜像并指定配置文件位置方式》:本文主要介绍springboot项目打jar制作成镜像并指定配置文件位置方式,具有很好的参考价值,希望对大家有所帮助,如有错误... 目录一、上传jar到服务器二、编写dockerfile三、新建对应配置文件所存放的数据卷目录四、将配置文

怎么用idea创建一个SpringBoot项目

《怎么用idea创建一个SpringBoot项目》本文介绍了在IDEA中创建SpringBoot项目的步骤,包括环境准备(JDK1.8+、Maven3.2.5+)、使用SpringInitializr... 目录如何在idea中创建一个SpringBoot项目环境准备1.1打开IDEA,点击New新建一个项

SpringSecurity显示用户账号已被锁定的原因及解决方案

《SpringSecurity显示用户账号已被锁定的原因及解决方案》SpringSecurity中用户账号被锁定问题源于UserDetails接口方法返回值错误,解决方案是修正isAccountNon... 目录SpringSecurity显示用户账号已被锁定的解决方案1.问题出现前的工作2.问题出现原因各

javax.net.ssl.SSLHandshakeException:异常原因及解决方案

《javax.net.ssl.SSLHandshakeException:异常原因及解决方案》javax.net.ssl.SSLHandshakeException是一个SSL握手异常,通常在建立SS... 目录报错原因在程序中绕过服务器的安全验证注意点最后多说一句报错原因一般出现这种问题是因为目标服务器

springboot项目中整合高德地图的实践

《springboot项目中整合高德地图的实践》:本文主要介绍springboot项目中整合高德地图的实践,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一:高德开放平台的使用二:创建数据库(我是用的是mysql)三:Springboot所需的依赖(根据你的需求再