菜雞wing遇敵PorterDuffXferMode,不料過于輕敵,應戰吃力。隨后與其大戰三天三夜,三百余回合不分勝負。幸得 @咪咪控 相助,僥幸獲勝。
關鍵字:PorterDuffXferMode ?錯誤 不正確 ?不達到預期 ?bug
上一篇帶來一個使用PorterDuffXferMode ?做的?[水波紋loadingview](http://blog.csdn.net/wingichoy/article/details/50523713),中間遇到了點小困難。
(說人話) ?PorterDuffXferMode總是不能按照效果圖預期的效果執行。關于PorterDuffXferMode的錯誤顯示是一個對初學者十分深的坑,到底是bug呢,還是有需要注意的地方呢。這里就跟隨我 帶上手電筒,去一探究竟。
轉載請注明出處:http://blog.csdn.net/wingichoy/article/details/50534175
首先,大家都知道有一個圖片:

然后,大部分時候 是看到了覺得很神奇,就躍躍欲試,尤其是src_in ?和dstIn可以實現遮罩效果,例如圓角圖片 圓形圖片都用了這種模式。
于是就挨個測試各種模式是否生效,結果往往不能達到預期效果。我們來做個測試。
從最簡單的開始:
**1.直接在canvas上面繪制圖形**:
~~~
@Override
protected void onDraw(Canvas canvas) {
//dst
canvas.drawRect(20,20,80,80,mDstPaint);
//src
canvas.drawCircle(30,30,30,mSrcPaint);
}
~~~
原圖效果是這樣的:

現在加一個mode上來,XOR
~~~
@Override
protected void onDraw(Canvas canvas) {
//dst
canvas.drawRect(20,20,80,80,mDstPaint);
mSrcPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.XOR));
//src
canvas.drawCircle(30,30,30,mSrcPaint);
}
~~~
跑起來的結果是這樣的:

WTF!!?? 這是什么鬼。不應該是相交部分消失嗎。 網上說“硬件加速”對這個有影響,那么在構造器里關閉硬件加速試一下:
~~~
public TestView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mDstPaint = new Paint();
mSrcPaint = new Paint();
mDstPaint.setColor(Color.YELLOW);
mSrcPaint.setColor(Color.BLUE);
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}
~~~
運行的結果:

這下正常了。相交的部分消失了。
**結論1:硬件加速對PorterDuffXferMode有影響,使用前請關閉硬件加速。**
那么這下真的天下太平了嗎?nonono~不要太天真,不然怎么能叫萬丈深淵呢。
繼續試驗其他模式: ?將模式改為SRC_IN

WTF?????跟效果圖根本不一致好嗎!!!! 在試試DST_IN

