【Android】 自定義View那點事(二)Xfermode篇

回顧

上一節咱們學會了使用Canvas和Paint實現自定義View的繪製,咱們已經知道如何簡單實現自定義View的方法。這一節咱們主要來講說Paint的setXfermode方法,對於Android開發中碰到最可能是實現圓角圖片或是圓形圖片顯示,只要掌握了setXfermode的使用就可以實現咱們想要的圖片顯示效果。html

setXfermode知多少

想了解Xfermode是什麼,固然先去翻翻Android官方開發文檔啦。文檔中Xfermode提供了三個Subclasses:AvoidXfermode,PixelXorXfermode,PorterDuffXfermode而這節咱們先介紹PorterDuffXfermode,這也是和本節要講的內容相關。其餘的放在下次再講吧。android

說說PorterDuffXfermode

既然要說PorterDuffXfermode,它是實現圖形混合模式,當要實現複雜顯示多個圖形重疊時呈現特別的顯示方式PorterDuffXfermode就派上用處。PorterDuffXfermode的構造方法須要傳遞一個PorterDuff.Mode 參數。再查查文檔吧,一看嚇一跳,Enum Values很多,一共有18種mode。具體每一種模式的實現效果是什麼樣的?把代碼敲起來就知道結果了啊。canvas

public class PorterDuffView extends View {

    Paint mPaint;
    Context mContext;
    int BlueColor;
    int PinkColor;
    int mWith;
    int mHeight;
    public PorterDuffView(Context context) {
        super(context);
        init(context);
    }
    public PorterDuffView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    public PorterDuffView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        mHeight = getMeasuredHeight();
        mWith = getMeasuredWidth();
    }

    private void init(Context context) {
        mContext = context;
        BlueColor = ContextCompat.getColor(mContext, R.color.colorPrimary);
        PinkColor = ContextCompat.getColor(mContext, R.color.colorAccent);
        mPaint = new Paint();
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setAntiAlias(true);
    }
    private  Bitmap drawRectBm(){
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setColor(BlueColor);
        paint.setStyle(Paint.Style.FILL);
        paint.setAntiAlias(true);
        Bitmap bm = Bitmap.createBitmap(200,200, Bitmap.Config.ARGB_8888);
        Canvas cavas = new Canvas(bm);
        cavas.drawRect(new RectF(0,0,70,70),paint);
        return bm;
    }
    private  Bitmap drawCircleBm(){
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setColor(PinkColor);
        paint.setStyle(Paint.Style.FILL);
        paint.setAntiAlias(true);
        Bitmap bm = Bitmap.createBitmap(200,200, Bitmap.Config.ARGB_8888);
        Canvas cavas = new Canvas(bm);
        cavas.drawCircle(70,70,35,paint);
        return bm;
    }
    @Override
    protected void onDraw(Canvas canvas) {
        mPaint.setFilterBitmap(false);
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setTextSize(20);
        RectF recf = new RectF(20,20,60,60);
        mPaint.setColor(BlueColor);
        canvas.drawRect(recf,mPaint);
        mPaint.setColor(PinkColor);
        canvas.drawCircle(100,40,20,mPaint);
        int sc = canvas.saveLayer(0, 0,mWith,mHeight, 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);
        int y = 180;
        int x = 50;
            for(PorterDuff.Mode mode : PorterDuff.Mode.values()){
                if(y >= 900){
                    y = 180;
                    x += 200;
                }
                mPaint.setXfermode(null);
                canvas.drawText(mode.name(),x + 100,y,mPaint);
                canvas.drawBitmap(drawRectBm(),x,y,mPaint);
                mPaint.setXfermode(new PorterDuffXfermode(mode));
                canvas.drawBitmap(drawCircleBm(),x,y,mPaint);
                y += 120;
            }
        mPaint.setXfermode(null);
        // 還原畫布
        canvas.restoreToCount(sc);
    }
}

圖片描述
實現效果圖,每一個模式的效果一目瞭然。微信

實踐求真知

知道PorterXfermode的使用模式下面咱們實踐到實際開發中,咱們來繪製圓形圖片和微信聊天圖片。在設計自定義ImageView以前先簡單回顧一下Canvas的繪製,對於Canvas的onDraw每執行一次就是在屏幕上增長一個Bitmap覆蓋在原來的圖層上。繪製自定義形狀圖片是經過將自定義圖形圖層和圖片資源圖層的內容使用PorterDuffXfermode繪製合併而成,那在圖層之間PorterDuffXfermode繪製以前。須要使用 canvas.saveLayerAlpha將原圖層的內容保存,使得圖形圖層是和圖片資源圖層進行繪製操做。在圖層繪製結束以後再使用canvas.restoreToCount恢復保存的原圖層內容。若不使用原圖層保存和恢復的方法的狀況確定達不到原來所想要的效果,但也能夠試試瞭解canvas的繪製原理,看看結果會是怎樣。ide

