这段时间用做一个飞行统计的模块,其中要做一个弧形展示效果,如图
做这个东西时,我们首先想到的是有没有现成的。于是就去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);
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)