你確定你沒有在逗我???? ?怎么是這個鬼東西。 ?(當時鼓搗了我三天四夜,一直在日狗,不過先別急,慢慢來。)
為什么一定要按照那個效果圖來呢。。。 因為特么的那個圖是官方的一個demo。。 那么我們就來看看這個demo長什么樣!
~~~
package io.appium.android.apis.graphics;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.Xfermode;
import android.os.Bundle;
import android.view.View;
public class Xfermodes extends GraphicsActivity {
// create a bitmap with a circle, used for the "dst" image
static Bitmap makeDst(int w, int h) {
Bitmap bm = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(bm);
Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
p.setColor(0xFFFFCC44);
c.drawOval(new RectF(0, 0, w*3/4, h*3/4), p);
return bm;
}
// create a bitmap with a rect, used for the "src" image
static Bitmap makeSrc(int w, int h) {
Bitmap bm = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(bm);
Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
p.setColor(0xFF66AAFF);
c.drawRect(w/3, h/3, w*19/20, h*19/20, p);
return bm;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new SampleView(this));
}
private static class SampleView extends View {
private static final int W = 64;
private static final int H = 64;
private static final int ROW_MAX = 4; // number of samples per row
private Bitmap mSrcB;
private Bitmap mDstB;
private Shader mBG; // background checker-board pattern
private static final Xfermode[] sModes = {
new PorterDuffXfermode(PorterDuff.Mode.CLEAR),
new PorterDuffXfermode(PorterDuff.Mode.SRC),
new PorterDuffXfermode(PorterDuff.Mode.DST),
new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER),
new PorterDuffXfermode(PorterDuff.Mode.DST_OVER),
new PorterDuffXfermode(PorterDuff.Mode.SRC_IN),
new PorterDuffXfermode(PorterDuff.Mode.DST_IN),
new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT),
new PorterDuffXfermode(PorterDuff.Mode.DST_OUT),
new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP),
new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP),
new PorterDuffXfermode(PorterDuff.Mode.XOR),
new PorterDuffXfermode(PorterDuff.Mode.DARKEN),
new PorterDuffXfermode(PorterDuff.Mode.LIGHTEN),
new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY),
new PorterDuffXfermode(PorterDuff.Mode.SCREEN)
};
private static final String[] sLabels = {
"Clear", "Src", "Dst", "SrcOver",
"DstOver", "SrcIn", "DstIn", "SrcOut",
"DstOut", "SrcATop", "DstATop", "Xor",
"Darken", "Lighten", "Multiply", "Screen"
};
public SampleView(Context context) {
super(context);
mSrcB = makeSrc(W, H);
mDstB = makeDst(W, H);
// make a ckeckerboard pattern
Bitmap bm = Bitmap.createBitmap(new int[] { 0xFFFFFFFF, 0xFFCCCCCC,
0xFFCCCCCC, 0xFFFFFFFF }, 2, 2,
Bitmap.Config.RGB_565);
mBG = new BitmapShader(bm,
Shader.TileMode.REPEAT,
Shader.TileMode.REPEAT);
Matrix m = new Matrix();
m.setScale(6, 6);
mBG.setLocalMatrix(m);
}
@Override protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.WHITE);
Paint labelP = new Paint(Paint.ANTI_ALIAS_FLAG);
labelP.setTextAlign(Paint.Align.CENTER);
Paint paint = new Paint();
paint.setFilterBitmap(false);
canvas.translate(15, 35);
int x = 0;
int y = 0;
for (int i = 0; i < sModes.length; i++) {
// draw the border
paint.setStyle(Paint.Style.STROKE);
paint.setShader(null);
canvas.drawRect(x - 0.5f, y - 0.5f,
x + W + 0.5f, y + H + 0.5f, paint);
// draw the checker-board pattern
paint.setStyle(Paint.Style.FILL);
paint.setShader(mBG);
canvas.drawRect(x, y, x + W, y + H, paint);
// draw the src/dst example into our offscreen bitmap
int sc = canvas.saveLayer(x, y, x + W, y + H, null,
Canvas.MATRIX_SAVE_FLAG |
Canvas.CLIP_SAVE_FLAG |
Canvas.HAS_ALPHA_LAYER_SAVE_FLAG |
Canvas.FULL_COLOR_LAYER_SAVE_FLAG |
Canvas.CLIP_TO_LAYER_SAVE_FLAG);
canvas.translate(x, y);
canvas.drawBitmap(mDstB, 0, 0, paint);
paint.setXfermode(sModes[i]);
canvas.drawBitmap(mSrcB, 0, 0, paint);
paint.setXfermode(null);
canvas.restoreToCount(sc);
// draw the label
canvas.drawText(sLabels[i],
x + W/2, y - labelP.getTextSize()/2, labelP);
x += W + 10;
// wrap around when we've drawn enough for one row
if ((i % ROW_MAX) == ROW_MAX - 1) {
x = 0;
y += H + 30;
}
}
}
}
}
~~~
一點一點看,截取onDraw里面的片段,這里
~~~
canvas.drawBitmap(mDstB, 0, 0, paint);
paint.setXfermode(sModes[i]);
canvas.drawBitmap(mSrcB, 0, 0, paint);
paint.setXfermode(null);
~~~
他是畫了兩個bitmap。網上有人說好像只對bitmap生效,那到底是不是這樣呢。我們來試驗一下。
我們新定義一個canvas ?再定義一個bitmap ? 現在bitmap上畫出來src ?然后將bitmap畫到canvas上:
~~~
public TestView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mDstPaint = new Paint();
mSrcPaint = new Paint();
mDstPaint.setColor(Color.YELLOW);
mSrcPaint.setColor(Color.BLUE);
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
mSrcBitmap = Bitmap.createBitmap(50,50, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mSrcBitmap);
}
~~~
~~~
@Override
protected void onDraw(Canvas canvas) {
//dst
canvas.drawRect(20,20,80,80,mDstPaint);
//src
// mSrcPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
mCanvas.drawCircle(25,25,25,mSrcPaint);
canvas.drawBitmap(mSrcBitmap,0,0,null);
}
~~~
現在的效果是這樣的:

加一個XOR 試試。。
日了狗了!!!!!沒反應啊,到底是什么鬼。

是不是兩個都需要bitmap才可以呢,再創建一個dstBitmap和dstCanvas?
~~~
mDstBitmap = Bitmap.createBitmap(50,50, Bitmap.Config.ARGB_8888);
mDstCanvas = new Canvas(mDstBitmap);
~~~
加一個XOR 試試
~~~
@Override
protected void onDraw(Canvas canvas) {
//dst
mDstCanvas.drawRect(20,20,80,80,mDstPaint);
canvas.drawBitmap(mDstBitmap,0,0,mDstPaint);
//src
mSrcPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.XOR));
mSrcCanvas.drawCircle(25,25,25,mSrcPaint);
canvas.drawBitmap(mSrcBitmap,0,0,mSrcPaint);
}
~~~
效果如下:

終于他媽的出來了!!!!那其他效果呢?
clear

