轉載請注明出處:http://blog.csdn.net/allen315410/article/details/45077165
上篇部落格中,我将我對Paint的ColorFilter相關的幾個子類以及用法做了總結,其中最常用的ColorMatrixColorFilter值得我們多學習學習,通過定義一個color值的4*5的矩陣,來設定Paint的各種各樣的變色效果。此外,還有PorterDuffColorFilter,其實用的并不是很多,但是PorterDuffColorFilter中使用的幾個概念尤其重要,我們要好好了解一下,PorterDuff是老外發明的一直圖形混合模式,并且Android 的API中為我們提供了18種不同的混合模式算法,我們平時在開發的時候需要了解這些模式分别代表的含義和用法,就是靈活的運用這些模式随意混合出我們需要的圖像效果。
Xfermode
Xfermode具體怎麼翻譯,說實話,我也不知道,我習慣叫它圖檔混合模式,随便了,管它叫什麼,不妨礙我們使用它。關于Xfermode的說明,可以在Google文檔中找到這樣的描述:Xfermode是在繪圖通道中自定義“傳輸模式”的基類。靜态函數建立可以調用或者傳回任意作為模式枚舉指定的預定義子類執行個體。當Xfermode配置設定給Paint,然後繪制對象與Paint就具備了所添加的xfermode。讀起來比較拗口,下面直接看Xfermode的源碼:
public class Xfermode {
protected void finalize() throws Throwable {
try {
finalizer(native_instance);
} finally {
super.finalize();
}
}
private static native void finalizer(long native_instance);
long native_instance;
}
看,Xfermode就這麼點代碼,經驗告訴我們,其下必有子類,擦,變元芳了~~~

