Android 自定義View學(xué)習(xí)(五)——Paint 關(guān)于PorterDuffXfermode學(xué)習(xí)

學(xué)習(xí)資料:

  • Android群英傳

1.PorterDuffXfermode

PorterDuffXfermode有點(diǎn)類似數(shù)學(xué)中的交集,并集,用來(lái)兩個(gè)圖像間的混合顯示模式,設(shè)置的是兩個(gè)圖層交集區(qū)域的顯示方式,dst是下層,先畫(huà)的圖形;src是上層,后畫(huà)的圖形

構(gòu)造方法:

  • PorterDuffXfermode(PorterDuff.Mode mode)

構(gòu)造方法中只需一個(gè)參數(shù),PorterDuff.ModeModePorterDuff這個(gè)類中的一個(gè)枚舉類,現(xiàn)在Mode中有18個(gè)枚舉值,圖中只有16個(gè),圖少了ADDOVERLAY


2.PorterDuff.Mode

PorterDuff.Mode錯(cuò)誤圖

上面的圖是有錯(cuò)誤的,下面的圖,和我自己測(cè)試的結(jié)果是一致的。錯(cuò)誤原因可以去Android中Canvas繪圖之PorterDuffXfermode使用及工作原理詳解看看

修正后的PorterDuff.Mode

最常用的就是DST_INSRC_IN,是可以用來(lái)實(shí)現(xiàn)自定義CircleImageView的一種方式

看圖幫助:后來(lái)者居上

  • 黃色的圓是DST下層,先進(jìn)行繪制;
  • 藍(lán)色的矩形是SRC上層,后進(jìn)行繪制
模式 效果
PorterDuff.Mode.CLEAR 上層繪制不會(huì)提交到畫(huà)布,并把與下層交集部分也清除
PorterDuff.Mode.SRC 顯示上層繪制,此時(shí)下層繪制也會(huì)顯示
PorterDuff.Mode.DST 顯示下層繪制,而上層不會(huì)繪制
PorterDuff.Mode.SRC_OVER 正常顯示,上層疊蓋在下層之上
PorterDuff.Mode.DST_OVER 上下層都顯示,下層在上
PorterDuff.Mode.SRC_IN 將交集顯示在下層繪制的區(qū)域
PorterDuff.Mode.DST_IN 顯示下層繪制
PorterDuff.Mode.SRC_OUT 取非交集區(qū)域
PorterDuff.Mode.DST_OUT 取下層非交集區(qū)域
PorterDuff.Mode.SRC_ATOP 取上層的交集區(qū)域和下層的非交集區(qū)域
PorterDuff.Mode.DST_ATOP 下層在上層之上
PorterDuff.Mode.XOR 去除兩層的交集區(qū)域
PorterDuff.Mode.DARKEN 取兩層全部區(qū)域,交集區(qū)域變暗
PorterDuff.Mode.LIGHTEN 取兩層全部區(qū)域,交集區(qū)域變亮
PorterDuff.Mode.MULTIPLY 取下層全部區(qū)域,交集區(qū)域色彩疊加,正片疊底
PorterDuff.Mode.SCREEN 取兩層全部區(qū)域,交集區(qū)域變透明
PorterDuff.Mode.ADD 飽和度疊加
PorterDuff.Mode.OVERLAY 對(duì)黑白無(wú)效,顯示兩層顏色中和后的中間色

PorterDuff.Mode不單單是進(jìn)行了圖形的操作,有的模式還對(duì)色彩有影響

這些模式最好都自己測(cè)試一下

具體的過(guò)程可以學(xué)習(xí)愛(ài)哥的自定義控件其實(shí)很簡(jiǎn)單1/6

有的模式,不支持硬件加速,最好關(guān)閉硬件加速

繪制過(guò)程后來(lái)者居上,dst為下層,先進(jìn)行繪制;src為上層,后進(jìn)行繪制。

生活中的例子: 雞蛋灌餅 : )


簡(jiǎn)單測(cè)試:

