Android 自定義View學習(五)——Paint 關于PorterDuffXfermode學習

學習資料:

  • Android群英傳

1.PorterDuffXfermode

PorterDuffXfermode有點類似數學中的交集,并集,用來兩個圖像間的混合顯示模式,設置的是兩個圖層交集區域的顯示方式,dst是下層,先畫的圖形;src是上層,后畫的圖形

構造方法:

  • PorterDuffXfermode(PorterDuff.Mode mode)

構造方法中只需一個參數,PorterDuff.ModeModePorterDuff這個類中的一個枚舉類,現在Mode中有18個枚舉值,圖中只有16個,圖少了ADDOVERLAY


2.PorterDuff.Mode

PorterDuff.Mode錯誤圖

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

修正后的PorterDuff.Mode

最常用的就是DST_INSRC_IN,是可以用來實現自定義CircleImageView的一種方式

看圖幫助:后來者居上

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

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

這些模式最好都自己測試一下

具體的過程可以學習愛哥的自定義控件其實很簡單1/6

有的模式,不支持硬件加速,最好關閉硬件加速

繪制過程后來者居上,dst為下層,先進行繪制;src為上層,后進行繪制。

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


簡單測試:

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);//關閉硬件加速
        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);
        //創建一個新的畫布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);
        //設置混合模式
         paint.setColor(Color.RED);
        paint.setXfermode(porterDuffXfermode);
        //繪制src層
        canvas.drawRect(rectF,paint);
        paint.setXfermode(null);
        canvas.restoreToCount(layerId);//將自己創建的畫布Layer繪制到畫布默認的Layer
    }


    /**
     * 測量
     */
    @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);
        }
    }
}

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


3.CircleImageView

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

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

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

本篇為了學習PorterDuffXfermode就使用前面提到的PorterDuff.Mode.SRC_IN


簡單實踐:


圓形圖片
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);
        //設置混合模式
        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);
        // 還原畫布
        canvas.restoreToCount(sc);
    }
}

一個超級簡單的自定義CircleImageView


4.最后

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

進一步學習,可以看鴻洋大神的Android 自定義控件實現刮刮卡效果 真的就只是刮刮卡么


5. 補充

9月9號 晚上

關于PorterDuffXfermode的坑總結的很好,PorterDuffXfermode不正確的真正原因


本人還很菜,有錯誤,請指出

共勉 : )

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,619評論 6 539
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,155評論 3 425
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,635評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,539評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,255評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,646評論 1 326
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,655評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,838評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,399評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,146評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,338評論 1 372
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,893評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,565評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,983評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,257評論 1 292
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,059評論 3 397
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,296評論 2 376

推薦閱讀更多精彩內容