Android 遮罩層效果


(用別人的代碼進行分析) 不知道在開發中有沒有經常使用到這種效果,所謂的遮罩層就是給一張圖片不是我們想要的形狀,這個時候我們就可以使用遮罩效果把這個圖片變成我們想要的形狀,一般使用最多就是圓形的效果,如下圖:

上面這個圖片是圓形的,而我們這個原圖是正方形的,所以我們可能就需要這么一個遮罩的效果使它變為圓形,這種一般就是我們圖片從網絡上獲取的,形狀不是由我們自己定的,所以才會加上這么一個效果,看下面的原圖:

這個是一個正方形的,那么要弄這么一個圓形,我們還需要一個圓形全黑的圖片,如下

就是這個圖片,其實簡單的來說就是兩個圖片相結合,只顯示被黑色圖片覆蓋的區域,外面的區域就不顯示,這樣就成一個圓形的圖片了(上面的圖片都是采用從別的應用程序來的),其實這個主要的技術還是涉及到了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()這個方法里面就是對圖片進行等比的縮放效果,縮放的大小就和我們全黑色的圖片大小一樣(這里我們原來外面圓形效果去掉了,就是上面第三張全白色的圖片)
如果圖片不是正方形的我們就需要進行判斷對比,最后得出的寬和高就是我們要等比縮放的大小。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM