| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.apache.weex.ui.view.gesture; |
| |
| import android.annotation.SuppressLint; |
| import android.content.Context; |
| import android.graphics.Point; |
| import android.graphics.PointF; |
| import android.graphics.Rect; |
| import android.os.Looper; |
| import android.support.annotation.NonNull; |
| import android.view.GestureDetector; |
| import android.view.MotionEvent; |
| import android.view.View; |
| import android.view.View.OnTouchListener; |
| import android.view.ViewGroup; |
| import android.view.ViewParent; |
| import com.alibaba.fastjson.JSONArray; |
| import com.alibaba.fastjson.JSONObject; |
| import org.apache.weex.bridge.EventResult; |
| import org.apache.weex.common.Constants; |
| import org.apache.weex.dom.WXEvent; |
| import org.apache.weex.ui.component.Scrollable; |
| import org.apache.weex.ui.component.WXComponent; |
| import org.apache.weex.ui.view.gesture.WXGestureType.GestureInfo; |
| import org.apache.weex.ui.view.gesture.WXGestureType.HighLevelGesture; |
| import org.apache.weex.ui.view.gesture.WXGestureType.LowLevelGesture; |
| import org.apache.weex.utils.WXLogUtils; |
| import org.apache.weex.utils.WXUtils; |
| import org.apache.weex.utils.WXViewUtils; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Map; |
| import org.apache.weex.WXSDKInstance; |
| import org.apache.weex.common.Constants.Event; |
| |
| public class WXGesture extends GestureDetector.SimpleOnGestureListener implements OnTouchListener { |
| |
| private final static String TAG = "Gesture"; |
| private static final int CUR_EVENT = -1; |
| public static final String START = "start"; |
| public static final String MOVE = "move"; |
| public static final String END = "end"; |
| public static final String UNKNOWN = "unknown"; |
| public static final String LEFT = "left"; |
| public static final String RIGHT = "right"; |
| public static final String UP = "up"; |
| public static final String DOWN = "down"; |
| private WXComponent component; |
| private GestureDetector mGestureDetector; |
| private Rect globalRect; |
| private Point globalOffset; |
| private Point globalEventOffset; |
| private PointF locEventOffset; |
| private PointF locLeftTop; |
| private long swipeDownTime = -1; |
| private long panDownTime = -1; |
| private WXGestureType mPendingPan = null;//event type to notify when action_up or action_cancel |
| private int mParentOrientation =-1; |
| private boolean mIsPreventMoveEvent = false; |
| private boolean mIsTouchEventConsumed = false; //Reset to false when first touch event, set to true when gesture event fired. |
| |
| private boolean requestDisallowInterceptTouchEvent = false; |
| |
| private boolean shouldBubbleResult = true; |
| private int shouldBubbleInterval = 0; //every times try |
| private int shouldBubbleCallRemainTimes = 0; |
| |
| private final List<OnTouchListener> mTouchListeners = new LinkedList<>(); |
| |
| public WXGesture(WXComponent wxComponent, Context context) { |
| this.component = wxComponent; |
| globalRect = new Rect(); |
| globalOffset = new Point(); |
| globalEventOffset = new Point(); |
| locEventOffset = new PointF(); |
| locLeftTop = new PointF(); |
| mGestureDetector = new GestureDetector(context, this, new GestureHandler()); |
| Scrollable parentScrollable = wxComponent.getParentScroller(); |
| if(parentScrollable != null) { |
| mParentOrientation = parentScrollable.getOrientation(); |
| } |
| shouldBubbleResult = WXUtils.getBoolean(wxComponent.getAttrs().get(Constants.Name.SHOULD_STOP_PROPAGATION_INIT_RESULT), true); |
| shouldBubbleInterval = WXUtils.getNumberInt(wxComponent.getAttrs().get(Constants.Name.SHOULD_STOP_PROPAGATION_INTERVAL), 0); |
| } |
| |
| private boolean isParentScrollable() { |
| if(component == null) { |
| return true; |
| } |
| Scrollable parentScrollable = component.getParentScroller(); |
| return parentScrollable == null || parentScrollable.isScrollable(); |
| } |
| |
| private boolean hasSameOrientationWithParent(){ |
| return (mParentOrientation == Constants.Orientation.HORIZONTAL && component.containsGesture( |
| HighLevelGesture.HORIZONTALPAN)) |
| || (mParentOrientation == Constants.Orientation.VERTICAL && component.containsGesture(HighLevelGesture.VERTICALPAN)); |
| } |
| |
| public void setPreventMoveEvent(boolean preventMoveEvent) { |
| mIsPreventMoveEvent = preventMoveEvent; |
| } |
| |
| /** |
| * |
| * @return true if current touch event is already consumed by gesture. |
| * Reset to false when next first touch event, set to true when gesture event fired. |
| */ |
| public boolean isTouchEventConsumedByAdvancedGesture(){ |
| return mIsTouchEventConsumed; |
| } |
| |
| |
| /** |
| * stoppropagation |
| * */ |
| public static boolean isStopPropagation(String type){ |
| return Constants.Event.STOP_PROPAGATION.equals(type) || Constants.Event.STOP_PROPAGATION_RAX.equals(type); |
| } |
| |
| public static boolean hasStopPropagation(WXComponent component){ |
| WXEvent event = component.getEvents(); |
| if(event == null){ |
| return false; |
| } |
| int size = event.size(); |
| for (int i=0; i<size; i++) { |
| if(i >= event.size()){ |
| break; |
| } |
| String type = event.get(i); |
| if(isStopPropagation(type)){ |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * shouldBubbleEvent default true |
| * */ |
| private boolean shouldBubbleTouchEvent(MotionEvent event){ |
| if(hasStopPropagation(component)){ |
| if(shouldBubbleInterval > 0 && shouldBubbleCallRemainTimes > 0){ |
| shouldBubbleCallRemainTimes--; |
| return shouldBubbleResult; |
| } |
| Map<String, Object> eventMap = createFireEventParam(event, CUR_EVENT, null); |
| eventMap.put("type", "touch"); |
| if(event.getAction() == MotionEvent.ACTION_DOWN){ |
| eventMap.put("action", START); |
| }else if(event.getAction() == MotionEvent.ACTION_CANCEL |
| || event.getAction() == MotionEvent.ACTION_UP){ |
| eventMap.put("action", END); |
| }else{ |
| eventMap.put("action", MOVE); |
| } |
| |
| String name = Event.STOP_PROPAGATION; |
| if(!component.getEvents().contains(Event.STOP_PROPAGATION)){ |
| name = Event.STOP_PROPAGATION_RAX; |
| } |
| EventResult result = component.fireEventWait(name, eventMap); |
| if(result.isSuccess() && result.getResult() != null){ |
| boolean stopPropagation = WXUtils.getBoolean(result.getResult(), !shouldBubbleResult); |
| shouldBubbleResult = !stopPropagation; |
| } |
| shouldBubbleCallRemainTimes = shouldBubbleInterval; |
| return shouldBubbleResult; |
| |
| } |
| return true; |
| } |
| |
| @SuppressWarnings("unused") |
| public void addOnTouchListener(OnTouchListener listener) { |
| if(listener != null) { |
| mTouchListeners.add(listener); |
| } |
| } |
| |
| @SuppressWarnings("unused") |
| public boolean removeTouchListener(OnTouchListener listener) { |
| if(listener != null) { |
| return mTouchListeners.remove(listener); |
| } |
| return false; |
| } |
| |
| @SuppressLint("ClickableViewAccessibility") |
| @Override |
| public boolean onTouch(View v, MotionEvent event) { |
| if(requestDisallowInterceptTouchEvent){ |
| requestDisallowInterceptTouchEvent = false; |
| return false; |
| } |
| try { |
| boolean result = mGestureDetector.onTouchEvent(event); |
| |
| if(mTouchListeners != null && !mTouchListeners.isEmpty()) { |
| for(OnTouchListener listener : mTouchListeners) { |
| result |= listener.onTouch(v, event); |
| } |
| } |
| |
| switch (event.getActionMasked()) { |
| case MotionEvent.ACTION_POINTER_DOWN: |
| case MotionEvent.ACTION_DOWN: |
| mIsTouchEventConsumed = false; |
| /** |
| * If component has same scroll orientation with it's parent and it's parent not scrollable |
| * , we should disallow parent in DOWN. |
| */ |
| if(hasSameOrientationWithParent() && !isParentScrollable()){ |
| ViewParent p; |
| if ((p = component.getRealView().getParent()) != null) { |
| p.requestDisallowInterceptTouchEvent(true); |
| } |
| } |
| result |= handleMotionEvent(LowLevelGesture.ACTION_DOWN, event); |
| break; |
| case MotionEvent.ACTION_MOVE: |
| result |= handleMotionEvent(LowLevelGesture.ACTION_MOVE, event); |
| break; |
| case MotionEvent.ACTION_UP: |
| case MotionEvent.ACTION_POINTER_UP: |
| finishDisallowInterceptTouchEvent(v); |
| result |= handleMotionEvent(LowLevelGesture.ACTION_UP, event); |
| result |= handlePanMotionEvent(event); |
| break; |
| case MotionEvent.ACTION_CANCEL: |
| finishDisallowInterceptTouchEvent(v); |
| result |= handleMotionEvent(LowLevelGesture.ACTION_CANCEL, event); |
| result |= handlePanMotionEvent(event); |
| break; |
| } |
| if(hasStopPropagation(component)){ |
| ViewGroup parent = (ViewGroup) v.getParent(); |
| boolean requestDisallowInterceptTouchEvent = false; |
| if(parent != null){ |
| if(!shouldBubbleTouchEvent(event)){ |
| requestDisallowInterceptTouchEvent = true; |
| } |
| parent.requestDisallowInterceptTouchEvent(requestDisallowInterceptTouchEvent); |
| } |
| if(component.getParent() != null){ |
| component.getParent().requestDisallowInterceptTouchEvent(requestDisallowInterceptTouchEvent); |
| } |
| if(mIsTouchEventConsumed && WXUtils.getBoolean(component.getAttrs().get("cancelTouchOnConsume"), false)){//when touch event consumed by one gesture, other component should not consumed |
| event.setAction(MotionEvent.ACTION_CANCEL); |
| } |
| } |
| return result; |
| } catch (Exception e) { |
| WXLogUtils.e("Gesture RunTime Error ", e); |
| return false; |
| } |
| } |
| |
| private String getPanEventAction(MotionEvent event) { |
| switch (event.getAction()) { |
| case MotionEvent.ACTION_DOWN: |
| return START; |
| case MotionEvent.ACTION_MOVE: |
| return MOVE; |
| case MotionEvent.ACTION_UP: |
| return END; |
| case MotionEvent.ACTION_CANCEL: |
| return END; |
| default: |
| return UNKNOWN; |
| } |
| } |
| |
| private void finishDisallowInterceptTouchEvent(View v){ |
| if(v.getParent() != null){ |
| v.getParent().requestDisallowInterceptTouchEvent(false); |
| } |
| } |
| |
| private boolean handlePanMotionEvent(MotionEvent motionEvent) { |
| if (mPendingPan == null) { |
| return false; |
| } |
| String state = null; |
| if (mPendingPan == HighLevelGesture.HORIZONTALPAN || mPendingPan == HighLevelGesture.VERTICALPAN) { |
| state = getPanEventAction(motionEvent); |
| |
| } |
| |
| if (component.containsGesture(mPendingPan)) { |
| if(mIsPreventMoveEvent && MOVE.equals(state)){ |
| return true; |
| } |
| List<Map<String, Object>> list = createMultipleFireEventParam(motionEvent, state); |
| for (Map<String, Object> map : list) { |
| component.fireEvent(mPendingPan.toString(), map); |
| } |
| //action is finish, clean pending pan |
| if (motionEvent.getAction() == MotionEvent.ACTION_UP || motionEvent.getAction() == MotionEvent.ACTION_CANCEL) { |
| mPendingPan = null; |
| } |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| /** |
| * |
| * @param WXGestureType possible low-level gesture, defined in {@link Event} |
| * @param motionEvent motionEvent, which contains all pointers event in a period of time |
| * @return true if this event is handled, otherwise false. |
| */ |
| private boolean handleMotionEvent(WXGestureType WXGestureType, MotionEvent motionEvent) { |
| if (component.containsGesture(WXGestureType)) { |
| List<Map<String, Object>> list = createMultipleFireEventParam(motionEvent, null); |
| for (Map<String, Object> map : list) { |
| component.fireEvent(WXGestureType.toString(), map); |
| } |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| /** |
| * Create a list of event for {@link WXSDKInstance#fireEvent(String, String, Map, Map)}. |
| * As there is a batch mechanism in MotionEvent, so this method returns a list. |
| * @param motionEvent motionEvent, which contains all pointers event in a period of time |
| * @return List of Map, which contains touch object. |
| */ |
| private List<Map<String, Object>> createMultipleFireEventParam(MotionEvent motionEvent,String state) { |
| List<Map<String, Object>> list = new ArrayList<>(motionEvent.getHistorySize() + 1); |
| //list.addAll(getHistoricalEvents(motionEvent)); |
| list.add(createFireEventParam(motionEvent, CUR_EVENT, state)); |
| return list; |
| } |
| |
| /** |
| * Get historical event. This is only applied to {@link MotionEvent#ACTION_MOVE}. |
| * For other types of motionEvent, historical event is meaningless. |
| * @param motionEvent motionEvent, which contains all pointers event in a period of time |
| * @return If motionEvent.getActionMasked()!=MotionEvent.ACTION_MOVE, |
| * this method will return an empty list. |
| * Otherwise this method will return the historical motionEvent, which may also be empty. |
| */ |
| private List<Map<String, Object>> getHistoricalEvents(MotionEvent motionEvent) { |
| List<Map<String, Object>> list = new ArrayList<>(motionEvent.getHistorySize()); |
| if (motionEvent.getActionMasked() == MotionEvent.ACTION_MOVE) { |
| Map<String, Object> param; |
| for (int i = 0; i < motionEvent.getHistorySize(); i++) { |
| param = createFireEventParam(motionEvent, i,null); |
| list.add(param); |
| } |
| } |
| return list; |
| } |
| |
| /** |
| * Create a map represented touch event at a certain moment. |
| * @param motionEvent motionEvent, which contains all pointers event in a period of time |
| * @param pos index used to retrieve a certain moment in a period of time. |
| * @return touchEvent |
| * @see <a href="https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent">touchEvent</a> |
| */ |
| private Map<String, Object> createFireEventParam(MotionEvent motionEvent, int pos, String state) { |
| JSONArray jsonArray = new JSONArray(motionEvent.getPointerCount()); |
| if (motionEvent.getActionMasked() == MotionEvent.ACTION_MOVE) { |
| for (int i = 0; i < motionEvent.getPointerCount(); i++) { |
| jsonArray.add(createJSONObject(motionEvent, pos, i)); |
| } |
| } else if (isPointerNumChanged(motionEvent)) { |
| int pointerIndex = motionEvent.getActionIndex(); |
| jsonArray.add(createJSONObject(motionEvent, CUR_EVENT, pointerIndex)); |
| } |
| Map<String, Object> map = new HashMap<>(); |
| map.put(GestureInfo.HISTORICAL_XY, jsonArray); |
| if (state != null) { |
| map.put(GestureInfo.STATE, state); |
| } |
| return map; |
| } |
| |
| /** |
| * Tell whether the number of motion event's pointer changed. |
| * @param event the current motion event |
| * @return true for number of motion event's pointer changed, otherwise false. |
| */ |
| private boolean isPointerNumChanged(MotionEvent event) { |
| return event.getActionMasked() == MotionEvent.ACTION_DOWN || |
| event.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN || |
| event.getActionMasked() == MotionEvent.ACTION_UP || |
| event.getActionMasked() == MotionEvent.ACTION_POINTER_UP || |
| event.getActionMasked() == MotionEvent.ACTION_CANCEL; |
| } |
| |
| /** |
| * Tell whether component contains pan gesture |
| * @return true for contains pan gesture, otherwise false. |
| */ |
| private boolean containsSimplePan() { |
| return component.containsGesture(HighLevelGesture.PAN_START) || |
| component.containsGesture(HighLevelGesture.PAN_MOVE) || |
| component.containsGesture(HighLevelGesture.PAN_END); |
| } |
| |
| /** |
| * Create a touchObject for a pointer at a certain moment. |
| * @param motionEvent motionEvent, which contains all pointers event in a period of time |
| * @param pos index used to retrieve a certain moment in a period of time. |
| * @param pointerIndex pointerIndex |
| * @return JSONObject represent a touch event |
| * @see <a href="https://developer.mozilla.org/en-US/docs/Web/API/Touch">touch</a> |
| */ |
| private JSONObject createJSONObject(MotionEvent motionEvent, int pos, int pointerIndex) { |
| PointF screenXY, pageXY; |
| if (pos == CUR_EVENT) { |
| pageXY = getEventLocInPageCoordinate(motionEvent, pointerIndex); |
| screenXY = getEventLocInScreenCoordinate(motionEvent, pointerIndex); |
| } else { |
| pageXY = getEventLocInPageCoordinate(motionEvent, pointerIndex, pos); |
| screenXY = getEventLocInScreenCoordinate(motionEvent, pointerIndex, pos); |
| } |
| JSONObject map = createJSONObject(screenXY, pageXY, (float) motionEvent.getPointerId(pointerIndex)); |
| float force = motionEvent.getPressure(); |
| if(force > 0 && force < 1) { |
| map.put("force", motionEvent.getPressure()); |
| } |
| return map; |
| } |
| |
| /** |
| * Create a touchObject for a pointer at a certain moment. |
| * @param screenXY the point of event happened in screen coordinate |
| * @param pageXY the point of event happened in page coorindate |
| * @param pointerId pointerIndex pointerIndex |
| * @return JSONObject represent a touch event |
| * @see <a href="https://developer.mozilla.org/en-US/docs/Web/API/Touch">touch</a> |
| */ |
| @NonNull |
| private JSONObject createJSONObject(PointF screenXY, PointF pageXY, float pointerId) { |
| JSONObject jsonObject = new JSONObject(); |
| jsonObject.put(GestureInfo.PAGE_X, pageXY.x); |
| jsonObject.put(GestureInfo.PAGE_Y, pageXY.y); |
| jsonObject.put(GestureInfo.SCREEN_X, screenXY.x); |
| jsonObject.put(GestureInfo.SCREEN_Y, screenXY.y); |
| jsonObject.put(GestureInfo.POINTER_ID, pointerId); |
| return jsonObject; |
| } |
| |
| /** |
| * @see {@link #getEventLocInScreenCoordinate(MotionEvent, int, int)} |
| */ |
| private PointF getEventLocInScreenCoordinate(MotionEvent motionEvent, int pointerIndex) { |
| return getEventLocInScreenCoordinate(motionEvent, pointerIndex, CUR_EVENT); |
| } |
| |
| /** |
| * Get event location in Screen's coordinate, e.g. root(global) coordinate. |
| * @see <a href="https://developer.mozilla.org/en-US/docs/Web/API/Touch/screenX">screenX</a> |
| * @see <a href="https://developer.mozilla.org/en-US/docs/Web/API/Touch/screenY">screenY</a> |
| * @param motionEvent the original motionEvent |
| * @param pointerIndex pointerIndex |
| * @param position if motionEvent.getHistoricalSize()!=0, the is the index of historical event, |
| * otherwise this is {@link #CUR_EVENT} which indicates historicalSize is zero |
| * @return the eventLocation in screen's coordinate |
| */ |
| private PointF getEventLocInScreenCoordinate(MotionEvent motionEvent, int pointerIndex, int position) { |
| float eventX, eventY; |
| if (position == CUR_EVENT) { |
| eventX = motionEvent.getX(pointerIndex); |
| eventY = motionEvent.getY(pointerIndex); |
| } else { |
| eventX = motionEvent.getHistoricalX(pointerIndex, position); |
| eventY = motionEvent.getHistoricalY(pointerIndex, position); |
| } |
| return getEventLocInScreenCoordinate(eventX, eventY); |
| } |
| |
| /** |
| * Get event location in Screen's coordinate, e.g. root(global) coordinate. |
| * @param eventX {@link MotionEvent#getX()} or {@link MotionEvent#getHistoricalX(int, int)} |
| * @param eventY {@link MotionEvent#getX()} or {@link MotionEvent#getHistoricalX(int, int)} |
| * @return the eventLocation in screen's coordinate |
| * @see {@link #getEventLocInScreenCoordinate(MotionEvent, int, int)} |
| */ |
| @NonNull |
| private PointF getEventLocInScreenCoordinate(float eventX, float eventY) { |
| globalRect.set(0, 0, 0, 0); |
| globalOffset.set(0, 0); |
| globalEventOffset.set((int) eventX, (int) eventY); |
| component.getRealView().getGlobalVisibleRect(globalRect, globalOffset); |
| globalEventOffset.offset(globalOffset.x, globalOffset.y); |
| return new PointF(WXViewUtils.getWebPxByWidth(globalEventOffset.x,component.getInstance().getInstanceViewPortWidth()), |
| WXViewUtils.getWebPxByWidth(globalEventOffset.y,component.getInstance().getInstanceViewPortWidth())); |
| } |
| |
| /** |
| * @see {@link #getEventLocInPageCoordinate(MotionEvent, int, int)} |
| */ |
| private PointF getEventLocInPageCoordinate(MotionEvent motionEvent, int pointerIndex) { |
| return getEventLocInPageCoordinate(motionEvent, pointerIndex, CUR_EVENT); |
| } |
| |
| /** |
| * Get event's location in Document's (Page) coordinate. |
| * @see <a href="https://developer.mozilla.org/en-US/docs/Web/API/Touch/pageX">pageX</a> |
| * @see <a href="https://developer.mozilla.org/en-US/docs/Web/API/Touch/pageY">pageY</a> |
| * @param motionEvent the original motionEvent |
| * @param pointerIndex pointerIndex |
| * @param position if motionEvent.getHistoricalSize()!=0, the is the index of historical event, |
| * otherwise this is {@link #CUR_EVENT} which indicates historicalSize is zero |
| * @return the event location in page's coordinate. |
| */ |
| private PointF getEventLocInPageCoordinate(MotionEvent motionEvent, int pointerIndex, int position) { |
| float eventX, eventY; |
| if (position == CUR_EVENT) { |
| eventX = motionEvent.getX(pointerIndex); |
| eventY = motionEvent.getY(pointerIndex); |
| } else { |
| eventX = motionEvent.getHistoricalX(pointerIndex, position); |
| eventY = motionEvent.getHistoricalY(pointerIndex, position); |
| } |
| return getEventLocInPageCoordinate(eventX, eventY); |
| } |
| |
| /** |
| * Get event's location in Document's (Page) coordinate. |
| * @param eventX {@link MotionEvent#getX()} or {@link MotionEvent#getHistoricalX(int, int)} |
| * @param eventY {@link MotionEvent#getX()} or {@link MotionEvent#getHistoricalX(int, int)} |
| * @return the event location in page's coordinate. |
| * @see {@link #getEventLocInPageCoordinate(MotionEvent, int, int)} |
| */ |
| @NonNull |
| private PointF getEventLocInPageCoordinate(float eventX, float eventY) { |
| locEventOffset.set(eventX, eventY); |
| locLeftTop.set(0, 0); |
| component.computeVisiblePointInViewCoordinate(locLeftTop); |
| locEventOffset.offset(locLeftTop.x, locLeftTop.y); |
| return new PointF(WXViewUtils.getWebPxByWidth(locEventOffset.x,component.getInstance().getInstanceViewPortWidth()), |
| WXViewUtils.getWebPxByWidth(locEventOffset.y,component.getInstance().getInstanceViewPortWidth())); |
| } |
| |
| private static class GestureHandler extends android.os.Handler { |
| |
| public GestureHandler() { |
| super(Looper.getMainLooper()); |
| } |
| } |
| |
| |
| /***************** OnGestureListener ****************/ |
| |
| @Override |
| public void onLongPress(MotionEvent e) { |
| if (component.containsGesture(HighLevelGesture.LONG_PRESS)) { |
| List<Map<String, Object>> list = createMultipleFireEventParam(e,null); |
| component.getInstance().fireEvent( |
| component.getRef(), |
| HighLevelGesture.LONG_PRESS.toString(), |
| list.get(list.size() - 1)); |
| mIsTouchEventConsumed = true; |
| } |
| } |
| |
| /** |
| * Gesture priority:horizontalPan & verticalPan > pan > swipe |
| */ |
| @Override |
| public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { |
| boolean result = false; |
| if (e1 == null || e2 == null) { |
| return false; |
| } |
| float dx = Math.abs(e2.getX() - e1.getX()); |
| float dy = Math.abs(e2.getY() - e1.getY()); |
| WXGestureType possiblePan; |
| if (dx > dy) { |
| possiblePan = HighLevelGesture.HORIZONTALPAN; |
| } else { |
| possiblePan = HighLevelGesture.VERTICALPAN; |
| } |
| if (mPendingPan == HighLevelGesture.HORIZONTALPAN || mPendingPan == HighLevelGesture.VERTICALPAN) { |
| //already during directional-pan |
| result = handlePanMotionEvent(e2); |
| } else if (component.containsGesture(possiblePan)) { |
| ViewParent p; |
| if ((p = component.getRealView().getParent()) != null) { |
| p.requestDisallowInterceptTouchEvent(true); |
| } |
| if (mPendingPan != null) { |
| handleMotionEvent(mPendingPan, e2);//finish pan if exist |
| } |
| mPendingPan = possiblePan; |
| component.fireEvent(possiblePan.toString(), createFireEventParam(e2, CUR_EVENT, START)); |
| |
| result = true; |
| } else if (containsSimplePan()) { |
| if (panDownTime != e1.getEventTime()) { |
| panDownTime = e1.getEventTime(); |
| mPendingPan = HighLevelGesture.PAN_END; |
| component.fireEvent(HighLevelGesture.PAN_START.toString(), |
| createFireEventParam(e1, CUR_EVENT, null)); |
| } else { |
| component.fireEvent(HighLevelGesture.PAN_MOVE.toString(), |
| createFireEventParam(e2, CUR_EVENT, null)); |
| } |
| result = true; |
| } else if (component.containsGesture(HighLevelGesture.SWIPE)) { |
| if (swipeDownTime != e1.getEventTime()) { |
| swipeDownTime = e1.getEventTime(); |
| List<Map<String, Object>> list = createMultipleFireEventParam(e2, null); |
| Map<String, Object> param = list.get(list.size() - 1); |
| if (Math.abs(distanceX) > Math.abs(distanceY)) { |
| param.put(GestureInfo.DIRECTION, distanceX > 0 ? LEFT : RIGHT); |
| } else { |
| param.put(GestureInfo.DIRECTION, distanceY > 0 ? UP : DOWN); |
| } |
| component.getInstance().fireEvent(component.getRef(), |
| HighLevelGesture.SWIPE.toString(), param); |
| result = true; |
| } |
| } |
| mIsTouchEventConsumed = mIsTouchEventConsumed || result; |
| return result; |
| } |
| |
| @Override |
| public boolean onDown(MotionEvent e) { |
| return true; |
| } |
| |
| public boolean isRequestDisallowInterceptTouchEvent() { |
| return requestDisallowInterceptTouchEvent; |
| } |
| |
| public void setRequestDisallowInterceptTouchEvent(boolean requestDisallowInterceptTouchEvent) { |
| this.requestDisallowInterceptTouchEvent = requestDisallowInterceptTouchEvent; |
| } |
| |
| |
| |
| } |