自定义view之仿慕课网课程学习圆形进度

请尊重个人劳动成果,转载注明出处,谢谢!
http://blog.csdn.net/xiaxiazaizai01

在做项目时需要实现一个带空白间隙的圆形加载进度view,并且要求颜色渐变,并简单的实现了手势上滑增加进度,下滑减少进度。老规矩,上效果图
这里写图片描述

关于自定义view也写了好几篇了,你该问了为什么还写,是不是有点没完没了,哈哈。。。没办法,在用别人的app时遇到耳目一新的功能都想去尝试模仿一下。套路还是和之前的差不多,这里我就不详细的介绍了,只介绍自定义类中的内容,不明白的可以去看我之前的博客。

自定义属性
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="CustomCircleView">
        <attr name="default_circle_stroke_color" format="color"/>
        <attr name="reached_ring_color" format="color"/>
        <attr name="default_circle_stroke_width" format="dimension"/>
        <attr name="reached_ring_stroke_width" format="dimension"/>
        <attr name="text_color" format="color"/>
        <attr name="text_size" format="dimension"/>
        <attr name="radius" format="dimension"/>
    </declare-styleable>
</resources>
由于代码中注释的已经很详细了,这里我就不再啰嗦了,直接上代码
public class CustomCircleView extends View {
    //常量设置
    private static final int DEFAULT_CIRCLE_STROKE_COLOR = Color.parseColor("#DEDEDE");
    private static final int REACHED_RING_STROKE_WIDTH = 6;
    private static final int DEFAULT_CIRCLE_STROKE_WIDTH = 6;
    private static final int TEXT_COLOR = Color.parseColor("#000000");
    private static final int TEXT_SIZE = 60;
    private static final int RADIUS = 100;

    private int defaultColor = DEFAULT_CIRCLE_STROKE_COLOR;//默认圆边框颜色
    private int defaultWidth = dp2px(DEFAULT_CIRCLE_STROKE_WIDTH);//默认圆的边框宽度
    private int reachedRingWidth = dp2px(REACHED_RING_STROKE_WIDTH);//进度圆环的宽度
    private int textColor = TEXT_COLOR;
    private int textSize = dp2px(TEXT_SIZE);
    private int circleRadius = dp2px(RADIUS);

    private Paint defaultCirclePaint;
    private Paint reachedRingPaint;
    private Paint textPaint;
    private Paint textPaint2;


    private int ringCount = 25;//圆环的块数
    private int ringDistances = 3;//圆环之间的间隔距离
    private float everyRingAngle;//每段圆环对应的角度

    private int reachedRingCount = 0;
    private int dataTextCount;
    private String text;
    //渐变的颜色值数组
    private int[] colorArray = new int[]{Color.parseColor("#FF9900"),Color.parseColor("#FFFF00"),
            Color.parseColor("#66FF00")};
    //初始化
    private Matrix matrix = new Matrix();



