android遮罩Xfermode的学习

Posted z8z87878

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了android遮罩Xfermode的学习相关的知识,希望对你有一定的参考价值。

没有绝对的实力,就看运气吧。

先看看效果吧

根据官方APIDemo给出的Xfermode例子我们可以看到下图展示那样

但是,说实话,看到这张图,我是懵逼的,我们应该去自己试一试来加深下自己的理解,所以我画了下,得到的结果如下,仅供参考!最好自己试一试

我先画的正方形,后画的圆


//        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); //圆和正方形相交部分清除,圆与透明部分相交也清除
//        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC)); //覆盖在表面,相交部分正方形被清除,圆与透明相交不被清除
//        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST)); //清除覆盖在上面的圆,圆相交不相交部分都清除
//        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER));//圆覆盖在表面,正方形内部没影响,圆与透明相交不被清除
//        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OVER));//清除覆盖在上面的圆,圆与透明部分相交不清除
//        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));//显示相交的,覆盖在上面的圆,清除不相交部分的圆和相交部分的正方形
//        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));//圆被清除,相交部分随着圆的透明度变化而正比例变化
//        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT));//清除相交部分的圆和正方形,不相交不清除
//        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));//圆被清除,相交部分随着圆的透明度变化而反比例
//        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP));//显示相交的,覆盖在上面的圆,清除不相交部分的圆不清除相交部分的正方形,和SRC_IN对比
//        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP));//相交部分圆被清除,相交部分随着圆的透明度变化而正比例变化,与DST_IN对比
//        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.XOR));//相交部分圆被清除,相交部分随着圆的透明度变化而反比例变化
//        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DARKEN));//相交部分加黑色素,透明度越大,越黑
//        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.LIGHTEN));//相交部分加亮色素,透明度越大,越亮
//        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY));//相交部分加黑色素,透明度越大,越黑,圆不相交部分被清除
//        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SCREEN));//相交部分加亮色素,透明度越大,越亮,感觉和LIGHTEN 一样

仅供参考,最好自己试一试。

弄清楚各遮罩的效果,就可以开始做了,这里面,实现圆形头像我使用的是PorterDuff.Mode.SRC_IN

//显示相交的,覆盖在上面的圆,清除不相交部分的圆和相交部分的正方形

嗯,这可以先画一个实心圆,然后在把背景图片画上去,PorterDuff.Mode.SRC_IN会清除不相交部分的背景图,所以就只剩下相交部分的圆形背景图了,这样就实现了。原理就是这样,我们来看看实际怎么实现吧

/**
 * Created by Administrator on 2016/10/16 0016.
 */

public class CircleView extends View 

     public CircleView(Context context) 
        this(context,null);
    

    public CircleView(Context context, AttributeSet attrs) 
        super(context, attrs);
    

    @Override
    public void draw(Canvas canvas)  //重写view的draw方法,view绘制的时候会调用此方法

        Bitmap bitmap = Bitmap.createBitmap(getWidth(),getHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas1 = new Canvas(bitmap); //bitmap为画纸,canvas1是画板
        super.draw(canvas1); //把背景图画在canvas1这张画板上,但注意画是画在纸上的,即背景图在bitmap上

        Bitmap bitmap2 = Bitmap.createBitmap(bitmap.getWidth(),bitmap.getHeight(), Bitmap.Config.ARGB_8888);//注意这里要带A即支持透明
        Canvas canvas2 = new Canvas(bitmap2); //再来一张新画纸,新画板

        Paint paint = new Paint();
        paint.setAntiAlias(true);
        paint.setColor(Color.WHITE);
        canvas2.drawARGB(0,0,0,0);   //初始化画纸带透明

        //画实心圆 paint的style默认是fill填充的
        canvas2.drawCircle(bitmap.getWidth()/2,bitmap.getHeight()/2,bitmap.getWidth()/2,paint);

        //设置遮罩的模式
        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));

        //前面我们已经通过super.draw(canvas1);把背景都画在了画纸bitmap上,即此时画背景即是画bitmap
        canvas2.drawBitmap(bitmap,0,0,paint);//此时,其实我们想要的效果就得到了,但是如果就这么结束的话,
        //是不行的,因为我们画在canvas2画板上!!!而view要显示是要画在,它自己带的参数canvas画板上的!!!

        //所以我们这里把canvas2画板上的画纸bitmap2画上来,这样view就达到我们想要的效果了
        canvas.drawBitmap(bitmap2,0,0,null);

        addStroke(canvas);

    

    /**增加描边*/
    private void addStroke(Canvas canvas) 
        Paint paint = new Paint();
        paint.setAntiAlias(true);
        paint.setStrokeWidth(2);
        paint.setColor(Color.RED);
        paint.setStyle(Paint.Style.STROKE);
        canvas.drawCircle(getWidth() / 2, getHeight() / 2,
                (float) (getHeight() / 2 -1), paint);
    

