三:自定义view,绘制圆形图片

1、BitmapShader

可以通过为paint指定一个渐变(BitmapShader,Paint.setShader(shader)),以图片填充BitmapShader,就可以得到如下圆形图片。

使用BitmapShader绘制圆形
1、渐变模式

BitmapShader,位图渐变,是将图片作为背景,绘制到指定的位图中,如果图片比为图小,则以以下模式进行填充:

  • TileMode.CLAMP :不平铺,即AABB型
  • TileMode.REPEAT :平铺,即ABABA型
  • TileMode.MIRROR:镜像平铺,即ABBA型

效果如下

三:自定义view,绘制圆形图片_第1张图片
位图渐变下的CLAMP
三:自定义view,绘制圆形图片_第2张图片
位图渐变下的REPEAT
三:自定义view,绘制圆形图片_第3张图片
位图渐变下的MIRROR
三:自定义view,绘制圆形图片_第4张图片
线性渐变下的CLAMP 、MIRROR、REPEAT
2、Matrix

可以使用 shader.setLocalMatrix(localM),使Matrix 和渐变结合,实现 位移、旋转、缩放、拉斜的渐变效果,如下可以缩放被填充的图片,移动其中心点到位图的中心点。

主要代码如下


        BitmapShader shader=new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);

         //计算缩放比例,缩放被填充的图片,移动其中心点到位图的中心点
        Matrix localM=new Matrix();
        int bitmapWidth=bitmap.getWidth();
        int bitmapHeight=bitmap.getHeight();

        float scaleX=width/(bitmapWidth*1.0f);
        float scaleY=height/(bitmapHeight*1.0f);
        float scale=Math.max(scaleX,scaleY);
        localM.setScale(scale,scale);

        if(scaleX

当然除了圆形,也可以绘制其他任意形状

        path.moveTo(0,height/3);
        path.lineTo(width,height/3);
        path.lineTo(width/4,height);
        path.lineTo(width/2,0);
        path.lineTo(3*width/4,height);
        path.close();
        canvas.drawPath(path,paint);
使用BitmapShader绘制

2、xfermode

可以先在画布上画图片,在将xfermode设为DST_IN,在画圆,如下:

xfermode,DST_IN

代码如下

        xfermode=new PorterDuffXfermode(PorterDuff.Mode.DST_IN);

        ...

        int width=getMeasuredWidth();
        int height=getMeasuredHeight();
        int radius=Math.min(width/2,height/2);
        int centerX=width/2;
        int centerY=height/2;

        desbitmap=Bitmap.createScaledBitmap(bitmap,width,height,true);

        circleBitmap=Bitmap.createBitmap(width,height, Bitmap.Config.ARGB_8888);
        Canvas canvas=new Canvas(circleBitmap);
        canvas.drawCircle(centerX,centerY,radius,paint);


        int layer=canvas.saveLayer(0,0,getMeasuredWidth(),getMeasuredHeight(),null,Canvas.ALL_SAVE_FLAG);
        paint.setXfermode(xfermode);
        canvas.drawBitmap(desbitmap,0,0,null);
        canvas.drawBitmap(circleBitmap,0,0,paint);
        paint.setXfermode(null);
        canvas.restoreToCount(layer);


当然,也可以是其他形状

panda

1、 关于xfermode

PorterDuffXfermode,可以将所绘制的图形的像素与Canvas中对应位置的像素按照一定规则进行混合,形成新的像素值。当使用PorterDuffXfermode时,需要将将其作为参数传给Paint.setXfermode(Xfermode xfermode)方法,这样在用该画笔paint进行绘图时,Android就会使用传入的PorterDuffXfermode,如果不想再使用Xfermode,那么可以执行Paint.setXfermode(null)。

一般我们在调用canvas.drawXXX()方法时都会传入一个画笔Paint对象,Android在绘图时会先检查该画笔Paint对象有没有设置Xfermode,如果没有设置Xfermode,那么直接将绘制的图形覆盖Canvas对应位置原有的像素;如果设置了Xfermode,那么会按照Xfermode具体的规则来更新Canvas中对应位置的像素颜色。就本例来说,在执行canvas.drawCirlce()方法时,画笔Paint没有设置Xfermode对象,所以绘制的黄色圆形直接覆盖了Canvas上的像素。当我们调用canvas.drawRect()绘制矩形时,画笔Paint已经设置Xfermode的值为PorterDuff.Mode.CLEAR,此时Android首先是在内存中绘制了这么一个矩形,所绘制的图形中的像素称作源像素(source,简称src),所绘制的矩形在Canvas中对应位置的矩形内的像素称作目标像素(destination,简称dst)。源像素的ARGB四个分量会和Canvas上同一位置处的目标像素的ARGB四个分量按照Xfermode定义的规则进行计算,形成最终的ARGB值,然后用该最终的ARGB值更新目标像素的ARGB值。
本例中的Xfermode是PorterDuff.Mode.CLEAR,该规则比较简单粗暴,直接要求目标像素的ARGB四个分量全置为0,即(0,0,0,0),即透明色,所以我们通过canvas.drawRect()在Canvas上绘制了一个透明的矩形,由于Activity本身屏幕的背景时白色的,所以此处就显示了一个白色的矩形。


三:自定义view,绘制圆形图片_第5张图片
PorterDuff.Mode.CLEAR,没有使用layer

PorterDuffXfermode支持以下十几种像素颜色的混合模式,分别为:CLEAR、SRC、DST、SRC_OVER、DST_OVER、SRC_IN、DST_IN、SRC_OUT、DST_OUT、SRC_ATOP、DST_ATOP、XOR、DARKEN、LIGHTEN、MULTIPLY、SCREEN。

我们知道一个像素的颜色由四个分量组成,即ARGB,第一个分量A表示的是Alpha值,后面三个分量RGB表示了颜色。我们用S代表源像素,源像素的颜色值可表示为[Sa, Sc],Sa中的a是alpha的缩写,Sa表示源像素的Alpha值,Sc中的c是颜色color的缩写,Sc表示源像素的RGB。我们用D代表目标像素,目标像素的颜色值可表示为[Da, Dc],Da表示目标像素的Alpha值,Dc表示目标像素的RGB。

源像素与目标像素在不同混合模式下计算颜色的规则如下所示:

SRC:[Sa, Sc],显示上层位图
1、SRC

三:自定义view,绘制圆形图片_第6张图片
2、SRC
DST:[Da, Dc],只显示下层位图
1、DST
SRC_OVER:[Sa + (1 - Sa)Da, Rc = Sc + (1 - Sa)Dc],上下层都显示,运算后上层显示在上面
1、SRC_OVER
DST_OVER:[Sa + (1 - Sa)Da, Rc = Dc + (1 - Da)Sc],上下层都显示,运算后下层显示在上面
1、DST
SRC_IN:[Sa * Da, Sc ** Da],取两层交集部分,内容取决于上层
1、SRC
DST_IN:[Sa * Da, Sa ** Dc],取两层交集部分,内容取决于下层
1、DST_IN

三:自定义view,绘制圆形图片_第7张图片
2、DST_IN
SRC_OUT:[Sa * (1 - Da), Sc (1 - Da)],取上层非交集部分
SRC_OUT,这里,Da * (1 - Sa),透明度始终是0
DST_OUT:[Da * (1 - Sa), Dc (1 - Sa)],取下层非交集部分
DST_OUT
SRC_ATOP:[Da, Sc * Da + (1 - Sa) Dc]
SRC_ATOP
DST_ATOP:[Sa, Sa * Dc + Sc (1 - Da)]
DST_ATOP
XOR:[Sa + Da - 2 Sa * Da, Sc (1 - Da) + (1 - Sa) Dc]
XOR

。。。

三:自定义view,绘制圆形图片_第8张图片
xfermode

注:xfermode,计算的是重叠部分的最终像素
1、des(图片)、src(空心圆)和最终位图大小相等,src是空心圆
2、des(黄圆)、src(蓝,矩形)和最终位图大小相等

参考:Android中Canvas绘图之PorterDuffXfermode使用及工作原理详解

2、关于 Canvas c=new Canvas(mybitmap):

        ...
        Bitmap mybitmap=BitmapFactory.decodeResource(context.getResources(),R.drawable.test3);
        boolean isMutable=mybitmap.isMutable();
        Canvas c=new Canvas(mybitmap);
        ...

如果这样设置,会报错:

 Immutable bitmap passed to Canvas constructor

就是说如果bitmap不可改变的情况下,canvas是不允许进行绘制的, 当你用BitmapFactory.decodeResource,返回的bitmap是默认状态下的mIsMutable=false。而使用Bitmap.createBitmap()返回的是 true。

        ...
        Bitmap b=BitmapFactory.decodeResource(context.getResources(),R.drawable.test3,options);
        mybitmap=Bitmap.createScaledBitmap(b,b.getWidth(),b.getHeight(),true);
        boolean isMutable=mybitmap.isMutable();
        Canvas c=new Canvas(mybitmap);
        ...

这种情况下mybitmap依然是b,因为大小一样,就返回了相同的bitmap。

    /**
     * Creates a new bitmap, scaled from an existing bitmap, when possible. If the
     * specified width and height are the same as the current width and height of
     * the source bitmap, the source bitmap is returned and no new bitmap is
     * created.
     */
    public static Bitmap createScaledBitmap(Bitmap src, int dstWidth, int dstHeight,
            boolean filter) {
        ...
        Bitmap b = Bitmap.createBitmap(src, 0, 0, width, height, m, filter);
        ...
        return b;
    }

参考: 关于new Canvas(Bitmap)中Bitmap的isMutable的要求、PorterDuffXferMode不正确的真正原因PorterDuffXferMode深入试验) 、在android中画圆形图片的几种办法、 自定义控件三部曲之绘图篇(十)——Paint之setXfermode(一)

你可能感兴趣的:(三:自定义view,绘制圆形图片)