一致的!!!!好激動有沒有!!!!搞了4天 越來越接近結論了!!!
**結論2:只有兩個bitmap的時候,才可以生效。**
不要高興太早。。如果坑到這里就完了,那還叫坑么。
繼續試。。嗯 好多模式都是一致的。
直到!!!SRC_IN和DST_IN ,會發現。。。都消失了。 為毛呢??
檢查代碼 ?發現 在往bitmap畫圓之前就set了mode ?這樣會有影響
糾正
~~~
@Override
protected void onDraw(Canvas canvas) {
//dst
mDstCanvas.drawRect(20,20,80,80,mDstPaint);
canvas.drawBitmap(mDstBitmap,0,0,mDstPaint);
//src
mSrcCanvas.drawCircle(25,25,25,mSrcPaint);
//再畫圓之后 設置mode
mSrcPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.XOR));
canvas.drawBitmap(mSrcBitmap,0,0,mSrcPaint);
}
~~~
發現

艸!!!!!!終于好了!!!!!!!!經過不懈努力!!!撒花!*★,°*:.☆\( ̄▽ ̄)/$:*.°★* 。?
其實我們剛才bitmap的大小是一樣的。 然后都是從0,0開始 完全覆蓋了。
那么錯開一點點 是什么效果呢,調整代碼如下
~~~
protected void onDraw(Canvas canvas) {
//dst
mDstCanvas.drawRect(20,20,80,80,mDstPaint);
canvas.drawBitmap(mDstBitmap,20,20,mDstPaint);
//src
mSrcCanvas.drawCircle(25,25,25,mSrcPaint);
mSrcPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(mSrcBitmap,0,0,mSrcPaint);
}
~~~
效果如下:

可以看有效果了!!!! 但是是一個什么鬼!!! ?矩形缺角??加藍色一點??
這樣看是很難看出效果的。。來調整一下bitmap的底色 和矩形的大小:
把兩個bitmap的底色都畫成灰色, 矩形不要占滿畫布 留出空間
~~~
mDstCanvas.drawColor(Color.GRAY);
~~~
~~~
@Override
protected void onDraw(Canvas canvas) {
//dst 黃色
mDstCanvas.drawRect(0,0,40,40,mDstPaint);
canvas.drawBitmap(mDstBitmap,20,20,mDstPaint);
//src 藍色
mSrcCanvas.drawCircle(25,25,25,mSrcPaint);
canvas.drawBitmap(mSrcBitmap,0,0,mSrcPaint);
}
~~~
效果如下。。 ?嗯 這樣就是兩個bitmap了。。很明了 ?bitmap的內容 位置,

然后再搞SRC_IN模式

dst_in
?
那么把bitmap底色去掉。 改成透明的呢?
dst_in:

SRC_IN:

**總結3:兩個bitmap位置不完全重疊的效果如上,并不能總結出效果,要按實際效果來。**
---------------------------------------------------------------------------------------------------------華麗麗的分割線-----------------------------------------------------------------------------------------
**最終大總結,如果想讓PorterDuffXferMode按照預期Demo(或者效果圖)的效果圖像實現,必須滿足以下條件:**
1、關閉硬件加速。
2、兩個bitmap大小盡量一樣。
3、背景色為透明色。
4、如果兩個bitmap位置不完全一樣,可能也是預期效果,只不過你看到的效果和你自己腦補的預期效果不一致。
最后想再說幾句。鼓搗這個模式鼓搗了幾乎一周,每天晚上下班都在搞。查了無數資料。但是好多不完整,甚至有一些誤導性。所以為了避免后來者入坑。親自試驗,盡量總結。 如果有說的不正確的地方請及時向我提出。我會及時改正。
如果本文幫助到了你,請點一個頂,或者評論一下,蟹蟹!!!!
- 前言
- android自定義viewgroup初步之一----抽屜菜單
- Android 自定義view --圓形百分比(進度條)
- Android 自定義View -- 簡約的折線圖
- 新手自定義view練習實例之(一) 泡泡彈窗
- 新手自定義view練習實例之(二) 波浪view
- 手把手帶你畫一個 時尚儀表盤 Android 自定義View
- 手把手帶你畫一個動態錯誤提示 Android自定義view
- 手把手帶你做一個超炫酷loading成功動畫view Android自定義view
- 關于Android自定義view 你所需要知道的基本函數
- Android自定義view進階-- 神奇的貝塞爾曲線
- wing帶你玩轉自定義view系列(1) 仿360內存清理效果
- wing帶你玩轉自定義view系列(2) 簡單模仿qq未讀消息去除效果
- wing帶你玩轉自定義view系列(3)模仿微信下拉眼睛
- 手把手教你畫一個 逼格滿滿圓形水波紋loadingview Android
- 有坑?? 為何wing墜入PorterDuffXferMode的萬丈深淵(PorterDuffXferMode深入試驗)
- 手把手帶你畫一個漂亮蜂窩view Android自定義view
- 一個炫字都不夠??!!!手把手帶你打造3D自定義view
- 恭喜發財! -- 手把手教你仿造一個qq下拉搶紅包 Android自定義view