布局

<com.and.xfmode.views.CircleView
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:layout_centerInParent="true"
        android:background="@mipmap/tou"/>

我注释写的还算详细吧,跟着注释跟着代码一起读觉得比较好理解.

接下来的微信效果其实也是大巫小巫的问题,不是问题,这里用的是PorterDuff.Mode.SRC_ATOP

//显示相交的,覆盖在上面的圆,清除不相交部分的圆不清除相交部分的正方形,和SRC_IN对比

它与SRC_IN不同的就是他不会清除相交部分 覆盖在下面的图形,然后我们改变上面的透明度就行了。选来看看那个图标

格子表示透明,好吧,中间那坨本来也是透明的,是我涂的白色,这里推荐一个阿里的图标网站http://www.iconfont.cn/,真的很好,雷锋啊。嗯,原理是这样,来看看怎么实现吧

<com.and.xfmode.views.GradientView
        android:id="@+id/gv"
        android:layout_width="120dp"
        android:layout_height="60dp"
        android:background="#10ea10"/>
public class GradientView extends View
    public GradientView(Context context) 
        this(context,null);
    

    public GradientView(Context context, AttributeSet attrs) 
        this(context, attrs,0);
    

    int alpha = 255;
    public GradientView(Context context, AttributeSet attrs, int defStyleAttr) 
        super(context, attrs, defStyleAttr);

        final Timer timer = new Timer();
        TimerTask task = new TimerTask() 
            @Override
            public void run() 

                alpha -= 10;      //每300毫秒背景色越透明
                if (alpha < 0)
                    timer.cancel();

                else
                    postInvalidate(); //主动调draw方法刷新视图
                


            
        ;
        timer.schedule(task,1000,300);
    

    public Bitmap icon;

    public  void setBitmap(Bitmap bitmap)
        icon = bitmap;  //外部传图标
        postInvalidate();//主动调draw方法刷新视图
    

    private String txt;
    public  void setText(String text)
        txt = text;    //外部传文字
        postInvalidate();//主动调draw方法刷新视图
    


    @Override
    public void draw(Canvas canvas)   //与上面的实现圆形头像一样,只是换了个遮罩模式

        Bitmap bitmap = Bitmap.createBitmap(getWidth(),getHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas1 = new Canvas(bitmap);
        super.draw(canvas1);   //背景色画在bitmap上

        Bitmap bitmap1 = Bitmap.createBitmap(bitmap.getWidth(),bitmap.getHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas2 = new Canvas(bitmap1);

        canvas2.drawARGB(0,0,0,0);
        Paint paint = new Paint();
        paint.setAntiAlias(true);
        paint.setTextAlign(Paint.Align.CENTER);
        paint.setTextSize(13);
        paint.setColor(Color.BLACK);

        if(txt != null) //画文字
            canvas2.drawText(txt,getWidth()/2,getHeight()-5,paint);
        


        if(icon != null) //画图片

            Matrix matrix = new Matrix(); //帮助实现bitmap缩放

            matrix.postScale(getWidth()/3.0f/icon.getWidth(),getWidth()/3.0f/icon.getHeight());
            canvas2.drawBitmap(Bitmap.createBitmap(icon,0,0,icon.getWidth(),icon.getHeight(),matrix,true),getWidth()/3,0,null);
        


        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP));

        paint.setAlpha(alpha); //透明度变化
        canvas2.drawBitmap(bitmap,0,0,paint);  //将背景色画在图标和文字上面,相交的部分才显示

        canvas.drawBitmap(bitmap1,0,0,null);  //最后画在画板上,不理解回去看圆形图片实现

        //四周的边框
        Paint paint1 = new Paint();
        paint1.setColor(Color.RED);
        paint.setAntiAlias(true);
        paint1.setStyle(Paint.Style.STROKE);
        canvas.drawRect(0,0,getWidth(),getHeight(),paint1);
    
@Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_circle);

        GradientView gv = (GradientView) findViewById(R.id.gv);
        gv.setBitmap(BitmapFactory.decodeResource(getResources(),R.mipmap.weixin_logo));
        gv.setText("微信");
    

最重要的还是知道了各种遮罩的特性,有时间自己最好试一下,下次就用这个GradientView来实现微信的底部导航菜单吧

以上是关于android遮罩Xfermode的学习的主要内容,如果未能解决你的问题,请参考以下文章

Android Xfermode 学习笔记

Android Paint Xfermode 学习小结

鹅厂系列五--仿微信底部导航菜单

鹅厂系列五--仿微信底部导航菜单

Android 自定义 View 进阶 - Xfermode

Android 颜色渲染 PorterDuff及Xfermode详解