最近在跟大牛在學(xué)習(xí)自定義view的時(shí)候,跟著他們寫了一個(gè)步數(shù)計(jì)數(shù),效果如下:
效果實(shí)現(xiàn)的大致一個(gè)思路:
1:寫一個(gè)類繼承自view
2:自定義一些屬性,這樣可以在布局文件中直接使用
3:在onMeasure方法中進(jìn)行測量
4:在onDraw方法中進(jìn)行繪制
5:提供參數(shù)設(shè)置的方法
在繼承view的時(shí)候需要實(shí)現(xiàn)一些構(gòu)造方法:
public QQStepView(Context context) {
this(context, null);
}
public QQStepView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public QQStepView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initAttrs(context, attrs);
initPaint();
}
這里說下這三個(gè)構(gòu)造方法會(huì)在什么情況下調(diào)用,第一構(gòu)造方法會(huì)在java文件中直接new的時(shí)候調(diào)用,第二個(gè)構(gòu)造方法是在布局文件中使用的時(shí)候調(diào)用,第三個(gè)構(gòu)造方法是在布局文件中使用并設(shè)有style樣式的時(shí)候調(diào)用,將第一個(gè)和第二個(gè)改為this,這樣不管在什么情況下使用都會(huì)統(tǒng)一調(diào)用第三個(gè)構(gòu)造方法,接下來在第三個(gè)構(gòu)造方法中初始化自定義屬性和畫筆;
初始化自定義屬性:
private void initAttrs(Context context, AttributeSet attrs) {
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.QQStepView);
mOuterColor = array.getColor(R.styleable.QQStepView_outerColor, mOuterColor);
mInnerColor = array.getColor(R.styleable.QQStepView_innerColor, mInnerColor);
mStepTextColor = array.getColor(R.styleable.QQStepView_stepTextColor, mStepTextColor);
mBorderWidth = (int) array.getDimension(R.styleable.QQStepView_borderWidth, mBorderWidth);
mStepTextSize = array.getDimensionPixelSize(R.styleable.QQStepView_stepTextSize, mStepTextSize);
//回收
array.recycle();
}
在初始化屬性后要記得回收;
初始化畫筆:
private void initPaint(){
outPaint = new Paint();
//抗鋸齒
outPaint.setAntiAlias(true);
outPaint.setStrokeWidth(mBorderWidth);
outPaint.setColor(mOuterColor);
//畫筆空心
outPaint.setStyle(Paint.Style.STROKE);
outPaint.setStrokeCap(Paint.Cap.ROUND);
innerPaint = new Paint();
innerPaint.setAntiAlias(true);
innerPaint.setStrokeWidth(mBorderWidth);
innerPaint.setColor(mInnerColor);
//畫筆空心
innerPaint.setStyle(Paint.Style.STROKE);
innerPaint.setStrokeCap(Paint.Cap.ROUND);
mTextPaint = new Paint();
mTextPaint.setAntiAlias(true);
mTextPaint.setColor(mStepTextColor);
mTextPaint.setTextSize(mStepTextSize);
}
接著就是在onMeasure中進(jìn)行測量了:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//獲取寬高模式模式
int modeWidth = MeasureSpec.getMode(widthMeasureSpec);
int modeHeight = MeasureSpec.getMode(heightMeasureSpec);
//獲取寬高值
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
//判斷模式是否是wrap_content
if (modeWidth == MeasureSpec.AT_MOST) {
width = dp2px(80);
}
if (modeHeight == MeasureSpec.AT_MOST) {
height = dp2px(80);
}
//寬度和高度不一致的時(shí)候取最小值
width = width > height ? height : width;
height = width > height ? height : width;
setMeasuredDimension(width, height);
}
在這里的做了一個(gè)寬高模式的判斷,如果是MeasureSpec.AT_MOST也就是wrap_content的時(shí)候就將寬和高設(shè)置為固定大小,同時(shí)如果寬高設(shè)置的不一致的時(shí)候,取其中的最小值進(jìn)行測量繪制;
現(xiàn)在可以在onDraw方法中進(jìn)行繪制了:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//繪制外弧 描邊問題
RectF rectf = new RectF(mBorderWidth / 2, mBorderWidth / 2, getWidth() - mBorderWidth / 2, getHeight() - mBorderWidth / 2);
canvas.drawArc(rectf, 135, 270, false, outPaint);
//繪制內(nèi)弧
if (mStepMax == 0) {
return;
}
float swweepAngle = (float) mCurrentStep / mStepMax;
canvas.drawArc(rectf, 135, swweepAngle * 270, false, innerPaint);
//繪制文字
String stepText = mCurrentStep + "";
Rect rect = new Rect();
mTextPaint.getTextBounds(stepText, 0, stepText.length(), rect);
int dx = getWidth() / 2 - rect.width() / 2;
//基線
Paint.FontMetricsInt metricsInt = mTextPaint.getFontMetricsInt();
int dy = (metricsInt.bottom - metricsInt.top) / 2 - metricsInt.bottom;
int baseLine = getHeight() / 2 + dy;
canvas.drawText(stepText, dx, baseLine, mTextPaint);
}
因?yàn)樵诓季治募袝r(shí)候的時(shí)候就會(huì)調(diào)用onDraw方法但是這個(gè)時(shí)候還沒有設(shè)置mStepMax 值還為0,在計(jì)算
float swweepAngle = (float) mCurrentStep / mStepMax;
就會(huì)出錯(cuò),所以做了下判斷,下面還提供了兩個(gè)方法;
設(shè)置最大步數(shù):
public synchronized void setStepMax(int stepMax){
this.mStepMax=stepMax;
}
設(shè)置當(dāng)前步數(shù):
public synchronized void setCurrentStep(int currentStep){
this.mCurrentStep=currentStep;
//不斷的去繪制
invalidate();
}
在設(shè)置當(dāng)前步數(shù)里面調(diào)用invalidate方法就會(huì)不停的調(diào)用onDraw方法進(jìn)行繪制,到這里效果就實(shí)現(xiàn)的差不多了,在java文件中調(diào)用時(shí),設(shè)置一些屬性動(dòng)畫就可以了;
final QQStepView stepView = (QQStepView) findViewById(R.id.stepView);
stepView.setStepMax(10000);
//屬性動(dòng)畫
ValueAnimator valueAnimator = ObjectAnimator.ofFloat(0, 6123);
valueAnimator.setDuration(2000);
//插值器
valueAnimator.setInterpolator(new DecelerateInterpolator());
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float animatedValue = (float) animation.getAnimatedValue();
stepView.setCurrentStep((int) animatedValue);
}
});
valueAnimator.start();
在使用屬性動(dòng)畫的時(shí)候要注意animation.getAnimatedValue();返回的是一個(gè)Object,在這里強(qiáng)轉(zhuǎn)成float型,不能轉(zhuǎn)成int要不會(huì)報(bào)轉(zhuǎn)換異常的錯(cuò)誤;
這樣就實(shí)現(xiàn)上面的效果了,上面寫的有什么問題,歡迎交流。
源碼地址:http://pan.baidu.com/s/1eSAFa4y