前言
在上一篇文章中,已经总结了MotionEvent以及多点触控相关的基础理论知识和常用的函数。本篇将通过实现单指拖动图片,多指拖动图片的实际案例来进行练习并实现一些效果,来理解前面的理论知识。要理解本文的代码,需要先掌握上一篇的理论知识,事件处理基础,以及一定的自定义View基础,这些我也在本系列文章的前几篇中讲过,有兴趣的可以按照本系列的顺序依次阅读学习,相信您一定会有不小的收获。。
一、实现单指拖动图片
要实现单指拖动图片,大致思路就是监控手指的ACTION_MOVE事件。手指移动过程中,获取事件的坐标,让图片根据坐标的变化来进行移动。
代码实现如下,先自定义一个View,在其中处理单指拖动逻辑。
1 public class SingleTouchDragView extends View { 2 private static final String TAG = "songzheweiwang"; 3 private Bitmap mBitmap; 4 private RectF mRectF; 5 private Matrix mMatrix; 6 private Paint mPaint; 7 private PointF mLstPointF; 8 private boolean mCanDrag = false; 9 10 public SingleTouchDragView(Context context, @Nullable AttributeSet attrs) { 11 super(context, attrs); 12 init(); 13 } 14 15 private void init() { 16 mPaint = new Paint(); 17 mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.dog); 18 mRectF = new RectF(0, 0, mBitmap.getWidth(), mBitmap.getHeight()); 19 mMatrix = new Matrix(); 20 mLstPointF = new PointF(); 21 } 22 23 @Override 24 public boolean onTouchEvent(MotionEvent event) { 25 switch (event.getAction()) { 26 case MotionEvent.ACTION_DOWN: 27 //判断按下位置是否在图片区域内 28 if (mRectF.contains(event.getX(), event.getY())) { 29 mCanDrag = true; 30 mLstPointF.set(event.getX(), event.getY()); 31 } 32 break; 33 case MotionEvent.ACTION_UP: 34 mCanDrag = false; 35 case MotionEvent.ACTION_MOVE: 36 if (mCanDrag) { 37 //移动图片 38 mMatrix.postTranslate(event.getX() - mLstPointF.x, event.getY() - mLstPointF.y); 39 //更新触摸位置 40 mLstPointF.set(event.getX(), event.getY()); 41 // 更新图片区域 42 mRectF = new RectF(0, 0, mBitmap.getWidth(), mBitmap.getHeight()); 43 mMatrix.mapRect(mRectF); 44 //刷新 45 invalidate(); 46 } 47 break; 48 } 49 //注意这里需要返回true,因为当前自定义view继承自基类View,默认是无法消费触摸事件的 50 return true; 51 } 52 53 @Override 54 protected void onDraw(Canvas canvas) { 55 super.onDraw(canvas); 56 canvas.drawBitmap(mBitmap, mMatrix, mPaint); 57 } 58 }
自定义View使用的布局如下:
1 <?xml version="1.0" encoding="utf-8"?> 2 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent"> 5 6 <com.example.demos.customviewdemo.SingleTouchDragView 7 android:layout_width="match_parent" 8 android:layout_height="match_parent" /> 9 </RelativeLayout>
用一根手指在图片上进行拖动,效果如下左图所示:
在单指操作的情况下,可以正常被拖动。但是如果是多指操作的时候,就会混乱了。右图为两根手指滑动的图片的效果,因为两根手指都在移动, 导致ACTION_MOVE事件中,一会儿以第一根手指的触摸点为坐标,一会儿又以第二根手指的触摸点为坐标,这就导致图片闪动。
二、实现多指操作时只有第一根手指可以拖动图片
1 public class MultiTouchDragView extends View { 2 private static final String TAG = "songzheweiwang"; 3 private Bitmap mBitmap; 4 private RectF mRectF; 5 private Matrix mMatrix; 6 private Paint mPaint; 7 private PointF mLstPointF; 8 private boolean mCanDrag = false; 9 10 public MultiTouchDragView(Context context, @Nullable AttributeSet attrs) { 11 super(context, attrs); 12 init(); 13 } 14 15 private void init() { 16 mPaint = new Paint(); 17 mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.dog); 18 mRectF = new RectF(0, 0, mBitmap.getWidth(), mBitmap.getHeight()); 19 mMatrix = new Matrix(); 20 mLstPointF = new PointF(); 21 } 22 23 @RequiresApi(api = Build.VERSION_CODES.KITKAT) 24 @Override 25 public boolean onTouchEvent(MotionEvent event) { 26 Log.i(TAG, "" MotionEvent.actionToString(event.getAction()) ";mCanDrag=" mCanDrag ";actionIndex=" event.getActionIndex() ";action=" event.getAction()); 27 switch (event.getActionMasked()) { 28 case MotionEvent.ACTION_DOWN: 29 case MotionEvent.ACTION_POINTER_DOWN: 30 //pointerId为0的手指(即我们定义的第一根手指)按下在指定区域内 31 if (event.getPointerId(event.getActionIndex()) == 0 && mRectF.contains(event.getX(), event.getY())) { 32 mCanDrag = true; 33 //getX()和getY()没有传入参数时,默认传入的0 34 mLstPointF.set(event.getX(), event.getY()); 35 } 36 break; 37 case MotionEvent.ACTION_MOVE: 38 if (mCanDrag) { 39 int pointerIndex = event.findPointerIndex(0); 40 //这里需要注意,多手指频繁按下和抬起时可能会出现pointerIndex为-1的情况,如不处理,后面会报错 41 if (pointerIndex == -1) { 42 break; 43 } 44 mMatrix.postTranslate(event.getX(pointerIndex) - mLstPointF.x, event.getY(pointerIndex) - mLstPointF.y); 45 mLstPointF.set(event.getX(), event.getY()); 46 mRectF = new RectF(0, 0, mBitmap.getWidth(), mBitmap.getHeight()); 47 mMatrix.mapRect(mRectF); 48 invalidate(); 49 } 50 break; 51 case MotionEvent.ACTION_POINTER_UP: 52 case MotionEvent.ACTION_UP: 53 if (event.getPointerId(event.getActionIndex()) == 0) { 54 mCanDrag = false; 55 } 56 break; 57 } 58 return true; 59 } 60 61 @Override 62 protected void onDraw(Canvas canvas) { 63 super.onDraw(canvas); 64 canvas.drawBitmap(mBitmap, mMatrix, mPaint); 65 } 66 }
效果图如下,用两根手指来依次按下并拖动图片:
我们发现,只有第一根手指在滑动时,图片才会跟着移动,第二根手指(右边的手指)的滑动无效。
三、实现两根手指轮流拖动图片
1 public class MultiTouchDragView2 extends View { 2 private static final String TAG = "songzheweiwang"; 3 private Bitmap mBitmap; 4 private RectF mRectF; 5 private Matrix mMatrix; 6 private Paint mPaint; 7 private PointF mLstPointF; 8 private boolean mCanDrag = false; 9 private int mActivePointerId; 10 private final int INVALID_POINTER = -1; 11 12 public MultiTouchDragView2(Context context, @Nullable AttributeSet attrs) { 13 super(context, attrs); 14 init(); 15 } 16 17 private void init() { 18 mPaint = new Paint(); 19 mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.dog); 20 mRectF = new RectF(0, 0, mBitmap.getWidth(), mBitmap.getHeight()); 21 mMatrix = new Matrix(); 22 mLstPointF = new PointF(); 23 } 24 25 @RequiresApi(api = Build.VERSION_CODES.KITKAT) 26 @Override 27 public boolean onTouchEvent(MotionEvent event) { 28 Log.i(TAG, "" MotionEvent.actionToString(event.getAction()) ";mCanDrag=" mCanDrag ";actionIndex=" event.getActionIndex() ";action=" event.getAction()); 29 int actionIndex = event.getActionIndex(); 30 switch (event.getActionMasked()) { 31 case MotionEvent.ACTION_DOWN: 32 //getX()和getY()没有传入参数时,默认传入的0 33 if (mRectF.contains(event.getX(), event.getY())) { 34 mActivePointerId = 0; //第一根手指按下时,pointerId和pointerIndex都为0 35 mCanDrag = true; 36 mLstPointF.set(event.getX(), event.getY()); 37 } 38 break; 39 case MotionEvent.ACTION_POINTER_DOWN: 40 //有新落下的手指,则将新落下的手指作为活动手指 41 mActivePointerId = event.getPointerId(actionIndex); 42 mLstPointF.set(event.getX(actionIndex), event.getY(actionIndex)); 43 break; 44 case MotionEvent.ACTION_MOVE: 45 if (mActivePointerId == INVALID_POINTER) { 46 break; 47 } 48 if (mCanDrag) { 49 int pointerIndex = event.findPointerIndex(mActivePointerId); 50 //这里需要注意,多手指频繁按下和抬起时可能会出现pointerIndex为-1的情况,如不处理,后面会报错 51 if (pointerIndex == -1) { 52 break; 53 } 54 mMatrix.postTranslate(event.getX(pointerIndex) - mLstPointF.x, event.getY(pointerIndex) - mLstPointF.y); 55 mLstPointF.set(event.getX(pointerIndex), event.getY(pointerIndex)); 56 mRectF = new RectF(0, 0, mBitmap.getWidth(), mBitmap.getHeight()); 57 mMatrix.mapRect(mRectF); 58 invalidate(); 59 } 60 break; 61 case MotionEvent.ACTION_POINTER_UP: 62 //如果当前抬起的手指为活动手指 63 if (mActivePointerId == event.getPointerId(actionIndex)) { 64 int newPointerIndex = actionIndex == 0 ? 1 : 0; 65 mActivePointerId = event.getPointerId(newPointerIndex); 66 mLstPointF.set(event.getX(newPointerIndex), event.getY(newPointerIndex)); 67 } 68 break; 69 case MotionEvent.ACTION_UP: 70 //最后一根手指也抬起来了 71 mActivePointerId = INVALID_POINTER; 72 mCanDrag = false; 73 break; 74 } 75 return true; 76 } 77 78 @Override 79 protected void onDraw(Canvas canvas) { 80 super.onDraw(canvas); 81 canvas.drawBitmap(mBitmap, mMatrix, mPaint); 82 } 83 }
依然用两根手指依次拖动图片,效果如下所示:
现在可以看到,两根手指正常拖动图片了,毫无违和感。