android 画布 轨迹,Android 多点触控,绘制滑动轨迹和十字光标
這個(gè)測(cè)試項(xiàng),要捕捉當(dāng)前有幾個(gè)觸摸點(diǎn),當(dāng)前觸摸點(diǎn)坐標(biāo),滑動(dòng)事件在x軸、y軸方向的速度等信息,在觸摸時(shí)跟隨觸摸點(diǎn)會(huì)出現(xiàn)十字光標(biāo),繪制出滑動(dòng)軌跡。
首先繪制出暗色格子背景,采用了自定義View,較為簡(jiǎn)單,核心代碼如下:
Paint paint; //畫(huà)筆
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));//設(shè)置畫(huà)筆顏色
paint.setStrokeJoin(Paint.Join.ROUND);//設(shè)置畫(huà)筆圖形接觸時(shí)筆跡的形狀
paint.setStrokeCap(Paint.Cap.ROUND);//設(shè)置畫(huà)筆離開(kāi)畫(huà)板時(shí)筆跡的形狀
paint.setStrokeWidth(1); //設(shè)置畫(huà)筆的寬度
}
/**
* 這個(gè)方法可以獲得控件的寬高
* @param canvas
*/
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
mWidth = w;
mHeight = h;
}
/**
* 繪制網(wǎng)格線
* @param canvas
*/
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.BLACK);
int lineStart = 80;
int space = lineStart; //長(zhǎng)寬間隔
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;
}
}
接下來(lái),因?yàn)橐谶@個(gè)背景上畫(huà)圖,我在其上覆蓋一層透明ImageView,給該iv設(shè)置這個(gè)屬性:
android:background="@android:color/transparent"
接下來(lái)的繪制滑動(dòng)軌跡和十字光標(biāo)都在這個(gè)iv上完成。
接下來(lái)遇到了一些坑,都踩了一遍。
因?yàn)檫@個(gè)繪圖是發(fā)生在一個(gè)Fragment里,我的繪圖界面要設(shè)置全屏,但是該Activity中的其他Fragment則不需要這個(gè)設(shè)置。于是就在這個(gè)Fragment中獲取到Window,然后設(shè)置全屏標(biāo)記,然后讓根視圖MATCH_PARENT。
getActivity().getWindow().setFlags(
WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
//mRootView是BaseFragment中設(shè)置的該Fragment的視圖
this.mRootView.setLayoutParams(
new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT));
創(chuàng)建bitmap,設(shè)置bitmap的高寬時(shí),遇到了問(wèn)題。
因?yàn)樵趏nCreateView中View.getWidth和View.getHeight無(wú)法獲得一個(gè)view的高度和寬度,這是因?yàn)閂iew組件布局要在onResume回調(diào)后完成。所以現(xiàn)在需要使用getViewTreeObserver().addOnGlobalLayoutListener()來(lái)獲得寬度或者高度。這是獲得一個(gè)view的寬度和高度的方法之一。
OnGlobalLayoutListener 是ViewTreeObserver的內(nèi)部類,當(dāng)一個(gè)視圖樹(shù)的布局發(fā)生改變時(shí),可以被ViewTreeObserver監(jiān)聽(tīng)到,這是一個(gè)注冊(cè)監(jiān)聽(tīng)視圖樹(shù)的觀察者(observer),在視圖樹(shù)的全局事件改變時(shí)得到通知。ViewTreeObserver不能直接實(shí)例化,而是通過(guò)getViewTreeObserver()獲得。
mTouchScreenIv.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
mTouchScreenIvWidth = mTouchScreenIv.getWidth();
mTouchScreenIvHeight = mTouchScreenIv.getHeight();
// 創(chuàng)建空白圖片
mBitmap1 = Bitmap.createBitmap(mTouchScreenIvWidth, mTouchScreenIvHeight, Bitmap.Config.ARGB_8888);
mBitmap2 = Bitmap.createBitmap(mTouchScreenIvWidth, mTouchScreenIvHeight, Bitmap.Config.ARGB_8888);
// 創(chuàng)建兩張畫(huà)布
mCanvas1 = new Canvas(mBitmap1); //底層畫(huà)軌跡的畫(huà)布
mCanvas2 = new Canvas(mBitmap2); //上面一層畫(huà)十字架的畫(huà)布
// 創(chuàng)建畫(huà)筆
mPaint1 = new Paint(); //畫(huà)軌跡的畫(huà)筆
mPaint2 = new Paint(); //畫(huà)十字架的畫(huà)筆
// 畫(huà)筆顏色為藍(lán)色
mPaint1.setColor(getResources().getColor(R.color.lightBlue));
mPaint2.setColor(getResources().getColor(R.color.lightBlue));
// 寬度1個(gè)像素
mPaint1.setStrokeWidth(1);
mPaint2.setStrokeWidth(1);
// 先將白色背景畫(huà)上
mCanvas1.drawBitmap(mBitmap1, new Matrix(), mPaint1);
mCanvas2.drawBitmap(mBitmap2, new Matrix(), mPaint2);
mBitmap3 = mergeBitmap(mBitmap1, mBitmap2);//將兩張bitmap圖合為一張
mTouchScreenIv.setImageBitmap(mBitmap3);
//用完要解除監(jiān)聽(tīng)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
mTouchScreenIv.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
}
});
把兩個(gè)bitmap合成一個(gè)bitmap
/**
* 把兩個(gè)位圖覆蓋合成為一個(gè)位圖,以底層位圖的長(zhǎng)寬為基準(zhǔn)
* @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控件設(shè)置觸摸事件。因?yàn)槭嵌帱c(diǎn)觸摸事件,所以記錄down的起始點(diǎn)和move時(shí)的終止點(diǎn)都需要使用float數(shù)組。設(shè)置四個(gè)大小為10的數(shù)組。
添加 觸摸事件的速度檢測(cè)器。
單點(diǎn)觸摸事件ACTION_DOWN ,多點(diǎn)觸摸事件ACTION_POINTER_DOWN,使用case穿透將兩種事件一起監(jiān)聽(tīng),遍歷觸摸事件,獲得坐標(biāo),進(jìn)行操作。
@Override
protected void setListener() {
mTouchScreenCheck.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
//當(dāng)前DOWN或者UP的是手指的index
int curPointerIndex = motionEvent.getActionIndex();
//通過(guò)index獲得當(dāng)前手指的id
int curPointerId = motionEvent.getPointerId(curPointerIndex);
//添加事件的速度計(jì)算器
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));
//設(shè)置當(dāng)前有幾個(gè)觸摸點(diǎn)
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:
//獲取當(dāng)前觸摸事件的個(gè)數(shù)
if (motionEvent.getPointerCount() > pointerCount) {
pointerCount = motionEvent.getPointerCount();
}
mTouchScreenTvP.setText("P:" + motionEvent.getPointerCount() + "/" + pointerCount);
//清除十字架
mCanvas2.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
//在移動(dòng)時(shí)的操作
MovePoint(motionEvent);
mTouchScreenIv.setImageBitmap(mergeBitmap(mBitmap1, mBitmap2));
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
//計(jì)算并顯示坐標(biāo)偏移量,并且設(shè)置背景顏色
setDxDy(motionEvent);
//清除十字架
mCanvas2.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
//清除這個(gè)觸摸事件的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 {
//獲取觸摸點(diǎn)的X,y坐標(biāo)
startXs[pointerId] = motionEvent.getX(pointerId);
startYs[pointerId] = motionEvent.getY(pointerId);
finalStartX = startXs[pointerId];
finalStartY = startYs[pointerId];
//設(shè)置上面的字變化并且背景顏色為白色
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) {
// 此處捕捉系統(tǒng)bug,以防程序停止
e.printStackTrace();
}
mTouchScreenTvP.setText("P:" + pointerCount + "/" + pointerCount);
// 在開(kāi)始和結(jié)束坐標(biāo)間畫(huà)一個(gè)點(diǎn)
mCanvas1.drawPoint(startXs[pointerId], startYs[pointerId], mPaint1);
//畫(huà)十字架
mCanvas2.drawLine(0, startYs[pointerId], mTouchScreenIvWidth, startYs[pointerId], mPaint2);
mCanvas2.drawLine(startXs[pointerId], 0, startXs[pointerId], mTouchScreenIvHeight, mPaint2);
}
}
/**
* 在移動(dòng)時(shí)對(duì)點(diǎn)的操作
* @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 {
// 獲取手移動(dòng)后的坐標(biāo)
endXs[pointerId] = motionEvent.getX(pointerId);
endYs[pointerId] = motionEvent.getY(pointerId);
// 在開(kāi)始和結(jié)束坐標(biāo)間畫(huà)一條線
mCanvas1.drawLine(startXs[pointerId], startYs[pointerId], endXs[pointerId], endYs[pointerId], mPaint1);
//重新畫(huà)十字架
mCanvas2.drawLine(0, endYs[pointerId], mTouchScreenIvWidth, endYs[pointerId], mPaint2);
mCanvas2.drawLine(endXs[pointerId], 0, endXs[pointerId], mTouchScreenIvHeight, mPaint2);
//設(shè)置顯示坐標(biāo)的數(shù)字變化并且背景顏色為白色
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);
//獲取當(dāng)前觸摸事件的速度
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 + "");
// 刷新開(kāi)始坐標(biāo)
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 ");
}
}
}
/**
* 計(jì)算并顯示坐標(biāo)偏移量,并且設(shè)置背景顏色
* @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 :當(dāng)觸摸到第一個(gè)點(diǎn)時(shí),被觸發(fā)
ACTION_POINTER_DOWN:當(dāng)控件上已經(jīng)有點(diǎn)被觸摸,再次有點(diǎn)被觸摸時(shí),觸發(fā)該事件。
ACTION_UP 和 ACTION_POINTER_UP 也是類似的,最后一個(gè)點(diǎn)抬起時(shí)才觸發(fā)ACTION_UP。
但是ACTION_MOVE沒(méi)有類似的方法,可以通過(guò)遍歷觸摸事件,獲得每一個(gè)觸摸事件。
在觸摸時(shí)每一個(gè)觸摸事件會(huì)被分配一個(gè)id,通過(guò)不同的id獲取每一個(gè)觸摸點(diǎn)的坐標(biāo)。
遺留bug:每當(dāng)有新的觸摸事件時(shí),以前的滑動(dòng)軌跡會(huì)被清空。
總結(jié)
以上是生活随笔為你收集整理的android 画布 轨迹,Android 多点触控,绘制滑动轨迹和十字光标的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: thinkpad E440 无线网卡安装
- 下一篇: 高频电子线路——第三章高频小信号放大器