画板功能实现

    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

有兴趣可以看看下面服务号和订阅号:

猜你喜欢

转载自blog.csdn.net/greatdaocaoren/article/details/107076992
今日推荐