自定義view-仿支付寶芝麻分圓盤

先上效果圖
這裏寫圖片描述

下面上代碼
1.先定義屬性

<resources>
    <declare-styleable name="RoundIndicatorView">
        <!--圓盤最大值-->
        <attr name="maxNum" format="dimension|integer"/>
        <!--圓盤起始角度-->
        <attr name="startAngle" format="dimension|integer"/>
        <!--圓盤掃過的角度-->
        <attr name="sweepAngle" format="dimension|integer"/>
    </declare-styleable>
</resources>

該view中用到的變量

    private int radius;//內圓半徑
    private int mWidth;//控件的寬度
    private int mHeight;//控件的高度
    private Paint paint_1;//內圓畫筆
    private Paint paint_2;
    private Paint paint_3;
    private Paint paint_4;
    private Paint paint_5;
    private Context context;
    private int maxNum;//圓盤最大值
    private int startAngle;//圓盤起始角度
    private int sweepAngle;//圓盤掃過的角度
    private int sweepInWidth;//內圓弧寬度
    private int sweepOutWidth;//外圓寬度
    private String[] text ={"較差","中等","良好","優秀","極好"};
    private int[] indicatorColor = {0xffffffff,0x00ffffff,0x99ffffff,0xffffffff};
    private int currentNum = 0;

初始化自定義的屬性及畫筆

    /**
     * 初始化自定義屬性
     * @param attrs
     */
    private void initAttrs(AttributeSet attrs){
        TypedArray typedArray = context.obtainStyledAttributes(attrs,R.styleable.RoundIndicatorView);
        maxNum = typedArray.getInt(R.styleable.RoundIndicatorView_maxNum,500);
        startAngle = typedArray.getInt(R.styleable.RoundIndicatorView_startAngle,160);
        sweepAngle = typedArray.getInt(R.styleable.RoundIndicatorView_sweepAngle,220);
        sweepInWidth = dp2px(8);
        sweepOutWidth = dp2px(3);
        typedArray.recycle();
    }

    /**
     * 初始化所有畫筆
     */
    private void initPaint(){
        paint_1 = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint_1.setDither(true);
        paint_1.setStyle(Paint.Style.STROKE);
        paint_1.setColor(0xffffffff);
        paint_2 = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint_3 = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint_4 = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint_5 = new Paint(Paint.ANTI_ALIAS_FLAG);

    }

重寫onmeasure及ondraw

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int wSize = MeasureSpec.getSize(widthMeasureSpec);
        int wMode = MeasureSpec.getMode(widthMeasureSpec);
        int hSize = MeasureSpec.getSize(heightMeasureSpec);
        int hMode = MeasureSpec.getMode(heightMeasureSpec);

        if (wMode == MeasureSpec.EXACTLY ){
            mWidth = wSize;
        }else {
            mWidth = dp2px(300);
        }
        if (hMode == MeasureSpec.EXACTLY){
            mHeight = hSize;
        }else {
            mHeight = dp2px(400);
        }
        setMeasuredDimension(mWidth,mHeight);
    }
    /**
     * 注意圓的半徑不要在構造方法裏就去設置,那時候是得不到view的寬高值的,所以我在onDraw方法裏設置半徑,默認就view寬度的1/4吧。把原點移到view的中心去:
     * @param canvas
     */
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        radius = getMeasuredWidth()/4;//不要在構造方法裏初始化,那時還沒測量寬高
        canvas.save();
        canvas.translate(mWidth/2,mWidth/2);

        drawRound(canvas);//畫圓盤
        drawScale(canvas);//畫刻度及文字
        drawIndicator(canvas);//畫芝麻分指示針
        drawCenterText(canvas);//畫中間的文字

        canvas.restore();
    }

畫內外兩個圓

    /**
     * 畫內外圓
     * @param canvas
     */
    private void drawRound(Canvas canvas){
        canvas.save();
        //內圓
        paint_1.setAlpha(0x40);
        paint_1.setStrokeWidth(sweepInWidth);
        RectF rectF = new RectF(-radius,-radius,radius,radius);
        canvas.drawArc(rectF,startAngle,sweepAngle,false,paint_1);
        //外圓
        paint_1.setStrokeWidth(sweepOutWidth);
        int w = dp2px(10);
        RectF rectF1 = new RectF(-radius-w,-radius-w,radius+w,radius+w);
        canvas.drawArc(rectF1,startAngle,sweepAngle,false,paint_1);
        canvas.restore();
    }

畫刻度以及刻度值

    /**
     * 畫刻度及刻度值
     * @param canvas
     */
    private void drawScale(Canvas canvas){
        canvas.save();
        float angle = sweepAngle/30;//刻度間隔
        canvas.rotate(-270 + startAngle);//將起始的刻度點旋轉到正上方
        for (int i = 0; i <= 30; i++) {
            if (i%6 == 0){//畫粗刻度和刻度值
                paint_1.setStrokeWidth(dp2px(2));
                paint_1.setAlpha(0x70);
                canvas.drawLine(0,-radius-sweepInWidth/2,0,-radius+sweepInWidth/2+dp2px(1),paint_1);
                drawText(canvas,i*maxNum/30+"",paint_1);
            }else {//畫西刻度
                paint_1.setStrokeWidth(dp2px(1));
                paint_1.setAlpha(0x55);
                canvas.drawLine(0,-radius-sweepInWidth/2,0,-radius+sweepInWidth/2,paint_1);
            }
            if (i==3 || i==9 || i==15 || i==21 || i==27){
                paint_1.setStrokeWidth(dp2px(2));
                paint_1.setAlpha(0x55);
                drawText(canvas,text[0],paint_1);
            }
            canvas.rotate(angle);//逆時針
        }
        canvas.restore();
    }
   private void drawText(Canvas canvas,String text,Paint paint){
        paint.setStyle(Paint.Style.FILL);
        paint.setTextSize(sp2px(8));
        float width = paint.measureText(text);//相比getTextBounds來說,這個方法獲得的類型是float,更精確些
        // Rect rect = new Rect();
        //paint.getTextBounds(text,0,text.length(),rect);
        canvas.drawText(text,-width/2,-radius+dp2px(15),paint);
        paint.setStyle(Paint.Style.STROKE);
    }

