自定义ArcView-构造拓展性高的view

这段时间用做一个飞行统计的模块,其中要做一个弧形展示效果,如图



做这个东西时,我们首先想到的是有没有现成的。于是就去github上找,找到了这个--https://github.com/lzyzsd/CircleProgress,大家有兴趣也可以去研究研究。本文参考的是其中的ArcProgress。其原理就是画弧和画字。。原理图:



先说画弧:

关键代码为: canvas.drawArc(rectF, startAngle, entityAngle, false, paint);
其中rectF为最外面的矩阵,startAngle为绘弧的开始角度,entityAngle为要画弧的最大值,false为不包含圆心,paint为画笔。

其次画字:

float bottomTextBaseline = getMeasuredHeight() - textPaint.descent(); 底部文字的基线
 canvas.drawText(bottomText, (getMeasuredWidth() - textPaint.measureText(bottomText)) / 2.0f, bottomTextBaseline, textPaint);

第一个参数为要画的文字,第二个参数为画字的起始x坐标,第三个为y坐标,第四个参数为文字画笔。

画字时的规则:参考http://blog.csdn.net/tianjf0514/article/details/7642656



接下来开始自定义我们的ArcView了,首先自定义属性,这个没什么可说的:

public ArcView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypedArray ta = context.obtainStyledAttributes(attrs,R.styleable.Arc);
        this.radius = ta.getDimensionPixelSize(R.styleable.Arc_radius,0);
        this.entityAngle = ta.getInt(R.styleable.Arc_entity_angle, DEFAULT_ENTITY_ANGLE);
        this.initPercent = ta.getInt(R.styleable.Arc_init_percent,0);   //初始的百分值

        init(context);
    }

关键是测量我们的view的大小。一般来说,我们画的东西要在这个view可见范围内的中心。在onMeasure()方法中要计算出rectF(弧形的矩阵)。


上代码:

<pre name="code" class="java">protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        Log.e("arcView","onMeasure");
        //整个内容的宽度
        int contentWidth = (int) (radius * 2 + out_stroke_width);

        //下面计算内容的高度
        float halfTemp = out_stroke_width / 2;
        float angle = (360 - entityAngle) / 2f;
        arcBottom2CenterHeight = (float) (radius * Math.cos(angle / 180 * Math.PI));
        int contentHeight = (int) (radius + arcBottom2CenterHeight + textPaint.descent() + out_stroke_width);

        setMeasuredDimension(resolveSize(contentWidth,widthMeasureSpec),resolveSize(contentHeight,heightMeasureSpec));

        float contentLeft,contentTop,contentRight,contentBottom;    //矩阵的左右上底


        if(getMeasuredWidth() == contentWidth){
            contentLeft = halfTemp;
            contentRight = contentWidth - halfTemp;
        }else{
            contentLeft = (getMeasuredWidth() - contentWidth) / 2 + halfTemp;
            contentRight = contentLeft + contentWidth - 2 * halfTemp;
        }

        if(getMeasuredHeight() == contentHeight){
            contentTop = halfTemp;
            contentBottom = contentWidth - halfTemp;
        }else{
            contentTop = (getMeasuredHeight() - contentHeight) / 2 + halfTemp;
            contentBottom = contentTop + contentWidth - 2 * halfTemp;
        }

        rectF = new RectF(contentLeft,contentTop,contentRight,contentBottom);
    }


 

接下来是ondraw方法:

protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        Log.e("arcView","draw"); //锁屏解锁后会再次调用ondraw()方法
        canvas.setDrawFilter(new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG|Paint.FILTER_BITMAP_FLAG));
        paint.setStrokeWidth(default_stroke_width);

        paint.setColor(unfinishedStrokeColor);
        canvas.drawArc(rectF, startAngle, entityAngle, false, paint);
        paint.setStrokeWidth(out_stroke_width);
        paint.setColor(finishedStrokeColor);
        canvas.drawArc(rectF, startAngle, initSweepAngle, false, paint);

        //画下面文字
        textPaint.setTextSize(default_gray_text_size);
        textPaint.setColor(default_gray_text_color);
        float bottomTextBaseline = getMeasuredHeight() - textPaint.descent();
        canvas.drawText(bottomText, (getMeasuredWidth() - textPaint.measureText(bottomText)) / 2.0f, bottomTextBaseline, textPaint);

        //画上面文字
        float topTextBaseline = radius / 2 + out_stroke_width / 2;
        canvas.drawText(topText,(getMeasuredWidth() - textPaint.measureText(topText)) / 2.0f,topTextBaseline,textPaint);

        //画中心文字
        textPaint.setTextSize(default_percent_text_size);
        textPaint.setColor(default_percent_text_color);
        float centerTextBaseline = getMeasuredHeight() / 2 - textPaint.ascent() / 2;
        canvas.drawText(centerText,(getMeasuredWidth() - textPaint.measureText(centerText)) / 2.0f,centerTextBaseline,textPaint);

    }

手机锁屏后再亮屏,view的ondraw方法会调用,从下一个activity返回也会调用,结果是view可见是会调用ondraw()方法。

最后重写onSaveInstanceState和onRestoreInstanceState来保存和恢复数据

protected Parcelable onSaveInstanceState() {
        Log.e("arcView","onSaveInstanceState");
        Bundle bundle = new Bundle();
        bundle.putParcelable(INSTANCE_STATE, super.onSaveInstanceState());
        bundle.putFloat(INSTANCE_PROGRESS, initSweepAngle);
        return bundle;
    }

    @Override
    protected void onRestoreInstanceState(Parcelable state) {
        Log.e("arcView","onRestoreInstanceState");
        if(state instanceof Bundle) {
            Bundle bundle = (Bundle) state;
            initSweepAngle = bundle.getFloat(INSTANCE_PROGRESS,0);
            super.onRestoreInstanceState(bundle.getParcelable(INSTANCE_STATE));
            return;
        }
        super.onRestoreInstanceState(state);
    }

我们有可能显示百分数时需要一个animate过程,上代码:

/*第一中方式实现animatePercent
    private OverScroller scroller;
    public void animatePercent(int percent){
        if(scroller.isFinished()) {
            scroller.startScroll(0, 0, percent, 0, 2000);
//        scroller.fling(0,0,500,0,0,100,0,0);
            invalidate();
        }
    }

    @Override
    public void computeScroll() {
        if(scroller.computeScrollOffset()){
            int tempPercent = scroller.getCurrX();
            setPercent(tempPercent);
        }
        super.computeScroll();
    }
    */

    private final int ANIMATE_DURATION = 1000;
    private static final int SIXTY_FPS_INTERVAL = 1000 / 60;
    private long mStartTime;
    static final android.view.animation.Interpolator sInterpolator = new OvershootInterpolator();
    public void animatePercent(final int percent){
        mStartTime = System.currentTimeMillis();
        this.post(new Runnable() {
            @Override
            public void run() {
                float t = interpolate();
                float currentPercent = 0 + t * percent;
                setPercent(currentPercent);
                if(t < 1){
                    ArcView.this.postDelayed(this,SIXTY_FPS_INTERVAL);
                }
            }
        });
    }

    private float interpolate() {
        float t = 1f * (System.currentTimeMillis() - mStartTime) / ANIMATE_DURATION;
        t = Math.min(1f,t);
        return sInterpolator.getInterpolation(t);
    }
第一种方式在结束时有点卡顿的现象,不知道你们的手机有没有。我推荐第二种,当然了,我们也可以用属性动画来实现。

最后是效果图



源码:

http://yunpan.cn/cdgSG2ii4CzKh (提取码:560b)





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值