Android一般实现一个画板功能,都能想到去坚定ontouch方法,然后通过这个方法返回的按下,移动,抬起,的回调监听,不断的更改在屏幕上的X,Y值,然后通过paint在canvas上不断的绘制,然后剩下的就是调整抗锯齿,画笔的平滑度等操作。
没错,大概思路也是如此,所以这篇文章也只是针对当前通用的画板进行细节处理和描述。首先看一下ontouch方法吧,这个方法主要的也是分发的功能。
@Override public boolean onTouchEvent(MotionEvent event) { float x=event.getX(); float y=event.getY(); currentX=x; currentY=y; isTouchUp=false; switch (event.getAction()){ case MotionEvent.ACTION_DOWN: oldx=x;oldy=y; down(x,y); //配合撤销,没有移动也要可以撤销 (仅仅只是点击操作) getParent().requestDisallowInterceptTouchEvent(true); move(x,y); break; case MotionEvent.ACTION_MOVE: getParent().requestDisallowInterceptTouchEvent(true); move(x,y); invalidate(); break; case MotionEvent.ACTION_CANCEL: getParent().requestDisallowInterceptTouchEvent(false); return false; case MotionEvent.ACTION_UP: if (handwriting.hasDraw()){ padUndoStack.push(handwriting); if (mCallBack!=null)mCallBack.onHasDraw(); } up(event.getX(),event.getY()); isTouchUp = true; invalidate(); break; } return true; } private void down(float x,float y){ commCanvas.setBitmap(drawBitmap); createNewPen(); if (handwriting!=null){ handwriting.touchDown(x,y); } if (mCallBack!=null){ mCallBack.onTouchDown(); } } private void move(float x,float y){ handwriting.touchMove(x,y); if (paintType== PaintConstants.PEN_TYPE.ERASER){ handwriting.draw(commCanvas,false); } } private void up(float x,float y){ if (handwriting==null){ return; } handwriting.touchUp(x,y); handwriting.draw(commCanvas,true); final float dx=Math.abs(oldx-x); final float dy=Math.abs(oldy-y); if (dx==0&&dy==0){ handwriting.touchUp(x+1,y); handwriting.draw(commCanvas,true); } }下面要先介绍一下PorterDuff.Mode。下面的操作要灵活使用到这个东西,用来实现橡皮擦功能,和画笔功能,要不然在画板中就会各种凌乱了。
再来看看画笔的这一部分代码:
@Override public void draw(Canvas canvas,boolean isUp) { if (canvas != null) { mFirstCurrentPosition.currentX = mCurrentX; mFirstCurrentPosition.currentY = mCurrentY; int color = mPenPaint.getColor(); if (isUp){ mPenPaint.setXfermode(newPorterDuffXfermode(PorterDuff.Mode.SRC)); if (mPenPaint.getColor()== Color.parseColor("#ffffff")||mPenPaint.getColor()== Color.parseColor("#303030")){ mPenPaint.setAlpha(204); }else { mPenPaint.setAlpha(153); } }else { mPenPaint.setXfermode(newPorterDuffXfermode(PorterDuff.Mode.SRC_OVER)); if (mPenPaint.getColor()== Color.parseColor("#ffffff")||mPenPaint.getColor()== Color.parseColor("#303030")){ mPenPaint.setAlpha(204); }else { mPenPaint.setAlpha(153); } } currentShape.draw(canvas, mPenPaint); } } protected void initPaint(int penSize, int penColor, Style style) { mPenPaint = new Paint(); mPenPaint.setStrokeWidth(penSize); mPenPaint.setColor(penColor); this.penSize = penSize; this.style = style; mPenPaint.setDither(true); mPenPaint.setAntiAlias(true); mPenPaint.setStyle(style); mPenPaint.setStrokeJoin(Paint.Join.ROUND); mPenPaint.setStrokeCap(Paint.Cap.ROUND); mPenPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER)); }这里给画笔的颜色设置的一个透明度,这个主要是最大化的还原在移动过程中和抬起后,所绘制的颜色色差降到最小,要不然在移动画笔过程中,颜色就会特别的深,而且颜色色值都有所改变,现在加个透明度,效果就是感觉画笔像打湿了一样。剩下的paint的一些属性设置,比如抗锯齿之类的,只是让这个画笔更加的润滑。
再来看看橡皮擦的方法的,橡皮擦就比较简单了:
private void setUp() { // color并不中还要,混色的模式决定了eraser mEraserPaint.setColor(Color.BLACK); mEraserPaint.setDither(true); mEraserPaint.setAntiAlias(true); mEraserPaint.setStyle(Paint.Style.STROKE); mEraserPaint.setStrokeJoin(Paint.Join.ROUND); mEraserPaint.setStrokeCap(Paint.Cap.ROUND); mEraserPaint .setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT)); } @Override public void draw(Canvas canvas,boolean isUp) { if (null != canvas) { canvas.drawPath(mPath, mEraserPaint); } }然后各种撤回操作,只是将回执的xy记录在一个list中,撤回时候去取这个记录:
/** * undo */ public void undo() { if (canUndo() && null != mDrawing) { CommHandwriting removedTool = mUndoStack .get(mUndoStack.size() - 1); mRedoStack.add(removedTool); mUndoStack.remove(mUndoStack.size() - 1); renewDraw(); } } /** * redo */ public void redo() { if (canRedo() && null != mDrawing) { CommHandwriting removedTool = mRedoStack .get(mRedoStack.size() - 1); mUndoStack.add(removedTool); mRedoStack.remove(mRedoStack.size() - 1); renewDraw(); } } private void renewDraw(){ if (null != originalBitmap&&!originalBitmap.isRecycled()) { // Set the temporary fore bitmap to canvas. // 当载入文件时保存了一份,现在要重新绘制出来 mDrawing.setTempForeBitmap(mDrawing.originalBitmap); } else { // 如果背景不存在,则重新创建一份背景 mDrawing.createCanvasBitmap(mDrawing.drawingBroadWidth, mDrawing.drawingBroadHeight); } Canvas canvas = mDrawing.commCanvas; // First draw the removed tools from undo stack. for (CommHandwriting paintTool : mOldActionStack) { paintTool.draw(canvas,true); } for (CommHandwriting paintTool : mUndoStack) { paintTool.draw(canvas,true); } mDrawing.invalidate(); }剩下的clearAll的操作,其实也只是使用到了paint的setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));这个方法,也就是上面粘贴出来截图的属性。
那么这样一来一个画板就这么实现了。剩下的只是根据页面进行调用就行了,当然也提供了demo以供下载查看:
https://download.csdn.net/download/greatdaocaoren/12568959