public class BetterRecyclerView extends RecyclerView { private int touchSlop; private Context mContext; private int INVALID_POINTER = -1; private int scrollPointerId = INVALID_POINTER; private int initialTouchX; private int initialTouchY; private final static String TAG = "BetterRecyclerView"; public BetterRecyclerView(Context context) { // super(context); this(context, null); } public BetterRecyclerView(Context context, @Nullable AttributeSet attrs) { // super(context, attrs); this(context, attrs, 0); } public BetterRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); ViewConfiguration vc = ViewConfiguration.get(context); touchSlop =vc.getScaledEdgeSlop() ; mContext =context ; } @Override public void setScrollingTouchSlop(int slopConstant) { super.setScrollingTouchSlop(slopConstant); ViewConfiguration vc = ViewConfiguration.get(mContext); switch (slopConstant) { case TOUCH_SLOP_DEFAULT: touchSlop = vc.getScaledTouchSlop(); break; case TOUCH_SLOP_PAGING: touchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(vc); break; } } @Override public boolean onInterceptTouchEvent(MotionEvent e) { if (e==null){ return false ; } int action = MotionEventCompat.getActionMasked(e); int actionIndex = MotionEventCompat.getActionIndex(e); switch (action){ case MotionEvent.ACTION_DOWN : scrollPointerId = MotionEventCompat.getPointerId(e, 0); initialTouchX = Math.round(e.getX() + 0.5f); initialTouchY = Math.round(e.getY() + 0.5f); return super.onInterceptTouchEvent(e); case MotionEvent.ACTION_POINTER_DOWN: scrollPointerId = MotionEventCompat.getPointerId(e, actionIndex); initialTouchX = Math.round(MotionEventCompat.getX(e, actionIndex) + 0.5f); initialTouchY = Math.round(MotionEventCompat.getY(e, actionIndex) + 0.5f); return super.onInterceptTouchEvent(e); case MotionEvent.ACTION_MOVE: final int index =e.findPointerIndex(scrollPointerId); if ( index<0){ return false ; } int x = Math.round(MotionEventCompat.getX(e, index) + 0.5f); int y = Math.round(MotionEventCompat.getY(e,index)+0.5f); if (getScrollState()!=SCROLL_STATE_DRAGGING){ int dx =x-initialTouchX ; int dy = y-initialTouchY ; boolean startScroll = false; if ((getLayoutManager().canScrollHorizontally()&&Math.abs(dx)>touchSlop)&&(getLayoutManager().canScrollVertically()||Math.abs(dx)>Math.abs((dy)))){ startScroll = true ; } if ((getLayoutManager().canScrollHorizontally()&&Math.abs(dy)>touchSlop)&&(getLayoutManager().canScrollHorizontally()||Math.abs(dy)>Math.abs((dx)))){ startScroll = true ; } return startScroll && super.onInterceptTouchEvent(e); } return super.onInterceptTouchEvent(e); } return super.onInterceptTouchEvent(e); } }
多点触摸手势 当多个pointer同时触摸屏幕,系统会生成如下事件: ACTION_DOWN—For the first pointer that touches the screen. This starts the gesture. The pointer data for this pointer is always at index 0 in the MotionEvent. ACTION_POINTER_DOWN—For extra pointers that enter the screen beyond the first. The pointer data for this pointer is at the index returned by getActionIndex(). ACTION_MOVE—A change has happened during a press gesture. ACTION_POINTER_UP—Sent when a non-primary pointer goes up. ACTION_UP—Sent when the last pointer leaves the screen. 你可以依靠每一个pointer的index和ID来追踪每一个pointer: Index:MotionEvent会把每一个pointer的信息放在一个数组里,index即是这个数组索引。大多数你用的MotionEvent方法是以这个index作为参数的。 ID:每一个pointer还有一个ID映射,在touch事件中保持恒定一致(persistent),这样就可以在整个手势中跟踪一个单独的pointer。 pointer在一个motion event中出现的顺序是未定的,所以pointer的index在不同的事件中是可变的,但是只要pointer保持active,它的ID是保持不变的。 通过getPointerId()获得ID,这样就可以在多个motion event中追踪pointer。然后对于连续的motion event,可以使用findPointerIndex()方法来获得指定ID的pointer在当前事件中的index。 比如: 复制代码 private int mActivePointerId; public boolean onTouchEvent(MotionEvent event) { .... // Get the pointer ID mActivePointerId = event.getPointerId(0); // ... Many touch events later... // Use the pointer ID to find the index of the active pointer // and fetch its position int pointerIndex = event.findPointerIndex(mActivePointerId); // Get the pointer's current position float x = event.getX(pointerIndex); float y = event.getY(pointerIndex); }