zoukankan      html  css  js  c++  java
  • 【原】使用Xfermode正确的绘制出遮罩效果

    以前写as3的时候,遮罩效果一个mask属性就搞定了,真是方便。

    转到android上以后,发现要实现类似的效果,可以使用Xfermode,android一共提供了三种:

    AvoidXfermode;
    PixelXorXfermode;
    PorterDuffXfermode;

    前两种已经不被推荐使用了(据说是因为不支持硬件加速,要生效得强制关闭硬件加速),就不细说了,主要说说第三种,一共提供了十六种效果(as3里也提供了类似,但是更加复杂的方法,所以对我而言还是比较熟悉的),如图所示:

    但是要正确的在canvas上实现这些效果,还真是没那么容易,我也是研究了半天,终于实现了自己想要的效果,下面用一个例子说明下我的操作流程。

    想要实现的效果是这样的:

    简单分析一下,绘制一个圆形和一个矩形,计算好相应的坐标位置,然后使用SRC_IN进行混合就可以了,类似这样:

    下面说一下我的操作流程:

    1. 绘制border

    2. 保存为单独层(canvas.saveLayer),特别注意这一步必须要有,否则无论如何出不来正常效果,起码我试了很久没有成功

    3. 绘制填充的圆形,同时也是遮罩

    4. 设置笔触的Xfermode为new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)

    5. 使用该笔触绘制矩形,进行混合

    6. 恢复到canvas上

    上代码,大家可以对照看一下各个步骤的具体代码

        @Override
        protected void onDraw(Canvas canvas) {
            float strokeWidth = getResources().getDimension(R.dimen.stroke_width);
            int borderColor = getResources().getColor(R.color.carnation);
            int fillColor = getResources().getColor(R.color.carnation_lighter);
            int percentColor = getResources().getColor(R.color.carnation_light);
            int width = getWidth();
            int height = getHeight();
    
            //border
            Paint stroke = new Paint(Paint.ANTI_ALIAS_FLAG);
            stroke.setStrokeWidth(strokeWidth);
            stroke.setStyle(Paint.Style.STROKE);
            stroke.setColor(borderColor);
            canvas.drawOval(new RectF(strokeWidth/2,strokeWidth/2,width-strokeWidth/2,height-strokeWidth/2),stroke);
    
            //save as new layer
            int save = canvas.saveLayer(0,0,width,height,null,Canvas.ALL_SAVE_FLAG);
    
            //fill background
            Paint fill = new Paint(Paint.ANTI_ALIAS_FLAG);
            fill.setStyle(Paint.Style.FILL);
            fill.setColor(fillColor);
            canvas.drawOval(new RectF(strokeWidth - 1, strokeWidth - 1, width - strokeWidth + 1, height - strokeWidth + 1), fill);
    
            //mix rect
            fill.setColor(percentColor);
            fill.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
            canvas.drawRect(0,(1-mPercent)*height,width,height,fill);
    
            //restore to canvas
            canvas.restoreToCount(save);
    
            super.onDraw(canvas);
        }

    之前也查了不少文章,貌似没有看到多少着重说saveLayer的,还是我对照官方apidemos源码试出来的,希望对遇到同样疑问的朋友有所帮助!

    2015/7/9 更新:

    1. 要实现混合的两个图形,必须位于同一个layer上,经测试位于不同layer上是无法混合的,即使最后都绘制到了canvas上。

    2. 不同的绘制顺序,可能有不同的效果,注意一下逻辑即可。

    更新一段复杂点的例子

    使用了两种混合方式SRC_IN和CLEAR,主要代码如下:

     1     @Override
     2     protected void onDraw(Canvas canvas) {
     3         int width = getWidth();
     4         int height = getHeight();
     5 
     6         float strokeWidth = DimenUtils.dp2px(4);
     7         float pointRadius = DimenUtils.dp2px(4);
     8         float gap = DimenUtils.dp2px(4);
     9         float monthRadius = height * 0.2f;
    10         float textSize = DimenUtils.dp2px(14);
    11 
    12         int color = getResources().getColor(R.color.carnation);
    13         int lightColor = getResources().getColor(R.color.carnation_light);
    14         int lighterColor = getResources().getColor(R.color.carnation_lighter);
    15 
    16         float degree = 360*mRate;
    17 
    18         Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
    19 
    20         //new layer
    21         int save = canvas.saveLayer(0, 0, width, height, null, Canvas.ALL_SAVE_FLAG);
    22 
    23         //draw percent
    24         canvas.save();
    25         float fillDistance = pointRadius+gap+strokeWidth/2;
    26         canvas.translate(fillDistance,fillDistance);
    27         RectF fillRect = new RectF(0,0,width-2*fillDistance,height-2*fillDistance);
    28         paint.setColor(lightColor);
    29         paint.setStyle(Paint.Style.FILL);
    30         canvas.drawOval(fillRect,paint);
    31         //mix rect
    32         paint.setColor(color);
    33         paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
    34         canvas.drawRect(0, (1 - mPercent) * (height-2*fillDistance), width-2*fillDistance, height-2*fillDistance, paint);
    35         canvas.restore();
    36 
    37         //border
    38         paint.setXfermode(null);
    39         paint.setStrokeWidth(strokeWidth);
    40         paint.setStyle(Paint.Style.STROKE);
    41         paint.setColor(lighterColor);
    42         RectF borderRect = new RectF(pointRadius,pointRadius,width-pointRadius,height-pointRadius);
    43         canvas.drawOval(borderRect, paint);
    44         paint.setColor(color);
    45         canvas.drawArc(borderRect,270,degree,false,paint);
    46         //draw point
    47         canvas.save();
    48         paint.setStyle(Paint.Style.FILL);
    49         canvas.translate(width/2,height/2);
    50         canvas.rotate(degree);
    51         canvas.drawCircle(0,pointRadius-height/2,pointRadius,paint);
    52         canvas.restore();
    53 
    54         //draw month
    55         canvas.save();
    56         canvas.translate(width*0.7f,height*0.8f);
    57         paint.setColor(Color.BLACK);
    58         paint.setStyle(Paint.Style.STROKE);
    59         paint.setStrokeWidth(gap);
    60         paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
    61         canvas.drawCircle(0,0,monthRadius+gap/2,paint);
    62         paint.setXfermode(null);
    63         paint.setStyle(Paint.Style.FILL);
    64         paint.setColor(getResources().getColor(R.color.orange));
    65         canvas.drawCircle(0,0,monthRadius,paint);
    66         canvas.restoreToCount(save);
    67     }

  • 相关阅读:
    ArcGIS为面要素生成邻接矩阵
    图片整理备份
    导出CityGML
    [ML] 数据处理
    微信公众号开发之access_token的全局共用
    DataReader转Dictionary数据类型之妙用
    标准化接口系统改造
    利用通用权限管理系统底层解决数据从不同库的导入导出问题
    通用权限管理系统中数据权限功能开发及使用说明
    常用API接口签名验证参考
  • 原文地址:https://www.cnblogs.com/DarkMaster/p/4618872.html
Copyright © 2011-2022 走看看