一、概述
今天要讲的效果是类似qq计步器的效果,先看下效果图:
二、思路分析
看图说话,图中有两个圆弧,一个背景圆弧,一个能动态变化的圆弧,中间有记录步数的文字。三个对象,每个对象有各自的属性。然后就是设置各自对象的大小和各自对象的绘制了。
2.1自定义属性
attrs文件中的代码:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="QQStepView">
<!--画笔颜色-->
<attr name="outColor" format="color"></attr>
<attr name="innerColor" format="color"></attr>
<!--画笔的宽度大小-->
<attr name="boderWidth" format="dimension"></attr>
<!--文字的颜色和大小-->
<attr name="stepColor" format="color"></attr>
<attr name="stepSeize" format="dimension"></attr>
</declare-styleable>
</resources>
定义了背景圆弧画笔和前景圆弧画笔的颜色及宽度,还有中间文字的颜色和尺寸打小,在构造函数中初始化。
2.2构造函数代码
TypedArray a = context.obtainStyledAttributes(attrs,R.styleable.QQStepView);
mOutColor = a.getColor(R.styleable.QQStepView_outColor,mOutColor);
mInnerColor = a.getColor(R.styleable.QQStepView_innerColor,mInnerColor);
mStepTextSize = a.getDimensionPixelSize(R.styleable.QQStepView_stepSeize,sp2px(mStepTextSize));
mBorderWidth = (int)a.getDimension(R.styleable.QQStepView_boderWidth,mBorderWidth);
mStepTextColor = a.getColor(R.styleable.QQStepView_stepColor,mStepTextColor);
a.recycle();
mOutPaint = new Paint();
mOutPaint.setAntiAlias(true); //抗锯齿
mOutPaint.setColor(mOutColor);
mOutPaint.setStrokeWidth(mBorderWidth);
mOutPaint.setStyle(Paint.Style.STROKE);
mInnerPaint = new Paint();
mInnerPaint.setAntiAlias(true); //抗锯齿
mInnerPaint.setColor(mInnerColor);
mInnerPaint.setStrokeWidth(mBorderWidth);
mInnerPaint.setStyle(Paint.Style.STROKE);
mTextPaint = new Paint();
mTextPaint.setColor(mStepTextColor);
mTextPaint.setTextSize(mStepTextSize);
构造函数没啥好讲的,都是套路
2.3onMeasure方法设置控件宽高
//onMeasure()
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//宽高不一致 取最小值 确保是一个正方形
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
setMeasuredDimension(width>height?height:width,width>height?height:width);
}
2.4onDraw方法中对几个对象进行绘制
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//画外圆弧 ,内圆弧 ,文字
int center = getWidth()/2;
int radius = center - mBorderWidth/2; //外切圆压住描边的中间
//指定圆弧的外轮廓矩形区域。比较易懂
RectF rectF = new RectF(center - radius, center - radius, center
+ radius, center + radius);
canvas.drawArc(rectF,135,270,false,mOutPaint);
if (mStepMax==0){
return;
}
//这里要强制转换为float型的
float sweepAngle = (float) mCurStep/mStepMax;
canvas.drawArc(rectF,135,sweepAngle*270,false,mInnerPaint);
String stepText = mCurStep+"";
//画文字
Rect bunds = new Rect();
mTextPaint.getTextBounds(stepText,0,stepText.length(),bunds);
int dy = (mTextPaint.getFontMetricsInt().bottom - mTextPaint.getFontMetricsInt().top)/2+mTextPaint.getFontMetricsInt().bottom;
int baseLine = getHeight()/2+dy;
canvas.drawText(stepText,getWidth()/2-bunds.width()/2,baseLine,mTextPaint);
}
这里对rectF的赋值不太懂的,可以参考这个链接:
关于canvas.drawArc,canvas.drawOval 和RectF 的关系
2.5其他方法
// 写几个方法动起来
public synchronized void setStepMax(int stepMax){
this.mStepMax = stepMax;
}
public synchronized void setCurrentStep(int currentStep){
this.mCurStep = currentStep;
// 不断绘制 onDraw()
invalidate();
}
解释一下,一个是设置你最大运动的步数,一个是设置你当前绘制到的那个步数,在此方法中调用invalidate方法,会不断调用onDraw方法进行绘制,这两个方法暴露给外部调用动态改变。
2.6MainActivity的实现
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final QQStepView qqStepView = (QQStepView) findViewById(R.id.step_view);
qqStepView.setStepMax(4000);
// 属性动画
ValueAnimator valueAnimator = ObjectAnimator.ofFloat(0, 3000);
valueAnimator.setDuration(1000);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float currentStep = (float) animation.getAnimatedValue();
qqStepView.setCurrentStep((int)currentStep);
}
});
valueAnimator.start();
}
用属性动画实现动画效果
三、结语
分析完毕,最后感谢,darren大神,具有无私分享精神,带着一起写效果,看源码。
附上代码地址:http://pan.baidu.com/s/1qXGO4w8