还是老规矩先看看动画效果
分析状态值及逻辑事件:0、初始状态1、点击实现旋转动画 2、长按倒计时动画(类似于uber取消订单的效果)
实现步骤
1、来分析下我们自定义view需要的属性
1、内圆的半径round_radius
2、外圆环的宽度ring_width
3、内圆与外圆环之间的间距space_inner_out
4、未点击时刚开始的状态下的颜色start_round_color(我们这个动画其实只用到了两周颜色,所以就定为开始的颜色和过程中的颜色)
5、点击开始动画的颜色centre_round_color
6、文字的大小text_size
7、文字的颜色text_color
2、定义view的属性,创建一个名字叫MyProgressView的类继承自view,这里提前创建它,只是为了我们在创建attrs资源文件自定义view属性的时方便命名
<resources>
<declare-styleable name="MyProgressView">
<attr name="space_inner_out" format="integer"/>
<attr name="round_radius" format="integer"/>
<attr name="ring_width" format="integer"/>
<attr name="text_size" format="integer"/>
<attr name="text_color" format="color"/>
<attr name="start_round_color" format="color"/>
<attr name="centre_round_color" format="color"/>
</declare-styleable>
</resources>
3、我们就该来实现自定义view的功能了
首先获取自定义属性
private void init(Context context, AttributeSet attrs) {
//获取自定义属性值
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MyProgressView);
screenWidth = ScreenUtils.getScreenWidth(context);
round_radius = a.getInt(R.styleable.MyProgressView_round_radius, screenWidth / 14);//默认大小为屏幕宽度的6分之一
ring_width = a.getInt(R.styleable.MyProgressView_ring_width, 16);//默认外圆的宽度为20
space_inner_out = a.getInt(R.styleable.MyProgressView_space_inner_out, 30);
//获取文字的大小,默认为30
text_size = a.getInt(R.styleable.MyProgressView_text_size, 30);
text_color = a.getColor(R.styleable.MyProgressView_text_color, Color.WHITE);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
start_round_color = a.getColor(R.styleable.MyProgressView_start_round_color,Color.BLUE);
centre_round_color = a.getColor(R.styleable.MyProgressView_centre_round_color,Color.RED);
calculate();
}
/**
* 计算
*/
public void calculate() {
//计算view的大小
viewSizw = round_radius * 2 + ring_width * 2 + space_inner_out * 2;
//获取bitmap
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.yuanhuan);
progress_bitmap = Bitmap.createScaledBitmap(bitmap, viewSizw, viewSizw, true);
//绘制旋转动画用到的matrix
matrix = new Matrix();
}
添加一个设置stute的方法
/**
* 设置状态值
* @param stute
*/
public void setStute(int stute) {
this.stute = stute;
if (stute == 1) {
if (animator_stute2 != null) {//当前是stute=2向stute=1转时,结束上一个动画
animator_stute2.cancel();
}
startAnimator();
} else if (stute == 2) {
if (animator_stute1 != null)//当前是stute=1向stute=2转时,结束上一个动画
animator_stute1.cancel();
startCountDown();
}
}
重写on Measure()
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(viewSizw, viewSizw);//设置view的大小是上面计算的
//圆环的位置
ring_rect = new RectF(0+ring_width/2, 0+ring_width/2,getWidth()-ring_width/2,getHeight()-ring_width/2);
width = getWidth();
height = getHeight();
//圆心
cx = width / 2;
cy = height / 2;
}
绘制
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
switch (stute) {
case 0://开始
//绘制内圆
mPaint.setColor(start_round_color);
mPaint.setStyle(Paint.Style.FILL);
canvas.drawCircle(cx, cy, round_radius, mPaint);//绘制内圆
//绘制外圆环
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(ring_width);
canvas.drawCircle(cx, cy, width / 2 - ring_width / 2, mPaint);
//绘制文字
mPaint.setStrokeWidth(0);
mPaint.setColor(text_color);
mPaint.setTextSize(text_size);
float v = mPaint.measureText(START_TEXT);
canvas.drawText(START_TEXT, width / 2 - v / 2, height / 2 + text_size / 2, mPaint);
break;
case 1:
//绘制内圆
mPaint.setColor(centre_round_color);
mPaint.setStyle(Paint.Style.FILL);
canvas.drawCircle(cx, cy, round_radius, mPaint);//绘制内圆
//绘制文字
mPaint.setStrokeWidth(0);
mPaint.setColor(text_color);
mPaint.setTextSize(text_size);
float v1 = mPaint.measureText(CENTRE_TEXT);
canvas.drawText(CENTRE_TEXT, width / 2 - v1 / 2, height / 2 + text_size / 2, mPaint);
//绘制外圆环
canvas.drawBitmap(progress_bitmap, matrix, mPaint);
break;
case 2:
//绘制内圆
mPaint.setColor(centre_round_color);
mPaint.setStyle(Paint.Style.FILL);
canvas.drawCircle(cx, cy, round_radius, mPaint);//绘制内圆
//绘制外圆环
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(ring_width);
canvas.drawArc(ring_rect, 270, index, false, mPaint);
//绘制文字
mPaint.setStrokeWidth(0);
mPaint.setColor(text_color);
mPaint.setTextSize(text_size);
float v2 = mPaint.measureText(CENTRE_TEXT);
canvas.drawText(CENTRE_TEXT, width / 2 - v2 / 2, height / 2 + text_size / 2, mPaint);
break;
}
}
还有两个动画的实现
/**
* 开启动画
*/
private void startAnimator() {
animator_stute1 = ValueAnimator.ofInt(360);
animator_stute1.setDuration(2000);
animator_stute1.setInterpolator(new LinearInterpolator());
animator_stute1.setRepeatCount(ValueAnimator.INFINITE);
animator_stute1.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
//这里使用来实现旋转动画
matrix.postRotate(DEGREES, progress_bitmap.getWidth() / 2, progress_bitmap.getHeight() / 2);
postInvalidate();
}
});
animator_stute1.start();
}
/**
* 停止的动画
*/
private void startCountDown() {
index=360;//将index设置为初始值
animator_stute2 = ValueAnimator.ofInt(360);
animator_stute2.setDuration(TIME_FOR_SOTP);
animator_stute2.setInterpolator(new LinearInterpolator());
animator_stute2.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
index -= S;
if (index <= 0) {
stute = 0;//设置为初始状态
index = 360;//将index设置为初始状态
is_stop = true;//停止动画结束
}
postInvalidate();
}
});
animator_stute2.start();
}
旋转动画实现的主要方法
matrix.postRotate(DEGREES, progress_bitmap.getWidth() / 2, progress_bitmap.getHeight() / 2);//这里这是旋转的角度DEGREES,旋转中心就是图片的中心
这里就是绘制旋转后的图片
//绘制外圆环 canvas.drawBitmap(progress_bitmap, matrix, mPaint);
在mainactivity中的使用
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final MyProgressView m = (MyProgressView) findViewById(R.id.mp);
m.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (m.getStute()==1){//如果当前已经是开始状态,则return
return;
}
m.setStute(1);
}
});
m.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View view) {
if (m.getStute()==1){//如果当前是开始状态才触发停止动画
m.setStute(2);
}
return false;
}
});
}
当然还要重写view的onTouchEvent()方法
@Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_UP && !is_stop && stute == 2) {
setStute(1);//没有结束停止动画,就返回出车动画
index=360;
} else if (event.getAction() == MotionEvent.ACTION_UP && is_stop && stute == 0) {
is_stop = false;//当前停止动画已经结束,则将is_stop设置为初始值
return true;
}
return super.onTouchEvent(event);
}
xml中的布局
<com.dituwuyou.myapplication.MyProgressView
android:id="@+id/mp"
android:layout_centerInParent="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:text_color="@android:color/white"
app:text_size="30"
app:space_inner_out="30"
app:round_radius="150"
app:ring_width="20"
app:centre_round_color="@color/color_ff4631"
app:start_round_color="@color/color_3F51B5"/>
接下来我贴出整个自定义view的代码
/**
* Created by Laer on 2016/10/24.
*/
public class MyProgressView extends View {
private int space_inner_out;//外圆环和内圆的间距(不提供大小设置,代码计算)
private int round_radius;//内圆的半径
private int ring_width;//外圆环的宽度
private int text_size;//文字的大小
private int text_color;//文字的颜色
private int centre_round_color;//开始动画的内圆颜色值
private int start_round_color;//没开始的内圆颜色值
//常量
private static final String START_TEXT = "开始";
private static final String CENTRE_TEXT = "停止";
private static final int S = 3;//减少时的速率
private static final int DEGREES =2;//旋转动画的速率
private static final int TIME_FOR_SOTP = 3000;//长按停止出车的时间,这里默认是3秒
//画笔
private Paint mPaint;
private RectF ring_rect;
//标识
private boolean is_stop;//是否停止出车
private boolean is_start_animotor;//是否开启动画
//
private int screenWidth;
private int viewSizw;//当前view的大小
private Bitmap progress_bitmap;
private Matrix matrix;
private int index = 360;//当前倒计时的进度值
private int stute = 0;//当前的状态值
private ValueAnimator animator_stute1;
private ValueAnimator animator_stute2;
private int width;//当前view的宽度
private int height;//当前view的高度
private int cx;//圆心的x坐标
private int cy;//圆心的y坐标
public MyProgressView(Context context) {
super(context);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
}
public MyProgressView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
/**
* 获取当前状态值
* @return
*/
public int getStute() {
return stute;
}
/**
* 设置状态值
* @param stute
*/
public void setStute(int stute) {
this.stute = stute;
if (stute == 1) {
if (animator_stute2 != null) {//当前是stute=2向stute=1转时,结束上一个动画
animator_stute2.cancel();
}
startAnimator();
} else if (stute == 2) {
if (animator_stute1 != null)//当前是stute=1向stute=2转时,结束上一个动画
animator_stute1.cancel();
startCountDown();
}
}
/**
* 开启动画
*/
private void startAnimator() {
animator_stute1 = ValueAnimator.ofInt(360);
animator_stute1.setDuration(2000);
animator_stute1.setInterpolator(new LinearInterpolator());
animator_stute1.setRepeatCount(ValueAnimator.INFINITE);
animator_stute1.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
matrix.postRotate(DEGREES, progress_bitmap.getWidth() / 2, progress_bitmap.getHeight() / 2);
postInvalidate();
}
});
animator_stute1.start();
}
/**
* 停止的动画
*/
private void startCountDown() {
index=360;//将index设置为初始值
animator_stute2 = ValueAnimator.ofInt(360);
animator_stute2.setDuration(TIME_FOR_SOTP);
animator_stute2.setInterpolator(new LinearInterpolator());
animator_stute2.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
index -= S;
if (index <= 0) {
stute = 0;//设置为初始状态
index = 360;//将index设置为初始状态
is_stop = true;//停止动画结束
}
postInvalidate();
}
});
animator_stute2.start();
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_UP && !is_stop && stute == 2) {
setStute(1);//没有结束停止动画,就返回出车动画
index=360;
} else if (event.getAction() == MotionEvent.ACTION_UP && is_stop && stute == 0) {
is_stop = false;//当前停止动画已经结束,则将is_stop设置为初始值
return true;
}
return super.onTouchEvent(event);
}
private void init(Context context, AttributeSet attrs) {
//获取自定义属性值
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MyProgressView);
screenWidth = ScreenUtils.getScreenWidth(context);
round_radius = a.getInt(R.styleable.MyProgressView_round_radius, screenWidth / 14);//默认大小为屏幕宽度的6分之一
ring_width = a.getInt(R.styleable.MyProgressView_ring_width, 16);//默认外圆的宽度为20
space_inner_out = a.getInt(R.styleable.MyProgressView_space_inner_out, 30);
//获取文字的大小,默认为30
text_size = a.getInt(R.styleable.MyProgressView_text_size, 30);
text_color = a.getColor(R.styleable.MyProgressView_text_color, Color.WHITE);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
start_round_color = a.getColor(R.styleable.MyProgressView_start_round_color,Color.BLUE);
centre_round_color = a.getColor(R.styleable.MyProgressView_centre_round_color,Color.RED);
calculate();
}
/**
* 计算
*/
public void calculate() {
//计算view的大小
viewSizw = round_radius * 2 + ring_width * 2 + space_inner_out * 2;
//获取bitmap
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.yuanhuan);
progress_bitmap = Bitmap.createScaledBitmap(bitmap, viewSizw, viewSizw, true);
//绘制旋转动画用到的matrix
matrix = new Matrix();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(viewSizw, viewSizw);//设置view的大小是上面计算的
//圆环的位置
ring_rect = new RectF(0+ring_width/2, 0+ring_width/2,getWidth()-ring_width/2,getHeight()-ring_width/2);
width = getWidth();
height = getHeight();
//圆心
cx = width / 2;
cy = height / 2;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
switch (stute) {
case 0://开始
//绘制内圆
mPaint.setColor(start_round_color);
mPaint.setStyle(Paint.Style.FILL);
canvas.drawCircle(cx, cy, round_radius, mPaint);//绘制内圆
//绘制外圆环
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(ring_width);
canvas.drawCircle(cx, cy, width / 2 - ring_width / 2, mPaint);
//绘制文字
mPaint.setStrokeWidth(0);
mPaint.setColor(text_color);
mPaint.setTextSize(text_size);
float v = mPaint.measureText(START_TEXT);
canvas.drawText(START_TEXT, width / 2 - v / 2, height / 2 + text_size / 2, mPaint);
break;
case 1:
//绘制内圆
mPaint.setColor(centre_round_color);
mPaint.setStyle(Paint.Style.FILL);
canvas.drawCircle(cx, cy, round_radius, mPaint);//绘制内圆
//绘制文字
mPaint.setStrokeWidth(0);
mPaint.setColor(text_color);
mPaint.setTextSize(text_size);
float v1 = mPaint.measureText(CENTRE_TEXT);
canvas.drawText(CENTRE_TEXT, width / 2 - v1 / 2, height / 2 + text_size / 2, mPaint);
//绘制外圆环
canvas.drawBitmap(progress_bitmap, matrix, mPaint);
break;
case 2:
//绘制内圆
mPaint.setColor(centre_round_color);
mPaint.setStyle(Paint.Style.FILL);
canvas.drawCircle(cx, cy, round_radius, mPaint);//绘制内圆
//绘制外圆环
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(ring_width);
canvas.drawArc(ring_rect, 270, index, false, mPaint);
//绘制文字
mPaint.setStrokeWidth(0);
mPaint.setColor(text_color);
mPaint.setTextSize(text_size);
float v2 = mPaint.measureText(CENTRE_TEXT);
canvas.drawText(CENTRE_TEXT, width / 2 - v2 / 2, height / 2 + text_size / 2, mPaint);
break;
}
}
}