Android畫板 半透明畫筆 筆跡疊加效果

轉載請註明出處:https://blog.csdn.net/kong_gu_you_lan/article/details/105572617

本文出自 容華謝後的博客

0.寫在前面

先看下效果圖,功能雖然簡單,但是實現的時候谷歌、百度了很久也沒有找到解決方案,提這個問題的人不少,但是回答的人一個也沒有,十分鬱悶,在此記錄,分享給各位。

疊加效果

1.半透明畫筆

先按照常規的方法實現一個簡單的畫板:

public class SketchpadView extends View {

    private Paint mPaint;
    private Path mPath;
    private float mLastX;
    private float mLastY;
    private Bitmap mBufferBitmap;
    private Canvas mBufferCanvas;

    public SketchpadView(Context context) {
        this(context, null);
    }

    public SketchpadView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public SketchpadView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        // 抗鋸齒、防抖動
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
        // 畫筆模式爲描邊
        mPaint.setStyle(Paint.Style.STROKE);
        // 拐角爲圓角
        mPaint.setStrokeJoin(Paint.Join.ROUND);
        // 兩端爲圓角
        mPaint.setStrokeCap(Paint.Cap.ROUND);
        // 畫筆寬度
        mPaint.setStrokeWidth(50);
        // 畫筆顏色
        mPaint.setColor(getResources().getColor(R.color.colorAccent));
        // 畫筆透明度,先設置顏色,再設置透明度0-255
        mPaint.setAlpha(80);
        // 筆跡路徑
        mPath = new Path();
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        // 雙緩存機制
        mBufferBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_4444);
        mBufferCanvas = new Canvas(mBufferBitmap);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (mBufferBitmap != null) {
            canvas.drawBitmap(mBufferBitmap, 0, 0, null);
        }
    }

    @SuppressLint("ClickableViewAccessibility")
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        float x = event.getX();
        float y = event.getY();
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mLastX = x;
                mLastY = y;
                mPath.moveTo(x, y);
                break;

            case MotionEvent.ACTION_MOVE:
                mPath.quadTo(mLastX, mLastY, (x + mLastX) / 2, (y + mLastY) / 2);
                mBufferCanvas.drawPath(mPath, mPaint);
                mLastX = x;
                mLastY = y;
                invalidate();
                break;

            case MotionEvent.ACTION_UP:
                mPath.reset();
                invalidate();
                break;
        }
        return true;
    }
}

注意:在初始化畫筆的時候,我們給畫筆設置了80的透明度,透明度一定要在顏色之後設置,因爲顏色中也存在透明通道,會覆蓋已設置的透明度。

// 畫筆顏色
mPaint.setColor(getResources().getColor(R.color.colorAccent));
// 畫筆透明度,先設置顏色,再設置透明度0-255
mPaint.setAlpha(80);

看下效果:

顏色漸變

咦,爲什麼還一段一段的顏色漸變?

這裏需要了解下Paint類中的一個很重要的方法setXfermode(Xfermode xfermode),參數Xfermode有三個子類:AvoidXfermode、PixelXorXfermode和PorterDuffXfermode,前兩個類在API 16被遺棄了,重點看下PorterDuffXfermode這個類,PorterDuffXfermode類主要用於圖形合成時的圖像過渡模式計算,共有18種過渡模式,本文重點看下其中的兩種模式SRC_OVER、SRC。

如果沒有調用setXfermode方法,Paint繪製默認採用的是SRC_OVER模式,即先繪製第一筆,繪製第二筆時,第二筆與第一筆重合的部分,會進行疊加,顏色會變得越來越深。

這就解釋了爲什麼上面的效果中,會出現一段一段的的顏色漸變筆跡,是因爲半透明的筆跡疊加了,現在我們將模式修改爲SRC,即重合的部分只顯示第二筆,看下效果:

無疊加效果

透明效果出來了,但是存在一個問題,就是每段筆跡重合的部分,沒有疊加顏色變深的效果,繼續往下看。

2.筆跡疊加

修改下代碼:

public class SketchpadView extends View {

    private Paint mPaint;
    private Path mPath;
    private float mLastX;
    private float mLastY;
    private Bitmap mBufferBitmap;
    private Canvas mBufferCanvas;

    public SketchpadView(Context context) {
        this(context, null);
    }

    public SketchpadView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public SketchpadView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        // 抗鋸齒、防抖動
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
        // 畫筆模式爲描邊
        mPaint.setStyle(Paint.Style.STROKE);
        // 拐角爲圓角
        mPaint.setStrokeJoin(Paint.Join.ROUND);
        // 兩端爲圓角
        mPaint.setStrokeCap(Paint.Cap.ROUND);
        // 畫筆寬度
        mPaint.setStrokeWidth(50);
        // 畫筆顏色
        mPaint.setColor(getResources().getColor(R.color.colorAccent));
        // 畫筆透明度,先設置顏色,再設置透明度0-255
        mPaint.setAlpha(80);
        // 筆跡路徑
        mPath = new Path();
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        // 雙緩存機制
        mBufferBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_4444);
        mBufferCanvas = new Canvas(mBufferBitmap);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (mBufferBitmap != null) {
            canvas.drawBitmap(mBufferBitmap, 0, 0, null);
        }
        // 修改1
        // ACTION_MOVE時,將筆跡臨時繪製在畫布上
        canvas.drawPath(mPath, mPaint);
    }

    @SuppressLint("ClickableViewAccessibility")
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        float x = event.getX();
        float y = event.getY();
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mLastX = x;
                mLastY = y;
                mPath.moveTo(x, y);
                break;

            case MotionEvent.ACTION_MOVE:
                mPath.quadTo(mLastX, mLastY, (x + mLastX) / 2, (y + mLastY) / 2);
                // 修改2
                // 將繪製方法移到ACTION_UP中
                mLastX = x;
                mLastY = y;
                invalidate();
                break;

            case MotionEvent.ACTION_UP:
            	// 修改3
                // ACTION_UP時,將當前一筆的筆跡,繪製到緩存畫布上
                mBufferCanvas.drawPath(mPath, mPaint);
                mPath.reset();
                invalidate();
                break;
        }
        return true;
    }
}

修改後的代碼,沒有設置Paint的圖像模式,默認爲疊加狀態,在ACTION_MOVE時,沒有將筆跡實時繪製在緩存畫布上,而是臨時繪製在實際畫布上,ACTION_UP時再將當前一筆的筆跡一次性繪製到緩存畫布,然後再繪製到實際畫布中,這樣一筆一筆下來,就會產生疊加的筆跡效果了,看下最終效果:

疊加效果

3.寫在最後

到這裏半透明畫筆的筆跡疊加效果就介紹完了,如有錯誤或者遺漏的地方可以給我留言評論,謝謝!

代碼已上傳至GitHub,歡迎Star、Fork!

GitHub地址:https://github.com/alidili/Demos/tree/master/TranslucentPaintDemo

本文Demo的Apk下載地址:https://github.com/alidili/Demos/raw/master/TranslucentPaintDemo/TranslucentPaintDemo.apk

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章