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

相关文章

Spring常见错误之Web嵌套对象校验失效解决办法

《Spring常见错误之Web嵌套对象校验失效解决办法》:本文主要介绍Spring常见错误之Web嵌套对象校验失效解决的相关资料,通过在Phone对象上添加@Valid注解,问题得以解决,需要的朋... 目录问题复现案例解析问题修正总结  问题复现当开发一个学籍管理系统时,我们会提供了一个 API 接口去

深入理解Redis大key的危害及解决方案

《深入理解Redis大key的危害及解决方案》本文主要介绍了深入理解Redis大key的危害及解决方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着... 目录一、背景二、什么是大key三、大key评价标准四、大key 产生的原因与场景五、大key影响与危

Python 中 requests 与 aiohttp 在实际项目中的选择策略详解

《Python中requests与aiohttp在实际项目中的选择策略详解》本文主要介绍了Python爬虫开发中常用的两个库requests和aiohttp的使用方法及其区别,通过实际项目案... 目录一、requests 库二、aiohttp 库三、requests 和 aiohttp 的比较四、requ

SpringBoot项目启动后自动加载系统配置的多种实现方式

《SpringBoot项目启动后自动加载系统配置的多种实现方式》:本文主要介绍SpringBoot项目启动后自动加载系统配置的多种实现方式,并通过代码示例讲解的非常详细,对大家的学习或工作有一定的... 目录1. 使用 CommandLineRunner实现方式:2. 使用 ApplicationRunne

使用IntelliJ IDEA创建简单的Java Web项目完整步骤

《使用IntelliJIDEA创建简单的JavaWeb项目完整步骤》:本文主要介绍如何使用IntelliJIDEA创建一个简单的JavaWeb项目,实现登录、注册和查看用户列表功能,使用Se... 目录前置准备项目功能实现步骤1. 创建项目2. 配置 Tomcat3. 项目文件结构4. 创建数据库和表5.

Python项目打包部署到服务器的实现

《Python项目打包部署到服务器的实现》本文主要介绍了PyCharm和Ubuntu服务器部署Python项目,包括打包、上传、安装和设置自启动服务的步骤,具有一定的参考价值,感兴趣的可以了解一下... 目录一、准备工作二、项目打包三、部署到服务器四、设置服务自启动一、准备工作开发环境:本文以PyChar

多模块的springboot项目发布指定模块的脚本方式

《多模块的springboot项目发布指定模块的脚本方式》该文章主要介绍了如何在多模块的SpringBoot项目中发布指定模块的脚本,作者原先的脚本会清理并编译所有模块,导致发布时间过长,通过简化脚本... 目录多模块的springboot项目发布指定模块的脚本1、不计成本地全部发布2、指定模块发布总结多模

SpringBoot项目删除Bean或者不加载Bean的问题解决

《SpringBoot项目删除Bean或者不加载Bean的问题解决》文章介绍了在SpringBoot项目中如何使用@ComponentScan注解和自定义过滤器实现不加载某些Bean的方法,本文通过实... 使用@ComponentScan注解中的@ComponentScan.Filter标记不加载。@C

Xshell远程连接失败以及解决方案

《Xshell远程连接失败以及解决方案》本文介绍了在Windows11家庭版和CentOS系统中解决Xshell无法连接远程服务器问题的步骤,在Windows11家庭版中,需要通过设置添加SSH功能并... 目录一.问题描述二.原因分析及解决办法2.1添加ssh功能2.2 在Windows中开启ssh服务2

Redis连接失败:客户端IP不在白名单中的问题分析与解决方案

《Redis连接失败:客户端IP不在白名单中的问题分析与解决方案》在现代分布式系统中,Redis作为一种高性能的内存数据库,被广泛应用于缓存、消息队列、会话存储等场景,然而,在实际使用过程中,我们可能... 目录一、问题背景二、错误分析1. 错误信息解读2. 根本原因三、解决方案1. 将客户端IP添加到Re