(用別人的代碼進行分析) 不知道在開發中有沒有經常使用到這種效果,所謂的遮罩層就是給一張圖片不是我們想要的形狀,這個時候我們就可以使用遮罩效果把這個圖片變成我們想要的形狀,一般使用最多就是圓形的效果,如下圖:
上面這個圖片是圓形的,而我們這個原圖是正方形的,所以我們可能就需要這么一個遮罩的效果使它變為圓形,這種一般就是我們圖片從網絡上獲取的,形狀不是由我們自己定的,所以才會加上這么一個效果,看下面的原圖:
這個是一個正方形的,那么要弄這么一個圓形,我們還需要一個圓形全黑的圖片,如下
就是這個圖片,其實簡單的來說就是兩個圖片相結合,只顯示被黑色圖片覆蓋的區域,外面的區域就不顯示,這樣就成一個圓形的圖片了(上面的圖片都是采用從別的應用程序來的),其實這個主要的技術還是涉及到了PorterDuffXfermode類的使用方法,
PorterDuffXfermode xfermode = new PorterDuffXfermode(PorterDuff.Mode.SRC_IN);
上面就是PorterDuffXfermode類的創建,那么里面的參數PorterDuff.Mode.SRC_IN其實有很多中情況,好像有達到16中情況,下面介紹一下常用的:
PorterDuff.Mode.SRC_IN:取兩層繪制交集。顯示上層。就是如果上面兩張圖片相疊,那么取這兩張圖片的交集而且顯示的是上層那種圖片
PorterDuff.Mode.DST_IN: 取兩層繪制交集。顯示下層。
其它可以到網上可以查詢得到,這兩個用得也比較多。下面還有一張圖片是外圍那層圖片的效果
好,我們現在來看看這個代碼是如何來寫的
package com.example.myimageview; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.util.AttributeSet; import android.view.View; public class MyImageView extends View { private Context context; private Bitmap bitmapBorder; private Bitmap bitmapMask; private Paint paint; private PorterDuffXfermode xfermode; private Bitmap bitmap; private int _width; private int _height; public MyImageView(Context context){ this(context, null); } public MyImageView(Context context, AttributeSet attrs) { super(context, attrs); this.context = context; bitmapBorder = decodeBitmap(R.drawable.border); bitmapMask = decodeBitmap(R.drawable.mask); _width = bitmapBorder.getWidth(); _height = bitmapBorder.getHeight(); paint = new Paint(); xfermode = new PorterDuffXfermode(PorterDuff.Mode.SRC_IN); } public MyImageView(Context context, AttributeSet attrs, int defStyle) { this(context, attrs); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(_width, _height); } private Bitmap decodeBitmap(int resId) { return BitmapFactory.decodeResource(context.getResources(), resId); } public void setResourseId(int resourseId) { bitmap = decodeBitmap(resourseId); invalidate(); } public void setResourseBitmap(Bitmap bitmap){ this.bitmap = bitmap; invalidate(); } @Override protected void onDraw(Canvas canvas) { if(bitmap == null){ return; } canvas.drawBitmap(bitmapBorder, 0, 0, paint); int saveFlags = 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.saveLayer(0, 0, _width, _height, null, saveFlags); canvas.drawBitmap(bitmapMask, 0, 0, paint); paint.setXfermode(xfermode); int left = _width/2 - bitmap.getWidth() /2; int top = _height/2 - bitmap.getHeight()/2; canvas.drawBitmap(bitmap, left, top, paint); paint.setXfermode(null); canvas.restore(); } }
下面我們來分析一下代碼的結構:
MyImageView是繼承view的一個子類,在構造函數中我們看到設置兩張圖片bitmapBorder就是我上面貼出來的最下面外圍的圖片,bitmapMask就是那個全黑圓形的圖片,_width和_height是bitmapBorder(外圍那張圖片的寬度和高度),因為那張圖片是最大的我們需要定義這個view的大小所以需這個寬和高,下面
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(_width, _height); }
就是使用寬和高來處理的,同時我們還看到了畫筆Paint和PorterDuffXfermode創建,要說明一下這個PorterDuffXfermode對象是通過畫筆Paint來設置的,下面有說明的
public void setResourseId(int resourseId) { bitmap = decodeBitmap(resourseId); invalidate(); }
這個代碼就是需要我們手動傳入想要變為圓形的圖片,這個是在Avtivity中設置就行,最主要的代碼來看onDraw()方法里面的。
@Override protected void onDraw(Canvas canvas) { if(bitmap == null){ return; } canvas.drawBitmap(bitmapBorder, 0, 0, paint); int saveFlags = 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.saveLayer(0, 0, _width, _height, null, saveFlags); canvas.drawBitmap(bitmapMask, 0, 0, paint); paint.setXfermode(xfermode); int left = _width/2 - bitmap.getWidth() /2; int top = _height/2 - bitmap.getHeight()/2; canvas.drawBitmap(bitmap, left, top, paint); paint.setXfermode(null); canvas.restore(); }
我們來看有三個地方使用了canvas.drawBitmap這個方法,簡單理解就是誰先那么該圖片就顯示在最下面,最后使用的drawBitmap()方法就顯示在最上面
canvas.saveLayer()方法和canvas.restore()是成對出現的,
Canvas 在一般的情況下可以看作是一張畫布,所有的繪圖操作如drawBitmap, drawCircle都發生在這張畫布上,這張畫板還定義了一些屬性比如Matrix,顏色等等。但是如果需要實現一些相對復雜的繪圖操作,比如多層動 畫,地圖(地圖可以有多個地圖層疊加而成,比如:政區層,道路層,興趣點層)。Canvas提供了圖層(Layer)支持,缺省情況可以看作是只有一個圖 層Layer。如果需要按層次來繪圖,Android的Canvas可以使用SaveLayerXXX, Restore 來創建一些中間層,對於這些Layer是按照“棧結構“來管理的:
創建一個新的Layer到“棧”中,可以使用saveLayer, savaLayerAlpha, 從“棧”中推出一個Layer,可以使用restore,restoreToCount。但Layer入棧時,后續的DrawXXX操作都發生在這個 Layer上,而Layer退棧時,就會把本層繪制的圖像“繪制”到上層或是Canvas上,在復制Layer到Canvas上時,可以指定Layer的 透明度(Layer),這是在創建Layer時指定的:public int saveLayerAlpha(RectF bounds, int alpha, int saveFlags);
具體點可以到http://blog.csdn.net/linghu_java/article/details/8939952這個網站了解一下。
paint.setXfermode(xfermode);這個就是設置了畫筆的效果了,left和top不用說了就是該圖片要顯示在中心的位置。canvas.drawBitmap(bitmap, left, top, paint);這個是最后那個canvas.drawBitmap方法,而且使用的PorterDuff.Mode.SRC_IN參數,表示的是取兩層繪制交集。顯示上層,那就是顯示最后一張圖片。
其實從上面來看效果是不錯的,但是在真正的應用中我們不難會發現有些圖片可能很大,這個時候就需要我們對圖片進行等比的放大效果
http://bbs.csdn.net/topics/310218516這個網址就看到有介紹等比縮放圖片的例子,我們來看下面的圖片效果
上面兩張圖對比,第一張就是原來的效果,第二張就是對圖片進行等比的縮放效果,看看第二張圖片代碼的:
package com.example.myimageview; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.PorterDuffXfermode; import android.graphics.RectF; import android.graphics.PorterDuff.Mode; import android.util.AttributeSet; import android.view.View; public class itchqImageView extends View{ private Bitmap bg; private Bitmap photo; private int bg_width; private int bg_height; public itchqImageView(Context context, AttributeSet attrs) { super(context, attrs); // TODO Auto-generated constructor stub init(); } private void init(){ bg=BitmapFactory.decodeResource(getResources(),R.drawable.mask); bg_width=bg.getWidth(); bg_height=bg.getHeight(); } public void setImageView(int imgId){ photo=BitmapFactory.decodeResource(getResources(), imgId); scaleImage(); } @Override protected void onDraw(Canvas canvas) { // TODO Auto-generated method stub super.onDraw(canvas); Paint paint=new Paint(Paint.ANTI_ALIAS_FLAG); paint.setXfermode(new PorterDuffXfermode(Mode.DST_IN)); RectF rectf=new RectF(0,0,bg_width,bg_height); canvas.saveLayer(rectf, null, Canvas.ALL_SAVE_FLAG); canvas.drawBitmap(photo, 0,0, null); canvas.drawBitmap(bg, 0, 0,paint); canvas.restore(); } private void scaleImage(){ if(photo!=null){ int widht=photo.getWidth(); int height=photo.getHeight(); int new_width=0; int new_height=0; if(widht!=height){ if(widht>height){ new_height=bg_height; new_width=widht*new_height/height; }else{ new_width=bg_width; new_height=height*new_width/widht; } }else{ new_width=bg_width; new_height=bg_height; } photo = Bitmap.createScaledBitmap(photo, new_width, new_height, true); } } }
scaleImage()這個方法里面就是對圖片進行等比的縮放效果,縮放的大小就和我們全黑色的圖片大小一樣(這里我們原來外面圓形效果去掉了,就是上面第三張全白色的圖片)
如果圖片不是正方形的我們就需要進行判斷對比,最后得出的寬和高就是我們要等比縮放的大小。