android自定义圆圈动画,自定义view实现动画数字圆圈
我們要實(shí)現(xiàn)的是如下的效果,
1.該view在設(shè)置屬性之后時(shí)候會有數(shù)字和圓圈不斷增長的效果
2.該view在按下和放開狀態(tài)下顯示不同的樣式。
這種效果邏輯上并不復(fù)雜,底層灰色圓圈和藍(lán)色扇形圓圈都是用canvas.drawArc()繪制出來的,中間的數(shù)字用drawtext繪制,數(shù)字不斷增長的效果用了繼承Animation的動畫類;在按下和放開狀態(tài)下顯示不同的樣式是重寫了View 的setPressed()方法。
先貼出所有代碼,再一一解釋import com.jcodecraeer.util.MyUtils;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Typeface;
import android.graphics.Paint.Align;
import android.graphics.Paint.Style;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.Transformation;
public class CircleBar extends View {
private RectF mColorWheelRectangle = new RectF();
private Paint mDefaultWheelPaint;
private Paint mColorWheelPaint;
private Paint textPaint;
private float mColorWheelRadius;
private float circleStrokeWidth;
private float pressExtraStrokeWidth;
private String mText;
private int mCount;
private float mSweepAnglePer;
private float mSweepAngle;
private int mTextSize;
BarAnimation anim;
public CircleBar(Context context) {
super(context);
init(null, 0);
}
public CircleBar(Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs, 0);
}
public CircleBar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(attrs, defStyle);
}
private void init(AttributeSet attrs, int defStyle) {
circleStrokeWidth = MyUtils.dip2px(getContext(), 10);
pressExtraStrokeWidth = MyUtils.dip2px(getContext(), 2);
mTextSize = MyUtils.dip2px(getContext(), 40);
mColorWheelPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mColorWheelPaint.setColor(0xFF29a6f6);
mColorWheelPaint.setStyle(Paint.Style.STROKE);
mColorWheelPaint.setStrokeWidth(circleStrokeWidth);
mDefaultWheelPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mDefaultWheelPaint.setColor(0xFFeeefef);
mDefaultWheelPaint.setStyle(Paint.Style.STROKE);
mDefaultWheelPaint.setStrokeWidth(circleStrokeWidth);
textPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.LINEAR_TEXT_FLAG);
textPaint.setColor(0xFF333333);
textPaint.setStyle(Style.FILL_AND_STROKE);
textPaint.setTextAlign(Align.LEFT);
textPaint.setTextSize(mTextSize);
mText = "0";
mSweepAngle = 0;
anim = new BarAnimation();
anim.setDuration(2000);
}
@Override
protected void onDraw(Canvas canvas) {
canvas.drawArc(mColorWheelRectangle, -90, 360, false, mDefaultWheelPaint);
canvas.drawArc(mColorWheelRectangle, -90, mSweepAnglePer, false, mColorWheelPaint);
Rect bounds = new Rect();
String textstr=mCount+"";
textPaint.getTextBounds(textstr, 0, textstr.length(), bounds);
canvas.drawText(
textstr+"",
(mColorWheelRectangle.centerX())
- (textPaint.measureText(textstr) / 2),
mColorWheelRectangle.centerY() + bounds.height() / 2,
textPaint);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int height = getDefaultSize(getSuggestedMinimumHeight(),
heightMeasureSpec);
int width = getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec);
int min = Math.min(width, height);
setMeasuredDimension(min, min);
mColorWheelRadius = min - circleStrokeWidth -pressExtraStrokeWidth ;
mColorWheelRectangle.set(circleStrokeWidth+pressExtraStrokeWidth, circleStrokeWidth+pressExtraStrokeWidth,
mColorWheelRadius, mColorWheelRadius);
}
@Override
public void setPressed(boolean pressed) {
Log.i(TAG,"call setPressed ");
if (pressed) {
mColorWheelPaint.setColor(0xFF165da6);
textPaint.setColor(0xFF070707);
mColorWheelPaint.setStrokeWidth(circleStrokeWidth+pressExtraStrokeWidth);
mDefaultWheelPaint.setStrokeWidth(circleStrokeWidth+pressExtraStrokeWidth);
textPaint.setTextSize(mTextSize-pressExtraStrokeWidth);
} else {
mColorWheelPaint.setColor(0xFF29a6f6);
textPaint.setColor(0xFF333333);
mColorWheelPaint.setStrokeWidth(circleStrokeWidth);
mDefaultWheelPaint.setStrokeWidth(circleStrokeWidth);
textPaint.setTextSize(mTextSize);
}
super.setPressed(pressed);
this.invalidate();
}
public void startCustomAnimation(){
this.startAnimation(anim);
}
public void setText(String text){
mText = text;
this.startAnimation(anim);
}
public void setSweepAngle(float sweepAngle){
mSweepAngle = sweepAngle;
}
public class BarAnimation extends Animation {
/**
* Initializes expand collapse animation, has two types, collapse (1) and expand (0).
* @param view The view to animate
* @param type The type of animation: 0 will expand from gone and 0 size to visible and layout size defined in xml.
* 1 will collapse view and set to gone
*/
public BarAnimation() {
}
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
super.applyTransformation(interpolatedTime, t);
if (interpolatedTime < 1.0f) {
mSweepAnglePer = interpolatedTime * mSweepAngle;
mCount = (int)(interpolatedTime * Float.parseFloat(mText));
} else {
mSweepAnglePer = mSweepAngle;
mCount = Integer.parseInt(mText);
}
postInvalidate();
}
}
}
屬性變量及其說明
private RectF mColorWheelRectangle = new RectF();圓圈的矩形范圍
private Paint mDefaultWheelPaint; ?繪制底部灰色圓圈的畫筆
private Paint mColorWheelPaint; 繪制藍(lán)色扇形的畫筆
private Paint textPaint; 中間文字的畫筆
private float mColorWheelRadius; 圓圈普通狀態(tài)下的半徑
private float circleStrokeWidth; 圓圈的線條粗細(xì)
private float pressExtraStrokeWidth;按下狀態(tài)下增加的圓圈線條增加的粗細(xì)
private String mText;中間文字內(nèi)容
private int mCount; 為了達(dá)到數(shù)字增加效果而添加的變量,他和mText其實(shí)代表一個(gè)意思
private float mSweepAnglePer; ?為了達(dá)到藍(lán)色扇形增加效果而添加的變量,他和mSweepAngle其實(shí)代表一個(gè)意思
private float mSweepAngle; 扇形弧度
private int mTextSize;文字顏色
BarAnimation anim;動畫類
構(gòu)造方法調(diào)用之后,第一個(gè)調(diào)用的是init方法,在該方法中初始化了各種畫筆的顏色,風(fēng)格等,字體大小和線條粗細(xì)則使用了我自己定義的工具函數(shù)dip2px(),這樣做的目的是在不同分辨率的手機(jī)上,相同數(shù)值的最終顯示效果差別不大,比如字體大小mTextSize的初始化:mTextSize = MyUtils.dip2px(getContext(), 40);
還定義了動畫對象以及動畫持續(xù)時(shí)間:anim = new BarAnimation();
anim.setDuration(2000);
其中BarAnimation為自定義的動畫類:public class BarAnimation extends Animation {
/**
* Initializes expand collapse animation, has two types, collapse (1) and expand (0).
* @param view The view to animate
* @param type The type of animation: 0 will expand from gone and 0 size to visible and layout size defined in xml.
* 1 will collapse view and set to gone
*/
public BarAnimation() {
}
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
super.applyTransformation(interpolatedTime, t);
if (interpolatedTime < 1.0f) {
mSweepAnglePer = interpolatedTime * mSweepAngle;
mCount = (int)(interpolatedTime * Float.parseFloat(mText));
} else {
mSweepAnglePer = mSweepAngle;
mCount = Integer.parseInt(mText);
}
postInvalidate();
}
}
這個(gè)動畫類利用了applyTransformation參數(shù)中的interpolatedTime參數(shù)(從0到1)的變化特點(diǎn),實(shí)現(xiàn)了該View的某個(gè)屬性隨時(shí)間改變而改變。原理是在每次系統(tǒng)調(diào)用animation的applyTransformation()方法時(shí),改變mSweepAnglePer,mCount的值,然后調(diào)用postInvalidate()不停的繪制view。if (interpolatedTime < 1.0f) {
mSweepAnglePer = interpolatedTime * mSweepAngle;
mCount = (int)(interpolatedTime * Float.parseFloat(mText));
}
mSweepAnglePer,mCount這兩個(gè)屬性只是動畫過程中要用到的臨時(shí)屬性,mText和mSweepAngle才是動畫結(jié)束之后表示扇形弧度和中間數(shù)值的真實(shí)值。
繪制方法
在onDraw方法中我們繪制了圓圈、扇形以及文字,但是繪制需要用到的一些坐標(biāo)值是經(jīng)過計(jì)算得出的,比如繪制扇形:canvas.drawArc(mColorWheelRectangle, -90, mSweepAnglePer, false, mColorWheelPaint);
mColorWheelRectangle是一個(gè)矩形,這個(gè)矩形的上下左右邊界都是在onMeasure方法中根據(jù)控件所分配的大小得出來的。
具體計(jì)算方式在onMeasure的實(shí)現(xiàn)中:@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int height = getDefaultSize(getSuggestedMinimumHeight(),
heightMeasureSpec);
int width = getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec);
int min = Math.min(width, height);
setMeasuredDimension(min, min);
mColorWheelRadius = min - circleStrokeWidth -pressExtraStrokeWidth ;
mColorWheelRectangle.set(circleStrokeWidth+pressExtraStrokeWidth, circleStrokeWidth+pressExtraStrokeWidth,
mColorWheelRadius, mColorWheelRadius);
}
從setMeasuredDimension(min, min)可以看出我們強(qiáng)制該View為正方形。上面說到的mColorWheelRectangle矩形區(qū)域比控件的實(shí)際邊界要小,這樣做的目的是在按下狀態(tài)下狀態(tài)下讓圓圈的線條變大之后也并不會超出矩形區(qū)域。
按下松開view樣式改變的實(shí)現(xiàn)
改變樣式很簡單,只需改變畫筆的樣式就可以了,關(guān)鍵是在什么地方改變。我們都知道設(shè)置背景成selector就能是按下松開狀態(tài)下背景改變,但是直接設(shè)背景不滿足這里的要求,因?yàn)檫@是個(gè)圓圈,如果設(shè)置背景那肯定不會緊貼著圓圈邊緣,但是我們可以在不同狀態(tài)下更改畫筆然后重繪達(dá)到相同的效果。如何檢測到按下與松開呢?
看了view的源碼知道setPressed()方法可以滿足我們的要求:
@Override
public void setPressed(boolean pressed) {
Log.i(TAG,"call setPressed ");
if (pressed) {
mColorWheelPaint.setColor(0xFF165da6);
textPaint.setColor(0xFF070707);
mColorWheelPaint.setStrokeWidth(circleStrokeWidth+pressExtraStrokeWidth);
mDefaultWheelPaint.setStrokeWidth(circleStrokeWidth+pressExtraStrokeWidth);
textPaint.setTextSize(mTextSize-pressExtraStrokeWidth);
} else {
mColorWheelPaint.setColor(0xFF29a6f6);
textPaint.setColor(0xFF333333);
mColorWheelPaint.setStrokeWidth(circleStrokeWidth);
mDefaultWheelPaint.setStrokeWidth(circleStrokeWidth);
textPaint.setTextSize(mTextSize);
}
super.setPressed(pressed);
this.invalidate();
}
每次按下或者松開setPressed都會被調(diào)用,我們重寫該方法,但要注意調(diào)用super.setPress()不然長按放開之后boolean pressed參數(shù)仍然為true,這樣松開之后樣式就保持按下的狀態(tài)。具體原因還需要多閱讀view的源碼。
總結(jié)
其實(shí)這里最主要的是要有耐心了解canvas的一些方法,還有就是要根據(jù)自己的需求有針對性的分析view的源碼。
總結(jié)
以上是生活随笔為你收集整理的android自定义圆圈动画,自定义view实现动画数字圆圈的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 计算机能力提升研修总结ppt,信息技术能
- 下一篇: c# 修改xslt并转为html,c#使