* Difficulty in project operations: Solution for nesting Baidu map (BaiduMap) in ScrollView, scrollviewbaidumap
Due to product requirements, sometimes Baidu map (BaiduMap) has to be nested in ScrollView ). However, there will be some problems after nesting. Two prominent problems are: 1) there is a conflict between event processing and BaiduMap in ScrollView. 2) there is a black image problem when BaiduMap is dragged along with ScrollView. Many people have encountered these two problems, which are also tricky. Therefore, I hope Baidu can provide an official solution. The following describes my solutions.
1) there is a conflict between event processing and BaiduMap in ScrollView.
To understand the cause of event conflicts, you must understand Android's event transmission and processing mechanism. Http://blog.csdn.net/theone10211024/article/details/43270455 this article is the best I have ever seen. If you still do not understand it, you may wish to proceed here. Here, I will simply talk about the transfer process:
Events-> Activity. dispatchTouchEvent ()-> (top layer) ViewGroup. dispatchTouchEvent ()-> (top layer) ViewGroup. onInterceptTouchEvent ()-> childView1.dispatchTouchEvent ()-> childView1.OnTouchListener. onTouch (if defined)-> childView1.onTouchEvent ()-> childView2 ..... -> childView n ..... -> (top layer) ViewGroup. onTouchListener. onTouch () (if defined)-> (top layer) ViewGroup. onTouchEvent ()-> Activity. onTouchEvent ();
If an event is consumed in any stage, the transfer will stop.
Next, let's take a look at the source code of ScrollView.
@Override public boolean onInterceptTouchEvent(MotionEvent ev) { /* * This method JUST determines whether we want to intercept the motion. * If we return true, onMotionEvent will be called and we do the actual * scrolling there. */ /* * Shortcut the most recurring case: the user is in the dragging * state and he is moving his finger. We want to intercept this * motion. */ final int action = ev.getAction(); if ((action == MotionEvent.ACTION_MOVE) && (mIsBeingDragged)) { return true; } switch (action & MotionEvent.ACTION_MASK) { case MotionEvent.ACTION_MOVE: { /* * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check * whether the user has moved far enough from his original down touch. */ /* * Locally do absolute value. mLastMotionY is set to the y value * of the down event. */ final int activePointerId = mActivePointerId; if (activePointerId == INVALID_POINTER) { // If we don't have a valid id, the touch down wasn't on content. break; } final int pointerIndex = ev.findPointerIndex(activePointerId); final float y = ev.getY(pointerIndex); final int yDiff = (int) Math.abs(y - mLastMotionY); if (yDiff > mTouchSlop) { mIsBeingDragged = true; mLastMotionY = y; initVelocityTrackerIfNotExists(); mVelocityTracker.addMovement(ev); if (mScrollStrictSpan == null) { mScrollStrictSpan = StrictMode.enterCriticalSpan("ScrollView-scroll"); } } break; } case MotionEvent.ACTION_DOWN: { final float y = ev.getY(); if (!inChild((int) ev.getX(), (int) y)) { mIsBeingDragged = false; recycleVelocityTracker(); break; } /* * Remember location of down touch. * ACTION_DOWN always refers to pointer index 0. */ mLastMotionY = y; mActivePointerId = ev.getPointerId(0); initOrResetVelocityTracker(); mVelocityTracker.addMovement(ev); /* * If being flinged and user touches the screen, initiate drag; * otherwise don't. mScroller.isFinished should be false when * being flinged. */ mIsBeingDragged = !mScroller.isFinished(); if (mIsBeingDragged && mScrollStrictSpan == null) { mScrollStrictSpan = StrictMode.enterCriticalSpan("ScrollView-scroll"); } break; } case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: /* Release the drag */ mIsBeingDragged = false; mActivePointerId = INVALID_POINTER; recycleVelocityTracker(); if (mScroller.springBack(mScrollX, mScrollY, 0, 0, 0, getScrollRange())) { invalidate(); } break; case MotionEvent.ACTION_POINTER_UP: onSecondaryPointerUp(ev); break; } /* * The only time we want to intercept motion events is if we are in the * drag mode. */ return mIsBeingDragged; }
It can be seen from this function that when being dragged (BeingDragged), ScrollView. onInterceptTouchEvent () returns true. The event is consumed by the parent View, that is, ScrollView. In ScrollView, The BaiduMap (which is actually the MapView) will no longer receive the event. Therefore, the core idea to solve this problem is: when the ScrollView is in the drag event and the drag area is in the BaiduMap, let the event not be consumed by the parent View, but be handed over to the BaiduMap for processing.
Solution:
1) rewrite the ScrollView. onInterceptTouchEvent () function. When the finger is found to be in the BaiduMap, false (that is, not consumed) is returned. Other times are handled by super. onInterceptTouchEvent. One of the reasons why I didn't adopt such a solution is that it is difficult to control whether the finger is in BaiduMap. You can try it.
2) We found that android provides a function requestDisallowInterceptTouchEvent () for view. Its definition is as follows:
Called when a child does not want this parent and its ancestors to intercept touch events with ViewGroup.onInterceptTouchEvent(MotionEvent). 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.
It means that when a child View does not want its parent view to consume the event, but passes it to itself, it can call this function to say, "You don't want to consume the event, just pass it to me for further processing ". Then, I will follow
// Override the onTouch () event. In the event, use the requestDisallowInterceptTouchEvent (boolean) method to set the unavailability of the parent class, true indicates that the parent class is unavailable. // solves the conflict between map touch events and scrollView touch events mMapView. setOnTouchListener (new View. onTouchListener () {@ Overridepublic boolean onTouch (View v, MotionEvent event) {if (event. getAction () = MotionEvent. ACTION_UP) {scrollView. requestDisallowInterceptTouchEvent (false);} else {scrollView. requestDisallowInterceptTouchEvent (true) ;}return false ;}});
However, when I initiate breakpoint debugging, I found that the event was not triggered in the onTouch () of MapView, in other words, MapView. onTouchListener. onTouch () is not called at all. If you are familiar with the event Transfer sequence, you will guess that MapView inherits from ViewGroup, and the event must be consumed by a childView in MapView before it is passed to MapView. Finally, I found the view that consumes the event. Finally, the Code is improved like this.
Override the onTouch () event. In the event, use the requestDisallowInterceptTouchEvent (boolean) method to set the unavailability of the parent class, true indicates that the parent class is unavailable. // The map touch event conflicts with the scrollView touch event.
View v = mMapView. getChildAt (0); // This view is actually the map layer we see drawn on the surface.
v.setOnTouchListener(new View.OnTouchListener() {@Overridepublic boolean onTouch(View v, MotionEvent event) {if(event.getAction() == MotionEvent.ACTION_UP){scrollView.requestDisallowInterceptTouchEvent(false);}else{scrollView.requestDisallowInterceptTouchEvent(true);}return false;}});After improvement, the benefits are achieved.
In fact, it is difficult to find and solve problems when you are confused. At that time, I decompiled the jar package of Baidu and found the correct answer only half-written.
2) When BaiduMap is dragged along with ScrollView, there is a black image problem.
According to my analysis, Baidu maps are drawn using openGl, which may be caused by repeated painting during the drag process. In fact, Baidu engineers do not recommend using Baidu maps in ScrollView unless you have.
If dynamic Baidu map is required, the solution may have to wait for the sound of Baidu engineers.