繪製圓形圖片

//
public class CircleImgView extends ImageView {

    private int mWidth;
    private int mHeight;
    private Paint mPaint;
    private Bitmap CircleBitmap;

    public CircleImgView(Context context) {
        super(context);
    }

    public CircleImgView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public CircleImgView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    private void ImgCircle(){
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setAntiAlias(true);
        paint.setFilterBitmap(true);
        paint.setColor(Color.GRAY);
        paint.setStrokeWidth(5);
        paint.setStyle(Paint.Style.FILL);
        CircleBitmap = Bitmap.createBitmap(mWidth,mHeight, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(CircleBitmap);
        canvas.drawCircle(mWidth/2,mHeight/2,mWidth/2,paint);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        mWidth = getMeasuredWidth();
        mHeight = getMeasuredHeight();
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setFilterBitmap(true);
        ImgCircle();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        int count = canvas.saveLayerAlpha(0, 0, mWidth, mHeight, 250, Canvas.HAS_ALPHA_LAYER_SAVE_FLAG);
        super.onDraw(canvas);
        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
        canvas.drawBitmap(CircleBitmap,0,0, mPaint);
        canvas.restoreToCount(count);
    }
}

繪製微信聊天圖片

public class ImgCustView extends ImageView{

    private int mWidth;
    private int mHeight;
    private Paint mPaint;
    private Bitmap canvasBitmap;
    public ImgCustView(Context context) {
        super(context);
    }

    public ImgCustView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public ImgCustView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    private void ImgShape(){
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setAntiAlias(true);
        paint.setFilterBitmap(true);
        paint.setColor(Color.GRAY);
        paint.setStyle(Paint.Style.FILL);
        canvasBitmap = Bitmap.createBitmap(mWidth,mHeight, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(canvasBitmap);
        Path path = new Path();
        path.moveTo(mWidth - 25,50);
        path.lineTo(mWidth - 25,80);
        path.lineTo(mWidth,65);
        path.close();
        RectF rectF = new RectF(0,0,mWidth - 25,mHeight);
        canvas.drawRoundRect(rectF,20,20,paint);
        canvas.drawPath(path,paint);
    }


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        mWidth = getMeasuredWidth();
        mHeight = getMeasuredHeight();
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setFilterBitmap(true);
        ImgShape();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        int count = canvas.saveLayerAlpha(0, 0, mWidth, mHeight, 250, Canvas.HAS_ALPHA_LAYER_SAVE_FLAG);
        super.onDraw(canvas);
        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
        canvas.drawBitmap(canvasBitmap,0,0, mPaint);
        canvas.restoreToCount(count);
    }

}

主程序

public class ImgCustActivity extends BaseActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(500,500);
        layoutParams.setMargins(20,20,20,20);
        //實例化ImageView並加載圖片資源
        ImageView imageView = new ImageView(this);
        imageView.setScaleType(ImageView.ScaleType.FIT_XY);
        imageView.setImageResource(R.drawable.background);
        imageView.setLayoutParams(layoutParams);
        //實例化圓形ImageView並加載圖片資源
        ImgCustView imgCustView = new ImgCustView(this);
        imgCustView.setScaleType(ImageView.ScaleType.FIT_XY);
        imgCustView.setImageResource(R.drawable.background);
        imgCustView.setLayoutParams(layoutParams);
        //實例化聊天ImageView並加載圖片資源
        CircleImgView circleView = new CircleImgView(this);
        circleView.setScaleType(ImageView.ScaleType.FIT_XY);
        circleView.setImageResource(R.drawable.background);
        circleView.setLayoutParams(layoutParams);

        llContent.setBackgroundColor(Color.LTGRAY);
        llContent.addView(imageView);
        llContent.addView(imgCustView);
        llContent.addView(circleView);
    }
}

圖片描述
最後的效果呈現學習

寫在最後

這節主要學習PorterDuffXfermode模式並經過幾個簡單例子實踐了一下。結合簡單例子咱們還能夠深刻學習繪製更爲複雜有趣的自定義圖形。本節只作到了拋磚迎玉的做用,之後我還會結合其餘內容繪製更有意思和創造性的圖形。以後還會講講關於View的動畫,下次見 see you!動畫

相關文章
相關標籤/搜索