android 画布 轨迹,Android 多点触控,绘制滑动轨迹和十字光标
這個測試項,要捕捉當前有幾個觸摸點,當前觸摸點坐標,滑動事件在x軸、y軸方向的速度等信息,在觸摸時跟隨觸摸點會出現十字光標,繪制出滑動軌跡。
首先繪制出暗色格子背景,采用了自定義View,較為簡單,核心代碼如下:
Paint paint; //畫筆
private int mWidth;
private int mHeight;
public Check(Context context, AttributeSet attrs) {
super(context, attrs);
paint = new Paint();
paint.setColor(getResources().getColor(R.color.deepGray));//設置畫筆顏色
paint.setStrokeJoin(Paint.Join.ROUND);//設置畫筆圖形接觸時筆跡的形狀
paint.setStrokeCap(Paint.Cap.ROUND);//設置畫筆離開畫板時筆跡的形狀
paint.setStrokeWidth(1); //設置畫筆的寬度
}
/**
* 這個方法可以獲得控件的寬高
* @param canvas
*/
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
mWidth = w;
mHeight = h;
}
/**
* 繪制網格線
* @param canvas
*/
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.BLACK);
int lineStart = 80;
int space = lineStart; //長寬間隔
int vertz = lineStart;
int hortz = lineStart;
for (int i = 0; i < 100; i++) {
canvas.drawLine(0, vertz, mWidth, vertz, paint);
canvas.drawLine(hortz, 0, hortz, mHeight, paint);
vertz += space;
hortz += space;
}
}
接下來,因為要在這個背景上畫圖,我在其上覆蓋一層透明ImageView,給該iv設置這個屬性:
android:background="@android:color/transparent"
接下來的繪制滑動軌跡和十字光標都在這個iv上完成。
接下來遇到了一些坑,都踩了一遍。
因為這個繪圖是發生在一個Fragment里,我的繪圖界面要設置全屏,但是該Activity中的其他Fragment則不需要這個設置。于是就在這個Fragment中獲取到Window,然后設置全屏標記,然后讓根視圖MATCH_PARENT。
getActivity().getWindow().setFlags(
WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
//mRootView是BaseFragment中設置的該Fragment的視圖
this.mRootView.setLayoutParams(
new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT));
創建bitmap,設置bitmap的高寬時,遇到了問題。
因為在onCreateView中View.getWidth和View.getHeight無法獲得一個view的高度和寬度,這是因為View組件布局要在onResume回調后完成。所以現在需要使用getViewTreeObserver().addOnGlobalLayoutListener()來獲得寬度或者高度。這是獲得一個view的寬度和高度的方法之一。
OnGlobalLayoutListener 是ViewTreeObserver的內部類,當一個視圖樹的布局發生改變時,可以被ViewTreeObserver監聽到,這是一個注冊監聽視圖樹的觀察者(observer),在視圖樹的全局事件改變時得到通知。ViewTreeObserver不能直接實例化,而是通過getViewTreeObserver()獲得。
mTouchScreenIv.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
mTouchScreenIvWidth = mTouchScreenIv.getWidth();
mTouchScreenIvHeight = mTouchScreenIv.getHeight();
// 創建空白圖片
mBitmap1 = Bitmap.createBitmap(mTouchScreenIvWidth, mTouchScreenIvHeight, Bitmap.Config.ARGB_8888);
mBitmap2 = Bitmap.createBitmap(mTouchScreenIvWidth, mTouchScreenIvHeight, Bitmap.Config.ARGB_8888);
// 創建兩張畫布
mCanvas1 = new Canvas(mBitmap1); //底層畫軌跡的畫布
mCanvas2 = new Canvas(mBitmap2); //上面一層畫十字架的畫布
// 創建畫筆
mPaint1 = new Paint(); //畫軌跡的畫筆
mPaint2 = new Paint(); //畫十字架的畫筆
// 畫筆顏色為藍色
mPaint1.setColor(getResources().getColor(R.color.lightBlue));
mPaint2.setColor(getResources().getColor(R.color.lightBlue));
// 寬度1個像素
mPaint1.setStrokeWidth(1);
mPaint2.setStrokeWidth(1);
// 先將白色背景畫上
mCanvas1.drawBitmap(mBitmap1, new Matrix(), mPaint1);
mCanvas2.drawBitmap(mBitmap2, new Matrix(), mPaint2);
mBitmap3 = mergeBitmap(mBitmap1, mBitmap2);//將兩張bitmap圖合為一張
mTouchScreenIv.setImageBitmap(mBitmap3);
//用完要解除監聽
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
mTouchScreenIv.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
}
});
把兩個bitmap合成一個bitmap
/**
* 把兩個位圖覆蓋合成為一個位圖,以底層位圖的長寬為基準
* @param backBitmap 在底部的位圖
* @param frontBitmap 蓋在上面的位圖
* @return
*/
public Bitmap mergeBitmap(Bitmap backBitmap, Bitmap frontBitmap) {
if (backBitmap == null || backBitmap.isRecycled()
|| frontBitmap == null || frontBitmap.isRecycled()) {
Log.e(TAG, "backBitmap=" + backBitmap + ";frontBitmap=" + frontBitmap);
return null;
}
Bitmap bitmap = backBitmap.copy(Bitmap.Config.ARGB_8888, true);
Canvas canvas = new Canvas(bitmap);
Rect baseRect = new Rect(0, 0, backBitmap.getWidth(), backBitmap.getHeight());
Rect frontRect = new Rect(0, 0, frontBitmap.getWidth(), frontBitmap.getHeight());
canvas.drawBitmap(frontBitmap, frontRect, baseRect, null);
return bitmap;
}
給iv控件設置觸摸事件。因為是多點觸摸事件,所以記錄down的起始點和move時的終止點都需要使用float數組。設置四個大小為10的數組。
添加 觸摸事件的速度檢測器。
單點觸摸事件ACTION_DOWN ,多點觸摸事件ACTION_POINTER_DOWN,使用case穿透將兩種事件一起監聽,遍歷觸摸事件,獲得坐標,進行操作。
@Override
protected void setListener() {
mTouchScreenCheck.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
//當前DOWN或者UP的是手指的index
int curPointerIndex = motionEvent.getActionIndex();
//通過index獲得當前手指的id
int curPointerId = motionEvent.getPointerId(curPointerIndex);
//添加事件的速度計算器
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
mVelocityTracker.addMovement(motionEvent);
int actionMasked = motionEvent.getActionMasked();
Log.i(TAG, "actionMasked === " + actionMasked);
switch (actionMasked) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_DOWN:
mCanvas1.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
mCanvas2.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
mTouchScreenIv.setImageBitmap(mergeBitmap(mBitmap1, mBitmap2));
//設置當前有幾個觸摸點
pointerCount = motionEvent.getPointerCount();
if (pointerCount > 10) {
pointerCount = 10;
}
mActivePointers.append(curPointerId, curPointerId);
//在down事件中的操作
DownPoint(motionEvent);
mTouchScreenIv.setImageBitmap(mergeBitmap(mBitmap1, mBitmap2));
break;
case MotionEvent.ACTION_MOVE:
//獲取當前觸摸事件的個數
if (motionEvent.getPointerCount() > pointerCount) {
pointerCount = motionEvent.getPointerCount();
}
mTouchScreenTvP.setText("P:" + motionEvent.getPointerCount() + "/" + pointerCount);
//清除十字架
mCanvas2.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
//在移動時的操作
MovePoint(motionEvent);
mTouchScreenIv.setImageBitmap(mergeBitmap(mBitmap1, mBitmap2));
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
//計算并顯示坐標偏移量,并且設置背景顏色
setDxDy(motionEvent);
//清除十字架
mCanvas2.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
//清除這個觸摸事件的ID
mActivePointers.remove(curPointerId);
mTouchScreenTvP.setText("P:" + 0 + "/" + pointerCount);
mTouchScreenIv.setImageBitmap(mergeBitmap(mBitmap1, mBitmap2));
break;
}
return true;
}
});
}
/**
* 在down事件中的操作
* @param motionEvent
*/
private void DownPoint(MotionEvent motionEvent) {
for (int i = 0; i < motionEvent.getPointerCount(); i++) {
int pointerId = mActivePointers.get(motionEvent.getPointerId(i));
try {
//獲取觸摸點的X,y坐標
startXs[pointerId] = motionEvent.getX(pointerId);
startYs[pointerId] = motionEvent.getY(pointerId);
finalStartX = startXs[pointerId];
finalStartY = startYs[pointerId];
//設置上面的字變化并且背景顏色為白色
mTouchScreenTvDx.setText("X:" + Math.round(motionEvent.getX(pointerId) * 10) / 10.0);
mTouchScreenTvDx.setBackgroundColor(Color.WHITE);
mTouchScreenTvDy.setText("Y:" + Math.round(motionEvent.getY(pointerId) * 10) / 10.0);
mTouchScreenTvDy.setBackgroundColor(Color.WHITE);
} catch (IllegalArgumentException e) {
// 此處捕捉系統bug,以防程序停止
e.printStackTrace();
}
mTouchScreenTvP.setText("P:" + pointerCount + "/" + pointerCount);
// 在開始和結束坐標間畫一個點
mCanvas1.drawPoint(startXs[pointerId], startYs[pointerId], mPaint1);
//畫十字架
mCanvas2.drawLine(0, startYs[pointerId], mTouchScreenIvWidth, startYs[pointerId], mPaint2);
mCanvas2.drawLine(startXs[pointerId], 0, startXs[pointerId], mTouchScreenIvHeight, mPaint2);
}
}
/**
* 在移動時對點的操作
* @param motionEvent
*/
private void MovePoint(MotionEvent motionEvent) {
for (int i = 0; i < motionEvent.getPointerCount(); i++) {
int pointerId = mActivePointers.get(motionEvent.getPointerId(i));
Log.i(TAG, "1111111 move pointerId" + pointerId);
Log.i(TAG, "1111111 endXS size " + endXs.length);
try {
// 獲取手移動后的坐標
endXs[pointerId] = motionEvent.getX(pointerId);
endYs[pointerId] = motionEvent.getY(pointerId);
// 在開始和結束坐標間畫一條線
mCanvas1.drawLine(startXs[pointerId], startYs[pointerId], endXs[pointerId], endYs[pointerId], mPaint1);
//重新畫十字架
mCanvas2.drawLine(0, endYs[pointerId], mTouchScreenIvWidth, endYs[pointerId], mPaint2);
mCanvas2.drawLine(endXs[pointerId], 0, endXs[pointerId], mTouchScreenIvHeight, mPaint2);
//設置顯示坐標的數字變化并且背景顏色為白色
mTouchScreenTvDx.setText("X:" + Math.round(endXs[pointerId] * 10) / 10.0);
mTouchScreenTvDx.setBackgroundColor(Color.WHITE);
mTouchScreenTvDy.setText("Y:" + Math.round(endYs[pointerId] * 10) / 10.0);
mTouchScreenTvDy.setBackgroundColor(Color.WHITE);
//獲取當前觸摸事件的速度
mVelocityTracker.computeCurrentVelocity(10);
mTouchScreenTvXv.setText("Xv:" +
Math.round(mVelocityTracker.getXVelocity(0) * 1000) / 1000.0 + "");
mTouchScreenTvYv.setText("Yv:" +
Math.round(mVelocityTracker.getYVelocity(0) * 1000) / 1000.0 + "");
// 刷新開始坐標
startXs[pointerId] = (int) motionEvent.getX(pointerId);
startYs[pointerId] = (int) motionEvent.getY(pointerId);
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
Log.i(TAG, "11111:IllegalArgumentException ");
}
}
}
/**
* 計算并顯示坐標偏移量,并且設置背景顏色
* @param motionEvent
*/
private void setDxDy(MotionEvent motionEvent) {
float dx = motionEvent.getX() - finalStartX;
float dy = motionEvent.getY() - finalStartY;
mTouchScreenTvDx.setText("dX:" + Math.round(dx * 10) / 10.0);
if (dx > 0.1 || dx < -0.1) {
mTouchScreenTvDx.setBackgroundColor(Color.RED);
} else {
mTouchScreenTvDx.setBackgroundColor(Color.WHITE);
}
mTouchScreenTvDy.setText("dY:" + Math.round(dy * 10) / 10.0);
if (dy > 0.1 || dy < -0.1) {
mTouchScreenTvDy.setBackgroundColor(Color.RED);
} else {
mTouchScreenTvDy.setBackgroundColor(Color.WHITE);
}
}
ACTION_DOWN :當觸摸到第一個點時,被觸發
ACTION_POINTER_DOWN:當控件上已經有點被觸摸,再次有點被觸摸時,觸發該事件。
ACTION_UP 和 ACTION_POINTER_UP 也是類似的,最后一個點抬起時才觸發ACTION_UP。
但是ACTION_MOVE沒有類似的方法,可以通過遍歷觸摸事件,獲得每一個觸摸事件。
在觸摸時每一個觸摸事件會被分配一個id,通過不同的id獲取每一個觸摸點的坐標。
遺留bug:每當有新的觸摸事件時,以前的滑動軌跡會被清空。
總結
以上是生活随笔為你收集整理的android 画布 轨迹,Android 多点触控,绘制滑动轨迹和十字光标的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: thinkpad E440 无线网卡安装
- 下一篇: Android NDK之Strip裁减s