生活随笔
收集整理的這篇文章主要介紹了
android多点触控的理解
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
首先多點(diǎn)觸控要使用event.getActionMasked()來獲取事件,調(diào)用情況如下:
- case MotionEvent.ACTION_DOWN: //第一根手指按下觸發(fā),只會觸發(fā)一次
- case MotionEvent.ACTION_MOVE: //所有手指的move事件都會觸發(fā)這個(gè)事件
- case MotionEvent.ACTION_UP: //只會觸發(fā)一次,最后一根手指抬起時(shí)觸發(fā)
- case MotionEvent.ACTION_POINTER_DOWN: //非第一跟手指按下觸發(fā)
- case MotionEvent.ACTION_POINTER_UP: //非最后一根手指抬起觸發(fā)
接下來看一段代碼和效果
public class MultiTouchView extends View {private float offsetX
, offsetY
;private float lastOffsetX
, lastOffsetY
;private float downX
, downY
;Bitmap bitmap
;Paint paint
;float currentScale
;private int currentPointId
;。。。省略
@Overrideprotected void onSizeChanged(int w
, int h
, int oldw
, int oldh
) {super.onSizeChanged(w
, h
, oldw
, oldh
);bitmap
= BitmapFactory.decodeResource(getResources(), R.mipmap
.test3
);paint
= new Paint();if ((float) bitmap
.getWidth() / bitmap
.getHeight() > (float) getWidth() / getHeight()) {currentScale
= (float) getWidth() / bitmap
.getWidth();} else {currentScale
= (float) getHeight() / bitmap
.getHeight();}}@Overrideprotected void onDraw(Canvas canvas
) {super.onDraw(canvas
);canvas
.translate(offsetX
, offsetY
);canvas
.scale(currentScale
, currentScale
, getWidth() / 2f, getHeight() / 2f);canvas
.drawBitmap(bitmap
, (getWidth() - bitmap
.getWidth()) / 2f, (getHeight() - bitmap
.getHeight()) / 2f, paint
);}@Overridepublic boolean onTouchEvent(MotionEvent event
) {switch (event
.getActionMasked()) { case MotionEvent.ACTION_DOWN
:downX
= event
.getX();downY
= event
.getY();currentPointId
=0;break;case MotionEvent.ACTION_MOVE
:offsetX
= lastOffsetX
+ event
.getX() - downX
;offsetY
= lastOffsetY
+ event
.getY() - downY
;invalidate();break;case MotionEvent.ACTION_UP
:lastOffsetX
= offsetX
;lastOffsetY
= offsetY
;break;case MotionEvent.ACTION_POINTER_DOWN
:break;case MotionEvent.ACTION_POINTER_UP
:break;}return true;}
}
自定義了一個(gè)view,onDraw繪制了一張圖片,在ACTION_MOVE的時(shí)候offsetX(Y)達(dá)到圖片隨手指滑動的效果:
我這里的操作是
先在右上角按下手指1,滑動,圖片能跟隨手指滑動接著在左下角按下手指2并滑動發(fā)現(xiàn)手指2沒法滑動圖片(依舊只會跟隨手指1滑動)然后松開手指1,這時(shí)候圖片跳到左下角并能跟隨手指2滑動了最后再按下手指1,圖片跳到了右上角并跟隨手指1滑動
為什么第二步按下手指2沒法滑動圖片呢?來看MotionEvent.ACTION_MOVE中的代碼:
offsetX
= lastOffsetX
+ event
.getX() - downX
問題就在這個(gè)event.getX(),查看MotionEvent 的源碼:
第二個(gè)參數(shù)pointerIndex用的是默認(rèn)值0,一番查閱后發(fā)現(xiàn):
每根手指按下后系統(tǒng)都會保存該手指的index和id,移除一根手指后index會重新排序,原id不變。當(dāng)插入一個(gè)手指時(shí)會根據(jù)id列表進(jìn)行插入操作,如 下圖:
當(dāng)依次按下四根手指時(shí)四根手指的id和index相同,依次是0123,當(dāng)移除第二根手指,那么將會重新排序,第三根手指的index變?yōu)?,id不變,第四根手指的index變?yōu)?,id不變。這時(shí)候再按下一個(gè)手指它會發(fā)現(xiàn)id只有0、2、3,于是生成了id為1的手指,然后重新排序變成跟最開始的狀態(tài)一樣。
回頭看之前的操作,依次按下手指1和2時(shí)生成了兩個(gè):手指1(id=0,index=0),手指2(id=1,index=1)
因此,在上面的操作的第二步中,因?yàn)锳ction_move中event.getX() 始終用的index都是0,所以處理的都是第一根手指,第二根手指自然無法拉動圖片;
第三步中,因?yàn)橐瞥说谝桓种?#xff0c;進(jìn)行了重新排序,手指2的index變成了0,所以手指2能拖動圖片
第四步,手指1重新按下發(fā)現(xiàn)當(dāng)前只有手指2(id=1),并沒有id0,因此創(chuàng)建了一個(gè)手指id為0,再重新排序的時(shí)候變成了手指1(id=0,index=0),手指2(id=1,index=1),手指1的index為0,因此是手指1能拖動圖片。
接下來實(shí)踐一下,將上面的View改為:最后按下的手指拉動圖片
思路:記錄最后按下手指的id,根據(jù)手指的id獲取index,ACTION_MOVE中event.getX/Y()傳入index:
case MotionEvent.ACTION_DOWN
:downX
= event
.getX();downY
= event
.getY();currentPointId
=0;break;
case MotionEvent.ACTION_MOVE
:int index
= event
.findPointerIndex(currentPointId
);offsetX
= lastOffsetX
+ event
.getX(index
) - downX
;offsetY
= lastOffsetY
+ event
.getY(index
) - downY
;invalidate();break;case MotionEvent.ACTION_UP
:lastOffsetX
= offsetX
;lastOffsetY
= offsetY
;break;
case MotionEvent.ACTION_POINTER_DOWN
:int pointerIndex
= event
.getActionIndex();currentPointId
=event
.getPointerId(pointerIndex
);downX
=event
.getX(pointerIndex
);downY
=event
.getY(pointerIndex
);lastOffsetX
= offsetX
;lastOffsetY
= offsetY
;break;
在ACTION_DOWN時(shí)記錄了當(dāng)前id:currentPointId=0(因?yàn)槭堑谝粋€(gè)手指id必然是0),在ACTION_POINTER_DOWN即非第一根手指按下的時(shí)候通過getActionIndex、event.getPointerId(pointerIndex)得到按下的id,最后在ACTION_MOVE事件中通過findPointerIndex(currentPointId)找到當(dāng)前id的index,這樣就保證了index為最后按下的手指index。
到此就實(shí)現(xiàn)了最后按下的手指拉動圖片的效果,但是引入了新的問題:當(dāng)拖動圖片的那根手指抬起來后會出現(xiàn)閃退。
這是因?yàn)閑vent.findPointerIndex(currentPointId)數(shù)組越界,因?yàn)槭种柑鸷骳urrentPointId被移除了,通過這個(gè)id找index自然會報(bào)錯(cuò)。在ACTION_POINTER_UP中進(jìn)行處理:
case MotionEvent.ACTION_POINTER_UP
:int upIndex
= event
.getActionIndex();int pointerUpId
= event
.getPointerId(upIndex
);if (pointerUpId
== currentPointId
) {if (upIndex
== event
.getPointerCount() - 1) {upIndex
= event
.getPointerCount() - 2;} else {upIndex
++;}currentPointId
= event
.getPointerId(upIndex
);downX
= event
.getX(upIndex
);downY
= event
.getY(upIndex
);lastOffsetX
= offsetX
;lastOffsetY
= offsetY
;}break;
當(dāng)手指抬起時(shí)獲取手指id,如果id是正在控制拖動圖片的手指id,那么如果該id是最后一個(gè)手指則將index回退一個(gè)(將拖動交給上一根手指處理),如果該id不是最后一個(gè),那么index++,即交給下一根手指處理。
最后剩下新手指按下圖片跳動的問題是因?yàn)槭种赴聪聲r(shí)沒有記錄當(dāng)前的downX(Y)和lastOffsetX (Y)。
完整代碼---------------------------------------------------------------------------------------->
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;import androidx.annotation.Nullable;public class MultiTouchView extends View {private float offsetX
, offsetY
;private float lastOffsetX
, lastOffsetY
;private float downX
, downY
;Bitmap bitmap
;Paint paint
;float currentScale
;private int currentPointId
;public MultiTouchView(Context context
) {this(context
, null);}public MultiTouchView(Context context
, @Nullable AttributeSet attrs
) {this(context
, attrs
, 0);}public MultiTouchView(Context context
, @Nullable AttributeSet attrs
, int defStyleAttr
) {super(context
, attrs
, defStyleAttr
);}@Overrideprotected void onSizeChanged(int w
, int h
, int oldw
, int oldh
) {super.onSizeChanged(w
, h
, oldw
, oldh
);bitmap
= BitmapFactory.decodeResource(getResources(), R.mipmap
.test3
);paint
= new Paint();if ((float) bitmap
.getWidth() / bitmap
.getHeight() > (float) getWidth() / getHeight()) {currentScale
= (float) getWidth() / bitmap
.getWidth();} else {currentScale
= (float) getHeight() / bitmap
.getHeight();}}@Overrideprotected void onDraw(Canvas canvas
) {super.onDraw(canvas
);canvas
.translate(offsetX
, offsetY
);canvas
.scale(currentScale
, currentScale
, getWidth() / 2f, getHeight() / 2f);canvas
.drawBitmap(bitmap
, (getWidth() - bitmap
.getWidth()) / 2f, (getHeight() - bitmap
.getHeight()) / 2f, paint
);}@Overridepublic boolean onTouchEvent(MotionEvent event
) {switch (event
.getActionMasked()) { case MotionEvent.ACTION_DOWN
:downX
= event
.getX();downY
= event
.getY();currentPointId
= 0;break;case MotionEvent.ACTION_MOVE
:int index
= event
.findPointerIndex(currentPointId
);offsetX
= lastOffsetX
+ event
.getX(index
) - downX
;offsetY
= lastOffsetY
+ event
.getY(index
) - downY
;invalidate();break;case MotionEvent.ACTION_UP
:lastOffsetX
= offsetX
;lastOffsetY
= offsetY
;break;case MotionEvent.ACTION_POINTER_DOWN
:int pointerIndex
= event
.getActionIndex();currentPointId
= event
.getPointerId(pointerIndex
);downX
= event
.getX(pointerIndex
);downY
= event
.getY(pointerIndex
);lastOffsetX
= offsetX
;lastOffsetY
= offsetY
;break;case MotionEvent.ACTION_POINTER_UP
:int upIndex
= event
.getActionIndex();int pointerUpId
= event
.getPointerId(upIndex
);if (pointerUpId
== currentPointId
) {if (upIndex
== event
.getPointerCount() - 1) {upIndex
= event
.getPointerCount() - 2;} else {upIndex
++;}currentPointId
= event
.getPointerId(upIndex
);downX
= event
.getX(upIndex
);downY
= event
.getY(upIndex
);lastOffsetX
= offsetX
;lastOffsetY
= offsetY
;}break;}return true;}
}
最終效果:
總結(jié)
以上是生活随笔為你收集整理的android多点触控的理解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。