編輯:Android技術基礎
上一節,我們學習了Xfermode兩個已經過世(過時)的兒子:AvoidXfermode, PixelXorXfermode, 雖然說有點用,但是終歸是被淘汰的了,本節我們來學習Xfermode還健在的三兒子:PorterDuffXfermode;
先祭上官方API文檔:PorterDuffXfermode!文檔內容很少,我們可以看到他的構造方法:
參數只有一個:PorterDuff.Mode mode,而Android給我們提供了16種圖片混排模式,簡單點可以 理解為兩個圖層按照不同模式,可以組合成不同的結果顯示出來!16種混排模式的結果圖如下:
這裡兩個圖層:先繪制的圖是目標圖(DST),後繪制的圖是源圖(SRC)!
當然,在文檔中我們發現可供使用的模式並不是16種,而是18種,新增了ADD和OVERLAY兩種模式!
嗯,說多也白說,代碼最實際,本節我們寫下代碼來驗證下這18種模式吧!
PS:這個PorterDuff的命名其實是兩個人名的組合:Tomas Proter和 Tom Duff組成的,他們是最早在 最早在SIGGRAPH上提出圖形混合概念的大神級人物,有興趣的自行百度~
好的,我們來寫個例子驗證下上面這個圖,通過修改不同的模式,來對結果進行對比分析!
代碼實現:
Step 1:我們先寫個獲取屏幕寬高的工具類吧!ScreenUtil.java:
/**
* Created by Jay on 2015/10/23 0023.
*/
public class ScreenUtil {
/**
* 獲取屏幕寬高,sdk17後不建議采用
*
* @param context
*/
public static int[] getScreenHW(Context context) {
WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display display = manager.getDefaultDisplay();
int width = display.getWidth();
int height = display.getHeight();
int[] HW = new int[] { width, height };
return HW;
}
/**
* 獲取屏幕寬高,建議采用
*
* @param context
*/
public static int[] getScreenHW2(Context context) {
WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics dm = new DisplayMetrics();
manager.getDefaultDisplay().getMetrics(dm);
int width = dm.widthPixels;
int height = dm.heightPixels;
int[] HW = new int[] { width, height };
return HW;
}
/**
* 獲取屏幕的寬度
*
* @param context
* @return
*/
public static int getScreenW(Context context) {
return getScreenHW2(context)[0];
}
/**
* 獲取屏幕的高度
*
* @param context
* @return
*/
public static int getScreenH(Context context) {
return getScreenHW2(context)[1];
}
}
Step 2:編寫我們的自定義View類,在這裡做試驗!XfermodeView.java:
/**
* Created by Jay on 2015/10/23 0023.
*/
public class XfermodeView extends View {
private PorterDuffXfermode pdXfermode; //定義PorterDuffXfermode變量
//定義MODE常量,等下直接改這裡即可進行測試
private static PorterDuff.Mode PD_MODE = PorterDuff.Mode.ADD;
private int screenW, screenH; //屏幕寬高
private int width = 200; //繪制的圖片寬高
private int height = 200;
private Bitmap srcBitmap, dstBitmap; //上層SRC的Bitmap和下層Dst的Bitmap
public XfermodeView(Context context) {
this(context, null);
}
public XfermodeView(Context context, AttributeSet attrs) {
super(context, attrs);
screenW = ScreenUtil.getScreenW(context);
screenH = ScreenUtil.getScreenH(context);
//創建一個PorterDuffXfermode對象
pdXfermode = new PorterDuffXfermode(PD_MODE);
//實例化兩個Bitmap
srcBitmap = makeSrc(width, height);
dstBitmap = makeDst(width, height);
}
public XfermodeView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
//定義一個繪制圓形Bitmap的方法
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(0xFF26AAD1);
c.drawOval(new RectF(0, 0, w * 3 / 4, h * 3 / 4), p);
return bm;
}
//定義一個繪制矩形的Bitmap的方法
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(0xFFFFCE43);
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);
canvas.drawBitmap(srcBitmap, (screenW / 3 - width) / 2, (screenH / 2 - height) / 2, paint);
canvas.drawBitmap(dstBitmap, (screenW / 3 - width) / 2 + screenW / 3, (screenH / 2 - height) / 2, 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);
canvas.drawBitmap(dstBitmap, (screenW / 3 - width) / 2 + screenW / 3 * 2,
(screenH / 2 - height) / 2, paint); //繪制i
//設置Paint的Xfermode
paint.setXfermode(pdXfermode);
canvas.drawBitmap(srcBitmap, (screenW / 3 - width) / 2 + screenW / 3 * 2,
(screenH / 2 - height) / 2, paint);
paint.setXfermode(null);
// 還原畫布
canvas.restoreToCount(sc);
}
}
代碼看起來好復雜是吧,其實不然,無非就是獲取了屏幕寬高,然後畫了一個矩形一個圓形, 計算了一下他們的位置,然後設置下圖層(固定寫法),接著設下下畫筆setXfermode,接著 繪制到canvas上而已,你看不懂的可能是繪制位置的計算吧,其實不然,位置你喜歡怎麼定 都可以!那麼接下來我們來一個個看下解果咯,你只需修改PD_MODE的值設置為不同模式即可!
運行效果圖:
飽和度疊加
所繪制不會提交到畫布上,嗯結果...不知道是為什麼了,正常是沒東西的..
取兩圖層全部區域,交集部分顏色加深
只保留目標圖的alpha和color,所以繪制出來只有目標圖
源圖和目標圖相交處繪制目標圖,不相交的地方繪制源圖
兩者相交的地方繪制目標圖,繪制的效果會受到原圖處的透明度影響
在不相交的地方繪制目標圖
目標圖繪制在上方
取兩圖層全部區域,點亮交集部分顏色
取兩圖層交集部分疊加後顏色
疊加
取兩圖層全部區域,交集部分變為透明色
只保留源圖像的alpha和color,所以繪制出來只有源圖
源圖和目標圖相交處繪制源圖,不相交的地方繪制目標圖
兩者相交的地方繪制源圖
不相交的地方繪制源圖
把源圖繪制在上方
不相交的地方按原樣繪制源圖和目標圖
PorterDuffXfermodeDemo.zip
嗯,本節就寫了一個簡單的View來驗證這18種不同PorterDuff.Mode下的不同效果, 嘿嘿,蠻耗時間的,不過,讀者看起來肯定清晰多了是吧~當然,這只是一些初步的見解!
PorterDuffXfermode的PorterDuff.Mode對於我們自定義控件是非常重要的! 本節我們初步了解,下節我們挑幾個例子來練練手!
如果你想看關於PorterDuff.Mode更加詳細的介紹可見: Android Paint之 setXfermode PorterDuffXfermode 講解,別人寫的不錯的一篇文章! 嗯,就到這裡,明早體檢,今天就寫這麼多~
2.5.9 AlertDialog(對話框)詳解
本節引言:本節繼續給大家帶來是顯示提示信息的第三個控件AlertDialog(對話框),同時它也是其他Dialog的的父類!比如ProgressDi
4.2.2 Service進階
本節引言上節我們學習了Service的生命周期,以及兩種啟動Service的兩種方法,本節繼續來深入了解Service中的IntentService
7.5.3 Android 4.4後WebView的一些注意事項
本節引言:本節參考原文:Android 4.4 中 WebView 使用注意事項.md從Android 4.4開始,Android中的WebView
8.3.16 Canvas API詳解(Part 1)
本節引言: 前面我們花了13小節詳細地講解了Android中Paint類大部分常用的API,本節開始我們來講解 Canvas(畫板)的一些常用API,我們在8.3.1