一、View轉換爲Bitmap
在Android中所有的控件都是View的直接子類或者間接子類,通過它們可以組成豐富的UI界面。在窗口顯示的時候Android會把這些控件都加載到內存中,形成一個以ViewRoot爲根節點的控件樹,然後由根節點開始逐級把控件繪製到屏幕上。
可以通過調用控件的setDrawingCacheEnabled(true)方法,開啓繪圖緩存功能,在繪製View的時候把圖像緩存起來,然後通過getDrawingCache()方法獲取這個緩存的Bitmap。需要注意的是,當不再使用這個Bitmap時,需要調用destroyDrawingCache()方法,釋放Bitmap資源。由於在繪製View到屏幕時緩存圖像會降低控件繪製的效率,因此只會在需要使用View的圖像緩存的時候才調用setDrawingCacheEnabled(true)方法開啓圖像緩存功能,當不再使用圖像緩存時需要調用setDrawingCacheEnabled(false)
關閉圖像緩存功能。
這種方法在支持拖拽類型的應用中經常見到,在Android系統的Launcher應用中也使用了這種方法,當用戶拖拽應用的快捷圖標時,獲取到控件對應的Bitmap,然後操作這個Bitmap隨着手指移動。
下面通過一段代碼來說明如何獲取View對應的Bitmap。在代碼中使用了兩個ImageView並給它們都設置了顯示的圖片資源,然後把第一個ImageView對應的bitmap顯示到第二個ImageView中。由於在Activity的onCreate方法中調用這個方法,當執行Activity的onCreate方法時,控件還沒有準備好,所以需要使用Handler進行延遲操作,Java代碼如下:
[代碼]java代碼:
02 |
public
void getDrawingCache( final
ImageView sourceImageView, final
ImageView destImageView) { |
04 |
new
Handler().postDelayed( new
Runnable() { |
10 |
sourceImageView.setDrawingCacheEnabled( true );
|
12 |
Bitmap mBitmap = sourceImageView.getDrawingCache();
|
14 |
destImageView.setImageBitmap(mBitmap);
|
20 |
new
Handler().postDelayed( new
Runnable() { |
27 |
destImageView.setImageResource(R.drawable.pet);
|
33 |
sourceImageView.setDrawingCacheEnabled( false );
|
35 |
sourceImageView.destroyDrawingCache();
|
42 |
mImageView1.setImageResource(R.drawable.android);
|
43 |
mImageView2.setImageResource(R.drawable.pet);
|
44 |
getDrawingCache(mImageView1, mImageView2); |
運行效果如下:
圖7-3 Demo運行效果圖1
圖7-4 Demo運行效果圖2
二、圖片圓角處理
在Android中可以很容通過圖像疊加的規則爲圖片添加圓角效果。正常情況下,在已有的圖像上繪圖時將會在其上面添加一層新的圖形。如果繪圖時使用的Paint是完全不透明的,那麼它將完全遮擋住下面的圖像,如果Paint是部分透明的,那麼它將會對重疊部分圖像的顏色疊加處理。通過PorterDuffXfermode規則可以設置繪製圖像時的疊加規則。PorterDuffXfermode是非常強大的轉換模式,使用它可以設置圖像疊加的Porter-Duff規則,來控制Paint如何與Canvas上已有的圖像進行疊加。下面列舉了常用的12條Porter-Duff規則及其表示的含義:
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.SRC_IN規則來給圖片添加圓角效果,主要的思路是先繪製一個圓角矩形,然後在上面繪製圖像,取圖像與圓角矩形的交集部分,只保留圖像。Java代碼如下:
[代碼]java代碼:
02 |
public
Bitmap getRoundedBitmap() { |
03 |
Bitmap mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.frame);
|
05 |
Bitmap bgBitmap = Bitmap.createBitmap(mBitmap.getWidth(), mBitmap.getHeight(), Config.ARGB_8888);
|
07 |
Canvas mCanvas =
new Canvas(bgBitmap);
|
09 |
Paint mPaint =
new Paint();
|
10 |
Rect mRect =
new Rect( 0 ,
0 , mBitmap.getWidth(), mBitmap.getHeight());
|
11 |
RectF mRectF =
new RectF(mRect);
|
14 |
mPaint.setAntiAlias( true );
|
16 |
mCanvas.drawRoundRect(mRectF, roundPx, roundPx, mPaint);
|
19 |
mPaint.setXfermode( new
PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); |
21 |
mCanvas.drawBitmap(mBitmap, mRect, mRect, mPaint);
|
效果如下圖所示:
圖7-5 圖片圓角處理
三、圖片灰化處理
在Android中可以通過ColorMatrix類實現圖像處理軟件中的濾鏡效果,通過ColorMatrix類可以對位圖中的每個像素進行變換處理,達到特殊的濾鏡效果,下面通過一個例子來介紹如何通過ColorMatrix對圖像進行灰化處理,Java代碼如下:
[代碼]java代碼:
02 |
public
Bitmap getGrayBitmap() { |
03 |
Bitmap mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.android);
|
04 |
Bitmap mGrayBitmap = Bitmap.createBitmap(mBitmap.getWidth(), mBitmap.getHeight(), Config.ARGB_8888);
|
05 |
Canvas mCanvas =
new Canvas(mGrayBitmap);
|
06 |
Paint mPaint =
new Paint();
|
09 |
ColorMatrix mColorMatrix =
new ColorMatrix();
|
11 |
mColorMatrix.setSaturation( 0 );
|
13 |
ColorMatrixColorFilter mColorFilter =
new ColorMatrixColorFilter(mColorMatrix);
|
15 |
mPaint.setColorFilter(mColorFilter);
|
17 |
mCanvas.drawBitmap(mBitmap,
0 , 0 , mPaint);
|
效果如下圖所示:
圖7-6 圖片灰化處理
四、提取圖像Alpha位圖
Android中的ARGB_8888類型的位圖由Alpha(透明度)、Red(紅)、Green(綠)、Blue(藍)四部分組成,其中Alpha部分也就是常說的Alpha通道,它控制圖像的透明度。在Android中Bitmap類提供了extractAlpha()方法,可以把位圖中的Alpha部分提取出來作爲一個新的位圖,然後與填充顏色後的Paint結合重新繪製一個新圖像。下面通過一個例子來說明Bitmap類的extractAlpha()方法的使用,Java代碼如下:
[代碼]java代碼:
02 |
public
Bitmap getAlphaBitmap() { |
03 |
BitmapDrawable mBitmapDrawable = (BitmapDrawable) getResources().getDrawable(R.drawable.enemy_infantry_ninja);
|
04 |
Bitmap mBitmap = mBitmapDrawable.getBitmap();
|
09 |
Bitmap mAlphaBitmap = Bitmap.createBitmap(mBitmap.getWidth(), mBitmap.getHeight(), Config.ARGB_8888);
|
11 |
Canvas mCanvas =
new Canvas(mAlphaBitmap);
|
12 |
Paint mPaint =
new Paint();
|
14 |
mPaint.setColor(Color.BLUE);
|
16 |
Bitmap alphaBitmap = mBitmap.extractAlpha();
|
18 |
mCanvas.drawBitmap(alphaBitmap,
0 , 0 , mPaint);
|
圖7-7 提取圖像Alpha位圖
其中最後一幅圖片是把原圖片四個邊距縮小兩個dp,然後與Alpha位圖一起繪製的結果,讀者可以參考本章Demo中的getStrokeBitmap()方法。
五、圖像變換
Android開發框架提供了一個座標變換矩陣Matrix類,它可以與Bitmap類的createBitmap方法結合使用,對圖像進行縮放、旋轉、扭曲等變換處理。圖像變換操作就是對座標變換矩陣進行矩陣乘法運算,Matrix類中提供了一些簡便的方法如preScale、postScale、preRotate、postRotate、preSkrew、postSkrew、preTranslate、postTranslate等封裝了矩陣的運算,它們與Bitmap類的createBitmap方法結合使用可以很容易地對圖像進行縮放、旋轉、扭曲、平移操作。
1)圖像縮放
使用Matrix類preScale或者postScale可以對圖像進行縮放操作,它的兩個參數分別爲x和y座標縮放比例,下面使用preScale對圖像進行放大0.75倍,Java代碼如下:
[代碼]java代碼:
02 |
public
Bitmap getScaleBitmap() { |
03 |
BitmapDrawable mBitmapDrawable = (BitmapDrawable) getResources().getDrawable(R.drawable.pet);
|
04 |
Bitmap mBitmap = mBitmapDrawable.getBitmap();
|
05 |
int
width = mBitmap.getWidth(); |
06 |
int
height = mBitmap.getHeight(); |
08 |
Matrix matrix =
new Matrix();
|
09 |
matrix.preScale( 0 .75f,
0 .75f); |
10 |
Bitmap mScaleBitmap = Bitmap.createBitmap(mBitmap,
0 , 0 , width, height, matrix,
true ); |
效果如下圖所示:
圖7-8 圖像縮放
2)圖片旋轉
使用Matrix類preRotate或者postRotate可以對圖像進行旋轉操作,它只有一個參數表示旋轉的角度,下面使用preRotate對圖像順時針旋轉30度,Java代碼如下:
[代碼]java代碼:
02 |
public
Bitmap getRotatedBitmap() { |
03 |
BitmapDrawable mBitmapDrawable = (BitmapDrawable) getResources().getDrawable(R.drawable.pet);
|
04 |
Bitmap mBitmap = mBitmapDrawable.getBitmap();
|
05 |
int
width = mBitmap.getWidth(); |
06 |
int
height = mBitmap.getHeight(); |
08 |
Matrix matrix =
new Matrix();
|
10 |
Bitmap mRotateBitmap = Bitmap.createBitmap(mBitmap,
0 , 0 , width, height, matrix,
true ); |
效果如下圖所示:
圖7-9 圖片旋轉
3)圖像傾斜
使用Matrix類preSkew或者postSkew可以對圖像進行傾斜操作,它的兩個參數分別爲x和y座標傾斜度,下面使用preSkew對圖像進行傾斜變換,Java代碼如下:
[代碼]java代碼:
02 |
public
Bitmap getScrewBitmap() { |
03 |
BitmapDrawable mBitmapDrawable = (BitmapDrawable) getResources().getDrawable(R.drawable.pet);
|
04 |
Bitmap mBitmap = mBitmapDrawable.getBitmap();
|
05 |
int
width = mBitmap.getWidth(); |
06 |
int
height = mBitmap.getHeight(); |
08 |
Matrix matrix =
new Matrix();
|
09 |
matrix.preSkew( 1 .0f,
0 .15f); |
10 |
Bitmap mScrewBitmap = Bitmap.createBitmap(mBitmap,
0 , 0 , width, height, matrix,
true ); |
效果如下圖所示:
圖7-10 圖像傾斜
4)圖像倒影
爲圖像添加倒影效果之後,圖像看起來會有立體感,更有真實感,在Android中使用Matrix類可以很容易實現圖像的倒影效果。主要是Matrix的preScale方法的使用,給它設置負數縮放比例,圖像就會進行反轉。然後通過設置Shader添加漸變效果。Java代碼如下:
[代碼]java代碼:
02 |
private
Bitmap getReflectedBitmap() { |
03 |
BitmapDrawable mBitmapDrawable = (BitmapDrawable) getResources().getDrawable(R.drawable.pet);
|
04 |
Bitmap mBitmap = mBitmapDrawable.getBitmap();
|
05 |
int
width = mBitmap.getWidth(); |
06 |
int
height = mBitmap.getHeight(); |
08 |
Matrix matrix =
new Matrix();
|
10 |
matrix.preScale( 1 , - 1 );
|
18 |
Bitmap mInverseBitmap = Bitmap.createBitmap(mBitmap,
0 , 0 , width, height, matrix,
false );
|
19 |
Bitmap mReflectedBitmap = Bitmap.createBitmap(width, height* 2 , Config.ARGB_8888);
|
22 |
Canvas mCanvas =
new Canvas(mReflectedBitmap);
|
24 |
mCanvas.drawBitmap(mBitmap,
0 , 0 ,
null ); |
25 |
mCanvas.drawBitmap(mInverseBitmap,
0 , height,
null ); |
28 |
Paint mPaint =
new Paint();
|
29 |
Shader mShader =
new LinearGradient( 0 , height,
0 , mReflectedBitmap.getHeight(),
0x70ffffff ,
0x00ffffff , TileMode.MIRROR);
|
30 |
mPaint.setShader(mShader);
|
32 |
mPaint.setXfermode( new
PorterDuffXfermode(PorterDuff.Mode.DST_IN)); |
34 |
mCanvas.drawRect( 0 , height, width, mReflectedBitmap.getHeight(), mPaint);
|
36 |
return
mReflectedBitmap; |
效果如下圖所示:
圖7-11 圖像倒影
5)圖像剪切
如果只需要圖像的一部分,就必須對圖像進行剪切處理,在原圖像上選擇一個剪切區域,使用PorterDuffXfermode圖像疊加規則,就可以把指定的圖像區域剪切下來,下面通過三個步驟來說明如果對圖像進行剪切操作。
第一步,創建一個新位圖作爲畫板,然後把原圖像畫到新位圖上面,Java代碼如下:
[代碼]java代碼:
01 |
BitmapDrawable bd = (BitmapDrawable) getResources().getDrawable(
|
03 |
Bitmap bitmap = bd.getBitmap();
|
04 |
int
w = bitmap.getWidth(); |
05 |
int
h = bitmap.getHeight(); |
06 |
Bitmap bm = Bitmap.createBitmap(w, h, Config.ARGB_8888);
|
07 |
Canvas canvas = new
Canvas(bm); |
08 |
Paint mPaint = new
Paint(); |
09 |
mPaint.setAntiAlias( true );
|
10 |
mPaint.setStyle(Style.STROKE);
|
11 |
canvas.drawBitmap(bitmap, 0 ,
0 , mPaint); |
效果如下圖所示:
圖7-12 第一步效果圖
第二步,繪製一個剪切區域,比如要剪切人物的臉部區域,需要在指定的位置繪製一個圓角矩形區域,代碼中的座標是在調試中獲得,在其他分辨率下會有所不同,Java代碼如下:
[代碼]java代碼:
03 |
DashPathEffect dashStyle = new
DashPathEffect( new
float [] { 10 ,
5 , 5 ,
5 }, 2 ); |
04 |
RectF faceRect = new
RectF( 0 ,
0 , 88 ,
106 ); |
05 |
float
[] faceCornerii = new
float [] { 30 , 30 , 30 , 30 , 75 , 75 , 75 , 75 };
|
06 |
Paint mPaint = new
Paint();
|
07 |
mPaint.setColor( 0xFF6F8DD5 );
|
08 |
mPaint.setStrokeWidth( 6 );
|
09 |
mPaint.setPathEffect(dashStyle);
|
10 |
Path clip = new
Path();
|
12 |
clip.addRoundRect(faceRect, faceCornerii, Direction.CW); |
14 |
canvas.translate(deltX, deltY);
|
15 |
canvas.clipPath(clip, Region.Op.DIFFERENCE);
|
16 |
canvas.drawColor( 0xDF222222 );
|
17 |
canvas.drawPath(clip, mPaint); |
效果如下圖所示:
圖7-13 第二步效果
第三步,從原圖像上獲取指定區域的圖像,並繪製到屏幕上,java代碼如下:
[代碼]java代碼:
1 |
Rect srcRect = new
Rect( 0 ,
0 , 88 ,
106 ); |
2 |
srcRect.offset(deltX, deltY);
|
3 |
PaintFlagsDrawFilter dfd = new
PaintFlagsDrawFilter(Paint.ANTI_ALIAS_FLAG, |
4 |
Paint.FILTER_BITMAP_FLAG); |
5 |
canvas.setDrawFilter(dfd); |
7 |
canvas.drawBitmap(bitmap, srcRect, faceRect, mPaint); |
效果如下圖所示:
圖7-13 第三部效果圖
6)圖像合成
如果要爲圖片添加水印,或者把幾張小圖片拼接成大圖片時,就需要利用圖像合成的方法,在前面實例代碼中已經使用了這種方法,就是創建新位圖作爲畫板,然後在對應的位置上繪製其他圖像。
讀者可以參考本章Demo中的getCompoundedBitmap方法,這裏不做過多說明。