Android View高仿猎豹清理大师自定义开口圆环比例进度

作者:简简单单 2015-09-25

一、概述

Android下的 猎豹清理大师的内存开口圆环比例进度 挺不错的,于是反编译了猎豹清理大师的app,原来是有两张图,于是想了一下思路,利用上下两张图,旋转上面张图以及使用 PorterDuffXfermode  来设置合适的渲染模式,就可以达到效果。下面看看咱们的效果吧

二、效果图


三、Xfermode渲染模式简介:

xfermode影响在Canvas已经有的图像上绘制新的颜色的方式
* 正常的情况下,在图像上绘制新的形状,如果新的Paint不是透明的,那么会遮挡下面的颜色.
* 如果新的Paint是透明的,那么会被染成下面的颜色

下面的Xfermode子类可以改变这种行为:

AvoidXfermode  指定了一个颜色和容差,强制Paint避免在它上面绘图(或者只在它上面绘图)。

PixelXorXfermode  当覆盖已有的颜色时,应用一个简单的像素XOR操作。

PorterDuffXfermode  这是一个非常强大的转换模式,使用它,可以使用图像合成的16条Porter-Duff规则的任意一条来控制Paint如何与已有的Canvas图像进行交互。

这里不得不提到那个经典的图:


上面的16种模式的说明如下:

从上面我们可以看到PorterDuff.Mode为枚举类,一共有16个枚举值:

1.PorterDuff.Mode.CLEAR  

  所绘制不会提交到画布上。
 
2.PorterDuff.Mode.SRC

   显示上层绘制图片
   
3.PorterDuff.Mode.DST

  显示下层绘制图片
 
4.PorterDuff.Mode.SRC_OVER

  正常绘制显示,上下层绘制叠盖。
 
5.PorterDuff.Mode.DST_OVER

  上下层都显示。下层居上显示。
 
6.PorterDuff.Mode.SRC_IN

   取两层绘制交集。显示上层。
   
7.PorterDuff.Mode.DST_IN

  取两层绘制交集。显示下层。
 
8.PorterDuff.Mode.SRC_OUT

 取上层绘制非交集部分。
 
9.PorterDuff.Mode.DST_OUT

 取下层绘制非交集部分。
 
10.PorterDuff.Mode.SRC_ATOP

 取下层非交集部分与上层交集部分
 
11.PorterDuff.Mode.DST_ATOP

 取上层非交集部分与下层交集部分
 
12.PorterDuff.Mode.XOR

  异或:去除两图层交集部分
13.PorterDuff.Mode.DARKEN

  取两图层全部区域,交集部分颜色加深
 
14.PorterDuff.Mode.LIGHTEN

  取两图层全部,点亮交集部分颜色
 
15.PorterDuff.Mode.MULTIPLY

  取两图层交集部分叠加后颜色
 
16.PorterDuff.Mode.SCREEN

  取两图层全部区域,交集部分变为透明色

四、自定义开口圆环View的实现

1、初始化绘制所需的画笔,字体颜色、大小等变量

public XCArcProgressBar(Context context, AttributeSet attrs,
        int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    // TODO Auto-generated constructor stub
    
    
    degrees =  0;
    paint  =  new Paint();
    //从attrs.xml中获取自定义属性和默认值
    TypedArray typedArray  = context.obtainStyledAttributes(attrs, R.styleable.XCRoundProgressBar);
    textColor  =typedArray.getColor(R.styleable.XCRoundProgressBar_textColor, Color.RED);
    textSize = typedArray.getDimension(R.styleable.XCRoundProgressBar_textSize, 15);
    max = typedArray.getInteger(R.styleable.XCRoundProgressBar_max, 100);
    isDisplayText  =typedArray.getBoolean(R.styleable.XCRoundProgressBar_textIsDisplayable, true);
    typedArray.recycle();
    
}


2、在onDraw()中绘制出来

在onDraw()方法中利用PorterDuffXfermode渲染模式绘制两张开口圆环Bitmap,并计算前景图的旋转角度,从而达到效果图效果。

首先先绘制底部背景图,然后绘制进度前景图,最后利用PorterDuffXfermode的渲染模式和旋转角度比例来进行前景图和背景图的遮罩处理。

