自定義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);
            }
        });
    }
}

有需要的童鞋直接拷貝代碼根據自己的需求稍微改改就能用,如有疑問歡迎留言,大家一塊探討,點擊源碼下載

發佈了29 篇原創文章 · 獲贊 60 · 訪問量 10萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章