前言

之前写过一个Android的画图类,自定义View实现的,其中的擦除效果很不自然,每次擦除都会将线条整条删除,而不是手指指到哪里就擦除哪里,很不自然。一直没有想明白如何做,直到看到了这篇文章:

。 结合文章的方法和自己的理解进行了重写,完成了安卓的画图效果。

Android删除数据表_Math

绘制顺序

如上图所示,首先是绘制的顺序:
原理很简单,首先我们将底层的Bitmap绘制出来,也就是绘制mCacheBitmap。
然后绘制列表中的路径,这是可以正常显示的路径,也就是绘制mPathList的内容。
最后设置了mErasePaint的Xfermode后进行下一步的绘制,我们通过如下设置mPorterDuffXfermode来设置DST_OUT的模式:

mPorterDuffXfermode = new PorterDuffXfermode(PorterDuff.Mode.DST_OUT);
        mErasePaint.setXfermode(mPorterDuffXfermode);

这也就是擦除的效果,可以参考前面贴出的博客,于是就绘制了mEraseList的内容。
更新:为了提高整体的速度,我们也可以使用clipRect方法。

private void createBitmap() {
        mBitmap = Bitmap.createBitmap(getMeasuredWidth(), getMeasuredHeight(), Bitmap.Config.ARGB_8888);
        mCanvas = new Canvas(mBitmap);
    }
    public void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        // 提高速度
        canvas.clipRect(getPaddingLeft(), getPaddingTop(),
                getRight(), getBottom());
        if (mCacheBitmap != null)
            mCanvas.drawBitmap(mCacheBitmap, 0, 0, null);
        if (mPathList != null) {
            for (int i = 0; i < mPathList.size(); i++) {
                PathInfo pathInfo = mPathList.get(i);
                mPaint.setStrokeWidth(pathInfo.width);
                mPaint.setColor(pathInfo.color);

                mCanvas.drawPath(pathInfo.path, mPaint);
            }
        }
        mErasePaint.setXfermode(mPorterDuffXfermode);
        mErasePaint.setStyle(Paint.Style.STROKE);
        for (int i = 0; i < mEraseList.size(); i++) {
            mErasePaint.setStrokeWidth(mEraseList.get(i).width);
            mErasePaint.setColor(mEraseList.get(i).color);
            mCanvas.drawPath(mEraseList.get(i).path, mErasePaint);
        }
        canvas.drawBitmap(mBitmap, 0, 0, null);
    }

切换

当我们从绘制切换到擦除时,是不需要多余的操作的,因为我们的绘制过程是正常的绘制,直接绘制在画板上就可以了,而擦除则需要设置特殊的模式,两相结合也是没有问题的,因为我们的擦除是后绘制的。
而当从擦除切换到绘制时,就会有比较大的问题了,在切换时,我们首先要将目前的View的Bitmap导出,这样后面无论如何绘制都与前面的无关,不会有任何影响。
导出View的Bitmap:

private void createViewBitmap() {
        setDrawingCacheEnabled(true);
        buildDrawingCache();
        Bitmap bitmap = getDrawingCache();
        if (bitmap != null)
            mCacheBitmap = Bitmap.createBitmap(bitmap);
        destroyDrawingCache();
        setDrawingCacheEnabled(false);
    }

然后要将绘制的路径列表和擦除列表都清空,去除前面的影响。然后再进行绘制。

路径判断

这里主要是讲讲如何进行Android的绘制,也就是触摸事件。首先我们是获得当前的点,并且要新建一个Path对象,这里我封装在PathInfo里了。:
手指按下事件:

private void touch_down(float x, float y) {
        mX = x;
        mY = y;
        Log.d("tag", x + " " + y);

        mPathInfo = new PathInfo();
        mPathInfo.path.moveTo(x, y);
        mPathInfo.width = this.mWidth;
        if (this.mMode == MODE_DRAW) {
            mPathInfo.color = this.mColor;
            mPathList.add(mPathInfo);
        } else if (this.mMode == MODE_ERASE) {
            mPathInfo.color = Color.WHITE;
            mEraseList.add(mPathInfo);
        }
    }

手指移动事件:

每次调用lineTo方法就能绘制从起点到当前点的路径了。

注意要设置画笔的模式是STROKE。参考:

private void touch_move(float x, float y) {
        float dx = Math.abs(x - mX);
        float dy = Math.abs(y - mY);
        if (dx >= MAX_TOUCH || dy >= MAX_TOUCH) {
            mPathInfo.path.lineTo(x, y);
        }
    }

onTouchEvent:

public boolean onTouchEvent(MotionEvent event) {
        float x = event.getX();
        float y = event.getY();

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                touch_down(x, y);
                break;
            case MotionEvent.ACTION_MOVE:
                touch_move(x, y);
                break;
        }
        invalidate();
        return true;
    }

效果图:

Android删除数据表_i++_02