檢視一下文檔發現Xfermode确實有AvoidXfermode、PixelXorXfermode、PorterDuffXfermode,下面來繼續學習一下3個子類的用法。
AvoidXfermode
看這個子類之前告訴大家一個不幸的消息,AvoidXfermode不支援硬體加速,在高于API16的機器上不會适用,如果想測試這個子類,1,可以關閉手機的硬體加速子產品;2,在AndroidManifest.xml中Application節點上設定硬體加速為false。
android:hardwareAccelerated="false"
在Android Studio下點選檢視一下AvoidXfermode的構造方法:
public AvoidXfermode(int opColor, int tolerance, Mode mode)
AvoidXfermode的構造方法也特别簡單,一共接收3個參數:第一個參數opColor是一個16進制的帶透明度通道的顔色值,如0X12345678。第二個參數tolerance表示容內插補點,什麼是容內插補點呢?可以了解成一個表示“精确”和“模糊”的概念,下面會解釋一下。第三個參數是AvoidXfermode的模式,AvoidXfermode的模式一共有兩種:AvoidXfermode.Mode.TARGET和AvoidXfermode.Mode.AVOID。
AvoidXfermode.Mode.TARGET
在該模式下Android會判斷畫布上的顔色是否會有跟opColor不一樣的顔色,比如我opColor是紅色,那麼在TARGET模式下就會去判斷我們的畫布上是否有存在紅色的地方,如果有,則把該區域“染”上一層我們畫筆定義的顔色,否則不“染”色,而tolerance容內插補點則表示畫布上的像素和我們定義的紅色之間的差别該是多少的時候才去“染”的,比如目前畫布有一個像素的色值是(200, 20, 13),而我們的紅色值為(255, 0, 0),當tolerance容內插補點為255時,即便(200, 20, 13)并不等于紅色值也會被“染”色,容內插補點越大“染”色範圍越廣反之則反,空說無憑我們來看看具體的實作和效果:
public class CustomView3 extends View {
private Paint mPaint;
private Bitmap mBitmap;
private Context mContext;
private int x, y, w, h;
private AvoidXfermode avoidXfermode;
public CustomView3(Context context) {
this(context, null);
}
public CustomView3(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
initRes();
initPaint();
}
private void initRes() {
//加載bitmap
mBitmap = BitmapFactory.decodeResource(mContext.getResources(), R.mipmap.image);
//擷取bitmap的展示起始布局
x = ScreenUtil.getScreenW(mContext) / 2 - mBitmap.getWidth() / 2;
y = ScreenUtil.getScreenH(mContext) / 2 - mBitmap.getHeight() / 2;
w = ScreenUtil.getScreenW(mContext) / 2 + mBitmap.getWidth() / 2;
h = ScreenUtil.getScreenH(mContext) / 2 + mBitmap.getHeight() / 2;
}
private void initPaint() {
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
avoidXfermode = new AvoidXfermode(0XFFFFFFFF, 0, AvoidXfermode.Mode.TARGET);
}
@Override
protected void onDraw(Canvas canvas) {
canvas.drawBitmap(mBitmap, x, y, mPaint);
mPaint.setARGB(255, 211, 53, 243);
mPaint.setXfermode(avoidXfermode);
canvas.drawRect(x, y, w, h, mPaint);
}
}
下面來運作看效果,首先确定一下開啟的模拟器是API16以下的,或者Application節點下設定了關閉“硬體加速”:
AvoidXfermode(0XFFFFFFFF, 0, AvoidXfermode.Mode.TARGET):
大家可以看到,在我們的模式為TARGET容內插補點為0的時候此時隻有當圖檔中像色顔色值為0XFFFFFFFF的地方才會被染色,而其他地方不會有改變
下面我們來修改一下容內插補點,将容內插補點改成255:
AvoidXfermode(0XFFFFFFFF, 255, AvoidXfermode.Mode.TARGET)
而當容內插補點為255的時候隻要是跟0XFFFFFFFF有點接近的地方都會被染色
AvoidXfermode.Mode.AVOID
則與TARGET恰恰相反,TARGET是我們指定的顔色是否與畫布的顔色一樣,而AVOID是我們指定的顔色是否與畫布不一樣,其他的都與TARGET類似
AvoidXfermode(0XFFFFFFFF, 0, AvoidXfermode.Mode.AVOID):
當模式為AVOID容內插補點為0時,隻有當圖檔中像素顔色值與0XFFFFFFFF完全不一樣的地方才會被染色
AvoidXfermode(0XFFFFFFFF, 255, AvoidXfermode.Mode.AVOID):
當容內插補點為255時,隻要與0XFFFFFFFF稍微有點不一樣的地方就會被染色
那麼這玩意究竟有什麼用呢?比如說當我們隻想在白色的區域畫點東西或者想把白色區域的地方替換為另一張圖檔的時候就可以采取這種方式!
PixelXorXfermode
PixelXorXfermode是Xfermode下的另外一種圖像混排模式,該類特别簡單,不過呢,也很不幸的,在API16中已經過時了。我們來做一個簡單的了解,先看PixelXorXfermode的構造方法:
public PixelXorXfermode(int opColor)
構造方法很簡單,隻要傳遞一個16進制帶透明通道的顔色值即可,那麼這個參數有什麼用呢?我在Google文檔中,找到了這樣的一個算法:實際上PixelXorXfermode内部是按照“opColor ^ src ^ dst”這個異或算法運算的,得到一個不透明的(alpha = 255)的色彩值,設定到圖像中,下面我們接着上面用到的圖檔Demo寫個PixelXorXfermode的Demo:
public class CustomView3 extends View {
private Paint mPaint;
private Bitmap mBitmap;
private Context mContext;
private int x, y, w, h;
private PixelXorXfermode pixelXorXfermode;
public CustomView3(Context context) {
this(context, null);
}
public CustomView3(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
initRes();
initPaint();
}
private void initRes() {
//加載bitmap
mBitmap = BitmapFactory.decodeResource(mContext.getResources(), R.mipmap.image);
//擷取bitmap的展示起始布局
x = ScreenUtil.getScreenW(mContext) / 2 - mBitmap.getWidth() / 2;
y = ScreenUtil.getScreenH(mContext) / 2 - mBitmap.getHeight() / 2;
w = ScreenUtil.getScreenW(mContext) / 2 + mBitmap.getWidth() / 2;
h = ScreenUtil.getScreenH(mContext) / 2 + mBitmap.getHeight() / 2;
}
private void initPaint() {
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
pixelXorXfermode = new PixelXorXfermode(0XFFFF0000);
}
@Override
protected void onDraw(Canvas canvas) {
//先繪制Bitmap,src
canvas.drawBitmap(mBitmap, x, y, mPaint);
//随便設定一個純色測試
mPaint.setARGB(255, 211, 53, 243);
//設定Xfermode
mPaint.setXfermode(pixelXorXfermode);
//在bitmap上混排一個純色的矩形(dst)
canvas.drawRect(x, y, w, h, mPaint);
}
}
混排後的圖像是:
PixelXorXfermode在底層已經取出src,dst每個像素點與opColor進行了opColor ^ src ^ dst運算了,結果輸出就是上圖所示的那樣!好了,我隻學這麼多了,因為它已經過時了,同樣上面的AvoidXfermode也是,過時了,了解即可。下面是對Xfermode的第三個子類,也是唯一一個還沒有過時的,非常重要的子類PorterDuffXfermode的學習。
PorterDuffXfermode
同樣PorterDuffXfermode也是Xfermode的子類,我們先看看它的構造方法:
public PorterDuffXfermode(PorterDuff.Mode mode)
PorterDuffXfermode的構造方法很簡單,構造方法中需要傳遞一個PorterDuff.Mode參數,關于PorterDuff.Mode,我們在上篇部落格中已經學習完了,其實跟ColorFilter的子類PorterDuffColorFilter的混排模式是一樣的。Android系統一共提供了18種混排模式,在模拟器的ApiDemos/Graphics/XferModes,有張效果圖:
這張圖可以很形象的說明圖檔各種混排模式下的效果。其中Src代表原圖,Dst代表目标圖,兩張圖檔使用不同的混排方式後,得到的圖像是如上圖所示的。PorterDuff.Mode也提供了18種混排模式已經算法,其中比上圖多了ADD和OVERLAY兩種模式:
其中Sa全稱為Source alpha表示源圖的Alpha通道;Sc全稱為Source color表示源圖的顔色;Da全稱為Destination alpha表示目标圖的Alpha通道;Dc全稱為Destination color表示目标圖的顔色,[...,..]前半部分計算的是結果圖像的Alpha通道值,“,”後半部分計算的是結果圖像的顔色值。圖像混排後是依靠這兩個值來重新計算ARGB值的,具體計算算法,抱歉,我也不知道,不過不要緊,不了解計算算法也不影響我們程式員寫程式的。我們隻要對照上面的apiDemo中提供的圖檔就能推測出混排後的結果的,下面将會對照ApiDemos/Graphics/XferModes的程式進行修改,來測試各個子產品的效果,測試程式如下:
public class XfermodeView extends View {
//PorterDuff模式常量 可以在此更改不同的模式測試
private static final PorterDuff.Mode MODE = PorterDuff.Mode.CLEAR;
private PorterDuffXfermode porterDuffXfermode;
private int screenW, screenH; //螢幕寬高
private Bitmap srcBitmap, dstBitmap;
//源圖和目标圖寬高
private int width = 120;
private int height = 120;
public XfermodeView(Context context) {
this(context, null);
}
public XfermodeView(Context context, AttributeSet attrs) {
super(context, attrs);
screenW = ScreenUtil.getScreenW((Activity) context);
screenH = ScreenUtil.getScreenH((Activity) context);
//建立一個PorterDuffXfermode對象
porterDuffXfermode = new PorterDuffXfermode(MODE);
//建立原圖和目标圖
srcBitmap = makeSrc(width, height);
dstBitmap = makeDst(width, height);
}
//建立一個圓形bitmap,作為dst圖
private 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;
}
// 建立一個矩形bitmap,作為src圖
private 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 onDraw(Canvas canvas) {
Paint paint = new Paint();
paint.setFilterBitmap(false);
paint.setStyle(Paint.Style.FILL);
//繪制“src”藍色矩形原圖
canvas.drawBitmap(srcBitmap, screenW / 8 - width / 4, screenH / 12 - height / 4, paint);
//繪制“dst”黃色圓形原圖
canvas.drawBitmap(dstBitmap, screenW / 2, screenH / 12, paint);
//建立一個圖層,在圖層上示範圖形混合後的效果
int sc = canvas.saveLayer(0, 0, screenW, screenH, 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);
//先繪制“dst”黃色圓形
canvas.drawBitmap(dstBitmap, screenW / 4, screenH / 3, paint);
//設定Paint的Xfermode
paint.setXfermode(porterDuffXfermode);
canvas.drawBitmap(srcBitmap, screenW / 4, screenH / 3, paint);
paint.setXfermode(null);
// 還原畫布
canvas.restoreToCount(sc);
}
}
為了友善觀察,需要将Activity_main.xml的背景色設定為黑色。
1.PorterDuff.Mode.CLEAR。中文描述:所繪制源圖像不會送出到畫布上。
private static final PorterDuff.Mode MODE = PorterDuff.Mode.CLEAR;
2.PorterDuff.Mode.SRC。中文描述:隻顯示源圖像。
private static final PorterDuff.Mode MODE = PorterDuff.Mode.SRC;
3.PorterDuff.Mode.DST。中文描述:隻顯示目标圖像。
private static final PorterDuff.Mode MODE = PorterDuff.Mode.DST;
4.PorterDuff.Mode.SRC_OVER。中文描述:正常繪制顯示,源圖像居上顯示。
private static final PorterDuff.Mode MODE = PorterDuff.Mode.SRC_OVER;
5.PorterDuff.Mode.DST_OVER。中文描述: 上下層都顯示。目标圖像居上顯示。
private static final PorterDuff.Mode MODE = PorterDuff.Mode.DST_OVER;
6.PorterDuff.Mode.SRC_IN。中文描述: 取兩層繪制交集中的源圖像。
private static final PorterDuff.Mode MODE = PorterDuff.Mode.SRC_IN;
7.PorterDuff.Mode.DST_IN。中文描述:取兩層繪制交集中的目标圖像。
private static final PorterDuff.Mode MODE = PorterDuff.Mode.DST_IN;
8.PorterDuff.Mode.SRC_OUT。中文描述:隻在源圖像和目标圖像不相交的地方繪制源圖像。
private static final PorterDuff.Mode MODE = PorterDuff.Mode.SRC_OUT;
9.PorterDuff.Mode.DST_OUT。中文描述:隻在源圖像和目标圖像不相交的地方繪制目标圖像。
private static final PorterDuff.Mode MODE = PorterDuff.Mode.DST_OUT;
10.PorterDuff.Mode.SRC_ATOP。中文描述:在源圖像和目标圖像相交的地方繪制源圖像,在不相交的地方繪制目标圖像。
private static final PorterDuff.Mode MODE = PorterDuff.Mode.SRC_ATOP;
11.PorterDuff.Mode.DST_ATOP。中文描述:在源圖像和目标圖像相交的地方繪制目标圖像而在不相交的地方繪制源圖像。
private static final PorterDuff.Mode MODE = PorterDuff.Mode.DST_ATOP;
12.PorterDuff.Mode.XOR。中文描述:異或:去除兩圖層交集部分
private static final PorterDuff.Mode MODE = PorterDuff.Mode.XOR;
13.PorterDuff.Mode.DARKEN。中文描述:取兩圖層全部區域,交集部分顔色加深
private static final PorterDuff.Mode MODE = PorterDuff.Mode.DARKEN;
14.PorterDuff.Mode.LIGHTEN。中文描述:取兩圖層全部,點亮交集部分顔色
private static final PorterDuff.Mode MODE = PorterDuff.Mode.LIGHTEN;
15.PorterDuff.Mode.MULTIPLY。中文描述:取兩圖層交集部分疊加後顔色
private static final PorterDuff.Mode MODE = PorterDuff.Mode.MULTIPLY;
16.PorterDuff.Mode.SCREEN。中文描述:濾色。
private static final PorterDuff.Mode MODE = PorterDuff.Mode.SCREEN;
以下是android中新加的兩種模式:
17.ADD。中文描述:飽和度相加。
private static final PorterDuff.Mode MODE = PorterDuff.Mode.ADD;
18.OVERLAY。中文描述:疊加
private static final PorterDuff.Mode MODE = PorterDuff.Mode.OVERLAY;
源碼請在這裡下載下傳,ps:源碼在Android Studio中建構。