畫芝麻分的指示針

    /**
     * 畫芝麻分指示針
     * @param c
     */
    private void drawIndicator(Canvas c){
        c.save();
        paint_2.setStyle(Paint.Style.STROKE);
        int sweep;
        if (currentNum <=maxNum){
//            sweep = currentNum/maxNum*sweepAngle;
            sweep = (int)((float)currentNum/(float)maxNum*sweepAngle);
        }else {
            sweep = sweepAngle;
        }
        paint_2.setStrokeWidth(sweepOutWidth);
        Shader shader = new SweepGradient(0,0,indicatorColor,null);
        paint_2.setShader(shader);
        int w = dp2px(10);
        RectF rectF = new RectF(-radius-w,-radius-w,radius+w,radius+w);
        c.drawArc(rectF,startAngle,sweep,false,paint_2);
        float x = (float) ((radius+dp2px(10))*Math.cos(Math.toRadians(startAngle+sweep)));
        float y = (float) ((radius+dp2px(10))*Math.sin(Math.toRadians(startAngle+sweep)));
        paint_3.setStyle(Paint.Style.FILL);
        paint_3.setColor(0xffffffff);
        paint_3.setMaskFilter(new BlurMaskFilter(dp2px(3), BlurMaskFilter.Blur.SOLID));//需關閉硬件加速
        c.drawCircle(x,y,dp2px(3),paint_3);
        c.restore();
    }

畫中間的文字

    private void drawCenterText(Canvas canvas){
        canvas.save();
        paint_4.setStyle(Paint.Style.FILL);
        paint_4.setTextSize(radius/2);
        paint_4.setColor(0xffffffff);
        canvas.drawText(currentNum+"",-paint_4.measureText(currentNum+"")/2,0,paint_4);

        paint_4.setTextSize(radius/4);
        String content = "信用";
        if (currentNum<maxNum/5){
            content += text[0];
        }else if (currentNum>=maxNum/5 && currentNum <maxNum*2/5){
            content += text[1];
        }else if (currentNum>=maxNum*2/5 && currentNum < maxNum*3/5){
            content += text[2];
        }else if (currentNum>= maxNum*3/5 && currentNum < maxNum*4/5){
            content += text[3];
        }else if (currentNum>=maxNum*4/5){
            content += text[4];
        }
        Rect rect = new Rect();
        paint_4.getTextBounds(content,0,content.length(),rect);
        canvas.drawText(content,-rect.width()/2,rect.height()+20,paint_4);
        canvas.restore();
    }

寫一個設置及獲取當前值的方法以及改變值後的動畫

    //獲取當前值
    public int getCurrentNum() {
        return currentNum;
    }

    //設置當前值
    public void setCurrentNum(int currentNum) {
        this.currentNum = currentNum;
        invalidate();
    }
    //設置改變值後的動畫
    public void setCurrentNumAnim(int num) {
        float duration = (float)Math.abs(num-currentNum)/maxNum *1500+500; //根據進度差計算動畫時間
        ObjectAnimator anim = ObjectAnimator.ofInt(this,"currentNum",num);
        anim.setDuration((long) Math.min(duration,2000));
        anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                int value = (int) animation.getAnimatedValue();
                int color = calculateColor(value);
                setBackgroundColor(color);
            }
        });
        anim.start();
    }
    private int calculateColor(int value){
        ArgbEvaluator evealuator = new ArgbEvaluator();
        float fraction = 0;
        int color = 0;
        if(value <= maxNum/2){
            fraction = (float)value/(maxNum/2);
            color = (int) evealuator.evaluate(fraction,0xFFFF6347,0xFFFF8C00); //由紅到橙
        }else {
            fraction = ( (float)value-maxNum/2 ) / (maxNum/2);
            color = (int) evealuator.evaluate(fraction,0xFFFF8C00,0xFF00CED1); //由橙到藍
        }
        return color;
    }

該自定義view中用到的方法

    //該view用到的工具類
    protected int dp2px(int dp){
        return (int) TypedValue.applyDimension(
                TypedValue.COMPLEX_UNIT_DIP,
                dp,
                getResources().getDisplayMetrics()
        );
    }
    protected int sp2px(int sp){
        return (int) TypedValue.applyDimension(
                TypedValue.COMPLEX_UNIT_SP,
                sp,
                getResources().getDisplayMetrics()
        );
    }
    public static DisplayMetrics getScreenMetrics(Context context){
        WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        DisplayMetrics dm = new DisplayMetrics();
        wm.getDefaultDisplay().getMetrics(dm);
        return dm;
    }

源碼下載地址

http://download.csdn.net/detail/zhou_anzhuojinjie/9738937
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章