    public CustomCircleView(Context context) {
        this(context,null);
    }
    public CustomCircleView(Context context, AttributeSet attrs) {
        this(context, attrs,0);
    }
    public CustomCircleView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        //获取自定义属性
        TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.CustomCircleView);
        int indexCount = typedArray.getIndexCount();
        for(int i=0;i<indexCount;i++){
            int attr = typedArray.getIndex(i);
            switch (attr){
                case R.styleable.CustomCircleView_default_circle_stroke_color:
                    defaultColor = typedArray.getColor(attr, defaultColor);
                    break;
                case R.styleable.CustomCircleView_default_circle_stroke_width:
                    defaultWidth = (int) typedArray.getDimension(attr, defaultWidth);
                    break;
                case R.styleable.CustomCircleView_reached_ring_stroke_width:
                    reachedRingWidth = (int) typedArray.getDimension(attr, reachedRingWidth);
                    break;
                case R.styleable.CustomCircleView_text_color:
                    textColor = typedArray.getColor(attr, textColor);
                    break;
                case R.styleable.CustomCircleView_text_size:
                    textSize = (int) typedArray.getDimension(attr, textSize);
                    break;
                case R.styleable.CustomCircleView_radius:
                    circleRadius = (int) typedArray.getDimension(attr, circleRadius);
                    break;
            }
        }
        typedArray.recycle();//回收
        //设置画笔
        setPaint();
    }

    private void setPaint() {
        defaultCirclePaint = new Paint();
        defaultCirclePaint.setAntiAlias(true);//抗锯齿
        defaultCirclePaint.setDither(true);//防抖动
        defaultCirclePaint.setColor(defaultColor);
        defaultCirclePaint.setStyle(Paint.Style.STROKE);
        defaultCirclePaint.setStrokeWidth(defaultWidth);

        reachedRingPaint = new Paint();
        reachedRingPaint.setAntiAlias(true);
        reachedRingPaint.setDither(true);
        reachedRingPaint.setStyle(Paint.Style.STROKE);
        reachedRingPaint.setStrokeWidth(reachedRingWidth);

        textPaint = new Paint();
        textPaint.setAntiAlias(true);
        textPaint.setDither(true);
        textPaint.setStyle(Paint.Style.FILL);
        textPaint.setTextSize(textSize);
        textPaint.setColor(textColor);

        textPaint2 = new Paint();
        textPaint2.setAntiAlias(true);
        textPaint2.setDither(true);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int widthSize;
        int heightSize;
        int paintWidth = Math.max(defaultWidth,reachedRingWidth);//画笔宽度,二者stroke的最大值
        if(widthMode != MeasureSpec.EXACTLY){
            widthSize = getPaddingLeft() + paintWidth + circleRadius*2 + getPaddingRight();
            widthMeasureSpec = MeasureSpec.makeMeasureSpec(widthSize,MeasureSpec.EXACTLY);
        }
        if(heightMode != MeasureSpec.EXACTLY){
            heightSize = getPaddingTop() + getPaddingBottom() + paintWidth + circleRadius*2;
            heightMeasureSpec = MeasureSpec.makeMeasureSpec(heightSize, MeasureSpec.EXACTLY);
        }

        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.save();
        canvas.translate(getPaddingLeft(),getPaddingTop());
        //每一段圆环对应的角度
        everyRingAngle = (360*1.0f - ringCount*ringDistances) / ringCount;
        for(int i=0;i<ringCount;i++){
            //绘制默认分段圆弧,习惯性都是从-90度的方向开始,所以这里-90
            canvas.drawArc(new RectF(0,0,circleRadius*2,circleRadius*2),i*(everyRingAngle+ringDistances) - 90, everyRingAngle,false,defaultCirclePaint);
        }
        /**
         *  SweepGradient(float cx, float cy,int colors[], float positions[]);
         *  cx  渲染中心点x 坐标
         *  cy  渲染中心y 点坐标
         *  colors  围绕中心渲染的颜色数组,至少要有两种颜色值
         *  positions   相对位置的颜色数组,可为null, 若为null,颜色沿渐变线均匀分布
         */
        SweepGradient sweepGradient = new SweepGradient(circleRadius,circleRadius,colorArray,null);
        reachedRingPaint.setShader(sweepGradient);//给图像着色,SweepGradient是Shader的子类
        matrix.setRotate(-90,circleRadius,circleRadius);//需设置旋转角度,不然的话颜色值数组中开始颜色和结束颜色值是相反的
        sweepGradient.setLocalMatrix(matrix);
 //       reachedRingPaint.setShadowLayer(10,10,10,Color.BLUE);//设置底层阴影
        //注意: 这个方法不支持硬件加速,所以我们要测试时必须先关闭硬件加速。
        //加上这一句 setLayerType(LAYER_TYPE_SOFTWARE, null);
 //       setLayerType(LAYER_TYPE_SOFTWARE,reachedRingPaint);
        /**
         * setShadowLayer(float radius, float dx, float dy, int shadowColor);
         * radius表示阴影的扩散半径;dx和dy表示阴影平面x、y上的偏移值;shadowColor阴影颜色。
         */
        //画颜色渐变的进度圆弧
        for(int i = 0;i<reachedRingCount;i++){
            canvas.drawArc(new RectF(0,0,circleRadius*2,circleRadius*2),i*(everyRingAngle+ringDistances) - 90, everyRingAngle,false,reachedRingPaint);
        }

        //画中间文字进度
        dataTextCount = reachedRingCount*4;
        text = dataTextCount + "%";
        float textWidth = textPaint.measureText(text);
        float textHeight = (textPaint.descent() + textPaint.ascent()) / 2;
        canvas.drawText(text, circleRadius - textWidth / 2, circleRadius - textHeight, textPaint);

        //画顶部文字说明
        String textExplain = "已学课时";
        textPaint2.setColor(Color.parseColor("#275D9D"));
        textPaint2.setTextSize(dp2px(20));
        float textWidth2 = textPaint2.measureText(textExplain);
        float textHeight2 = (textPaint2.descent() + textPaint2.ascent()) / 2;
        canvas.drawText(textExplain,circleRadius - textWidth2 / 2, circleRadius - textHeight2 - dp2px(50), textPaint2);

        canvas.restore();
    }


    private int yDown;//按下时y坐标
    private int yUp;//抬起时y的坐标
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                yDown = (int) event.getY();
                break;
            case MotionEvent.ACTION_UP:
                yUp = (int) event.getY();
                if(yDown > yUp && reachedRingCount < ringCount){//表示上滑
                    up();
                }else if(yDown < yUp && reachedRingCount > 0){
                    down();
                }
                break;
        }
        return true;
    }

    private void down(){
        reachedRingCount--;
        invalidate();
    }

    private void up(){
        reachedRingCount++;
        invalidate();
    }

    public void startAnima(int mValue){
        ValueAnimator animator = ValueAnimator.ofInt(0, mValue / 4);
        animator.setDuration(3000);
        animator.setInterpolator(new LinearInterpolator());
        animator.setRepeatCount(0);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                reachedRingCount = (int) animation.getAnimatedValue();
                invalidate();
            }
        });
        animator.start();//开启动画

    }

    public void setmValue(int data){
        this.dataTextCount = data;
    }
    /**
     * dp 2 px
     *
     * @param dpVal
     */
    protected int dp2px(int dpVal)
    {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
                dpVal, getResources().getDisplayMetrics());
    }

    /**
     * sp 2 px
     *
     * @param spVal
     * @return
     */
    protected int sp2px(int spVal)
    {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
                spVal, getResources().getDisplayMetrics());

    }

}
最后是在MainActivity中去调用我们的自定义控件
public class MainActivity extends AppCompatActivity {

    private CustomCircleView customCircleView;
    private Button btnStart;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        customCircleView = (CustomCircleView) findViewById(R.id.customView);
        btnStart = (Button) findViewById(R.id.btn_start);
        btnStart.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                customCircleView.startAnima(100);
            }
        });
    }
}

有需要的童鞋直接拷贝代码根据自己的需求稍微改改就能用,如有疑问欢迎留言,大家一块探讨,点击源码下载

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值