PorterDuff.Mode.MULTIPLY
public class PorterDuffView extends View {
    private Paint paint ;
    private RectF rectF;
    private PorterDuffXfermode porterDuffXfermode;
    public PorterDuffView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initPaint();
    }

    private void initPaint() {
        setLayerType(View.LAYER_TYPE_SOFTWARE, null);//關(guān)閉硬件加速
        paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        rectF = new RectF(150,150,500,500);
       porterDuffXfermode = new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY);
    }

    @Override
  protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //繪制背景色
        canvas.drawColor(Color.YELLOW);
        //創(chuàng)建一個(gè)新的畫(huà)布Layer
        int layerId = canvas.saveLayer(0, 0, getWidth(),getHeight() , null, Canvas.ALL_SAVE_FLAG);
        //繪制dst層
        float x = getWidth() / 4;
        float y = getHeight() / 4;
        float radius = Math.min(getWidth(), getHeight()) / 4;
        paint.setColor(Color.CYAN);
        canvas.drawCircle(x, y, radius, paint);
        //設(shè)置混合模式
         paint.setColor(Color.RED);
        paint.setXfermode(porterDuffXfermode);
        //繪制src層
        canvas.drawRect(rectF,paint);
        paint.setXfermode(null);
        canvas.restoreToCount(layerId);//將自己創(chuàng)建的畫(huà)布Layer繪制到畫(huà)布默認(rèn)的Layer
    }


    /**
     * 測(cè)量
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        int wSpecMode = MeasureSpec.getMode(widthMeasureSpec);
        int wSpecSize = MeasureSpec.getSize(widthMeasureSpec);
        int hSpecMode = MeasureSpec.getMode(heightMeasureSpec);
        int hSpecSize = MeasureSpec.getSize(heightMeasureSpec);

        if (wSpecMode == MeasureSpec.AT_MOST && hSpecMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(300, 300);
        } else if (wSpecMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(300, hSpecSize);
        } else if (hSpecMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(wSpecSize, 300);
        }
    }
}

之所以更改圖層是為了讓繪制不受背景色影響。將繪制的圓形和矩形在一個(gè)新的圖層繪制,繪制完成后,再把新的圖層加入到Canvas的默認(rèn)圖層。


3.CircleImageView

如果只是單單為了顯示一個(gè)圓形的圖片,可以有好多種辦法。

可以借助PorterDuffXfermode或者v4包下的RoundedBitmapDrawable進(jìn)行把Biamp進(jìn)行形狀上的改變得到圓形

若不是經(jīng)常大量的頻繁的顯示出一個(gè)圓形圖片,也可以利用CardViewAndroid 一個(gè)另類的顯示圓形圖片方式

本篇為了學(xué)習(xí)PorterDuffXfermode就使用前面提到的PorterDuff.Mode.SRC_IN


簡(jiǎn)單實(shí)踐:


圓形圖片
public class CircleImageView extends ImageView {

    private Paint mPaint;

    public CircleImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initPaint();

    }

    private void initPaint() {
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    }


    @Override
    protected void onDraw(Canvas canvas) {
        BitmapDrawable drawable = (BitmapDrawable) getDrawable();
        if (drawable != null) {
            Bitmap bitmap = drawable.getBitmap();
            drawTargetBitmap(canvas, bitmap);
        } 
    }


    private void drawTargetBitmap(Canvas canvas, Bitmap bitmap) {
        final int sc = canvas.saveLayer(0, 0, getWidth(),getHeight(), null, Canvas.ALL_SAVE_FLAG);
        //先繪制dst層
        final float x = getWidth() / 2;
        final float y = getHeight() / 2;
        final float radius = Math.min(getWidth(), getHeight()) / 2;
        canvas.drawCircle(x, y, radius, mPaint);
        //設(shè)置混合模式
        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
        //繪制src層
        final float f_x = getWidth()  / 2 -bitmap.getWidth() / 2;
        final float f_y = getHeight() / 2 -bitmap.getHeight() / 2;
        canvas.drawBitmap(bitmap, f_x,f_y, mPaint);
        // 還原混合模式
        mPaint.setXfermode(null);
        // 還原畫(huà)布
        canvas.restoreToCount(sc);
    }
}

一個(gè)超級(jí)簡(jiǎn)單的自定義CircleImageView


4.最后

簡(jiǎn)單介紹完畢,主要就是18種模式的理解,最好每種模式的效果都能夠明白,掌握最常用的DST_INSRC_IN

進(jìn)一步學(xué)習(xí),可以看鴻洋大神的Android 自定義控件實(shí)現(xiàn)刮刮卡效果 真的就只是刮刮卡么


5. 補(bǔ)充

9月9號(hào) 晚上

關(guān)于PorterDuffXfermode的坑總結(jié)的很好,PorterDuffXfermode不正確的真正原因


本人還很菜,有錯(cuò)誤,請(qǐng)指出

共勉 : )

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容