@Override
protected void onDraw(Canvas canvas) {
    // TODO Auto-generated method stub
    super.onDraw(canvas);
    int width = getWidth();
    int height = getHeight();
    int centerX = getWidth() / 2;// 获取中心点X坐标
    int centerY = getHeight() / 2;// 获取中心点Y坐标
    Bitmap bitmap = Bitmap.createBitmap(width, height, Config.ARGB_8888);
    Canvas can = new Canvas(bitmap);
    // 绘制底部背景图
    bmpTemp = Utils.decodeCustomRes(getContext(), R.drawable.arc_bg);
    float dstWidth = (float) width;
    float dstHeight = (float) height;
    int srcWidth = bmpTemp.getWidth();
    int srcHeight = bmpTemp.getHeight();
    can.setDrawFilter(new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG
            | Paint.FILTER_BITMAP_FLAG));// 抗锯齿
    Bitmap bmpBg = Bitmap.createScaledBitmap(bmpTemp, width, height, true);
    can.drawBitmap(bmpBg, 0, 0, null);
    // 绘制进度前景图
    Matrix matrixProgress = new Matrix();
    matrixProgress.postScale(dstWidth / srcWidth, dstHeight / srcWidth);
    bmpTemp = Utils.decodeCustomRes(getContext(), R.drawable.arc_progress);
    Bitmap bmpProgress = Bitmap.createBitmap(bmpTemp, 0, 0, srcWidth,
            srcHeight, matrixProgress, true);
    degrees = progress * 270 / max - 270;
    //遮罩处理前景图和背景图
    can.save();
    can.rotate(degrees, centerX, centerY);
    paint.setAntiAlias(true);
    paint.setXfermode(new PorterDuffXfermode(Mode.SRC_ATOP));
    can.drawBitmap(bmpProgress, 0, 0, paint);
    can.restore();
    
    if ((-degrees) >= 85) {
        int posX = 0;
        int posY = 0;
        if ((-degrees) >= 270) {
            posX = 0;
            posY = 0;
        } else if ((-degrees) >= 225) {
            posX = centerX / 2;
            posY = 0;
        } else if ((-degrees) >= 180) {
            posX = centerX;
            posY = 0;
        } else if ((-degrees) >= 135) {
            posX = centerX;
            posY = 0;
        } else if ((-degrees) >= 85) {
            posX = centerX;
            posY = centerY;
        }
        
        if ((-degrees) >= 225) {
            can.save();
            Bitmap dst = bitmap
                    .createBitmap(bitmap, 0, 0, centerX, centerX);
            paint.setAntiAlias(true);
            paint.setXfermode(new PorterDuffXfermode(Mode.SRC_ATOP));
            Bitmap src = bmpBg.createBitmap(bmpBg, 0, 0, centerX, centerX);
            can.drawBitmap(src, 0, 0, paint);
            can.restore();
            can.save();
            dst = bitmap.createBitmap(bitmap, centerX, 0, centerX, height);
            paint.setAntiAlias(true);
            paint.setXfermode(new PorterDuffXfermode(Mode.SRC_ATOP));
            src = bmpBg.createBitmap(bmpBg, centerX, 0, centerX, height);
            can.drawBitmap(src, centerX, 0, paint);
            can.restore();
        } else {
            can.save();
            Bitmap dst = bitmap.createBitmap(bitmap, posX, posY, width
                    - posX, height - posY);
            paint.setAntiAlias(true);
            paint.setXfermode(new PorterDuffXfermode(Mode.SRC_ATOP));
            Bitmap src = bmpBg.createBitmap(bmpBg, posX, posY,
                    width - posX, height - posY);
            can.drawBitmap(src, posX, posY, paint);
            can.restore();
        }
    }
    //绘制遮罩层位图
    canvas.drawBitmap(bitmap, 0, 0, null);
    
    // 画中间进度百分比字符串
    paint.reset();
    paint.setStrokeWidth(0);
    paint.setColor(textColor);
    paint.setTextSize(textSize);
    paint.setTypeface(Typeface.DEFAULT_BOLD);
    int percent = (int) (((float) progress / (float) max) * 100);// 计算百分比
    float textWidth = paint.measureText(percent + "%");// 测量字体宽度,需要居中显示
    if (isDisplayText && percent != 0) {
        canvas.drawText(percent + "%", centerX - textWidth / 2, centerX
                + textSize / 2 - 25, paint);
    }
    //画底部开口处标题文字
    paint.setTextSize(textSize/2);
    textWidth = paint.measureText(title);
    canvas.drawText(title, centerX-textWidth/2, height-textSize/2, paint);
}


3、设置比例进度的同步接口方法,主要供刷新进度比例用。

/**
 * 设置进度,此为线程安全控件,由于考虑多线的问题,需要同步
 * 刷新界面调用postInvalidate()能在非UI线程刷新
 * @author caizhiming
 */ 
public synchronized void setProgress(int progress) {
    if(progress < 0){
        throw new IllegalArgumentException("progress must more than 0");
    }
    if(progress > max){
        this.progress = progress;
    }
    if(progress <= max){
        this.progress = progress;
        postInvalidate();
    }
}

最后,自己试玩着感觉良好,可以直接应用到实际项目中

相关文章

精彩推荐