为什么引入属性动画 Android3.0之前提供的补间动画机制还算相对比较健全的,比如你的需求中只需要对View进行移动、缩放、旋转和淡入淡出的操作,那么补间动画已经足够健全了。但是,如果一旦需求超出了这四种操作,补间动画就无能为力了。
例如,我们需要改变View的宽度,这个时候就不能通过补间动画实现。此外,补间动画还有一个最大的缺陷,就是它只是改变了View的显示效果而已,并不会真正的改变View的属性。具体来说,例如屏幕左上角有一个Button,使用补间动画将其移动到右下角,此刻你去点击右下角的Button,它是绝对不会响应点击事件的,因此其作用区域依然还在左上角。只不过是补间动画将其绘制在右下角而已。
ok,直接进入正题:
【Android 属性动画基础,入门】 以下是我写的一个MainActivity类,里面很清楚的讲解了如何使用属性动画:
只是基础哦,更多特效,请自行研究哦~
public class MainActivity extends AppCompatActivity implements View.OnClickListener {private Button btnAlpha,btnScaleX,btnRotation,btnTranslate;
private Button btnScaleRotation,btnRotationTrans,btnAlphaRepeat,btnTransRepeat;
private Button btnColor,btnJiBu;
private StepArcView strepView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}private void initView(){
btnAlpha = findViewById(R.id.btn_alpha);
btnAlpha.setOnClickListener(this);
btnScaleX = findViewById(R.id.btn_scaleX);
btnScaleX.setOnClickListener(this);
btnRotation = findViewById(R.id.btn_rotation);
btnRotation.setOnClickListener(this);
btnTranslate = findViewById(R.id.btn_translate);
btnTranslate.setOnClickListener(this);
btnScaleRotation = findViewById(R.id.btn_scale_rotation);
btnScaleRotation.setOnClickListener(this);
btnRotationTrans = findViewById(R.id.btn_romation_trans);
btnRotationTrans.setOnClickListener(this);
btnAlphaRepeat = findViewById(R.id.btn_alpha_repeat);
btnAlphaRepeat.setOnClickListener(this);
btnTransRepeat = findViewById(R.id.btn_translate_repeat);
btnTransRepeat.setOnClickListener(this);
btnColor = findViewById(R.id.btn_color);
btnColor.setOnClickListener(this);
btnJiBu = findViewById(R.id.btn_jibu);
btnJiBu.setOnClickListener(this);
strepView = findViewById(R.id.step_view);
}/**
* ObjectAnimator是ValueAnimator的子类
* 第一个参数:所要作用的目标控件
* 第二个参数:所要操作该控件的属性值
* 第三个参数:所要操作的属性的开始值
* 第四个参数:所要操作属性的结束值
*/
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_alpha:
ObjectAnimator anim = ObjectAnimator.ofFloat(btnAlpha, "alpha", 0f, 1f);
anim.setDuration(1500);
anim.start();
break;
case R.id.btn_scaleX:
/**动画组合**/
PropertyValuesHolder holderX = PropertyValuesHolder.ofFloat("scaleX", 0f, 1f);
PropertyValuesHolder holderY= PropertyValuesHolder.ofFloat("scaleY", 0f, 1f);
/**同时播放两个动画**/
ObjectAnimator.ofPropertyValuesHolder(btnScaleX, holderX, holderY).setDuration(1500).start();
break;
case R.id.btn_rotation:
/**旋转**/
ObjectAnimator rota = ObjectAnimator.ofFloat(btnRotation,"rotationX", 0f, 360f);
rota.setDuration(1500);
rota.start();
break;
case R.id.btn_translate:
ObjectAnimator trans = ObjectAnimator.ofFloat(btnTranslate,"translationY", 100f, 0f);
trans.setDuration(1500);
trans.start();
break;
case R.id.btn_scale_rotation:
/**动画组合**/
AnimatorSet animatorSetGroup1 = new AnimatorSet();
ObjectAnimator objectAnimatorScaleX1 = ObjectAnimator.ofFloat(btnScaleRotation, "scaleX", 0f, 1f);
ObjectAnimator objectAnimatorScaleY1 = ObjectAnimator.ofFloat(btnScaleRotation, "scaleY", 0f, 1f);
ObjectAnimator objectAnimatorRotateX1 = ObjectAnimator.ofFloat(btnScaleRotation, "rotationX", 0f, 360f);
ObjectAnimator objectAnimatorRotateY1 = ObjectAnimator.ofFloat(btnScaleRotation, "rotationY", 0f, 360f);
animatorSetGroup1.setDuration(1500);
animatorSetGroup1.play(objectAnimatorScaleX1).with(objectAnimatorScaleY1)
.before(objectAnimatorRotateX1).before(objectAnimatorRotateY1);
animatorSetGroup1.start();
break;
case R.id.btn_romation_trans:
AnimatorSet animatorSetGroup2 = new AnimatorSet();
ObjectAnimator objectAnimatorTranslate2 = ObjectAnimator.ofFloat(btnRotationTrans, "translationX", 150f, 0f);
ObjectAnimator objectAnimatorRotateX2 = ObjectAnimator.ofFloat(btnRotationTrans, "rotationX", 0f, 360f);
ObjectAnimator objectAnimatorRotateY2 = ObjectAnimator.ofFloat(btnRotationTrans, "rotationY", 0f, 360f);
animatorSetGroup2.setDuration(1500);
animatorSetGroup2.play(objectAnimatorTranslate2).after(objectAnimatorRotateX2)
.after(objectAnimatorRotateY2);
animatorSetGroup2.start();
break;
case R.id.btn_alpha_repeat:
ObjectAnimator alphaRepeat = ObjectAnimator.ofFloat(btnAlphaRepeat, "alpha", 0f, 1f);
alphaRepeat.setDuration(150);
alphaRepeat.setRepeatCount(8);
alphaRepeat.start();
break;
case R.id.btn_translate_repeat:
ObjectAnimator transRepeat = ObjectAnimator.ofFloat(btnTransRepeat, "translationX", -10f, 10f);
transRepeat.setDuration(150);
transRepeat.setRepeatCount(8);
transRepeat.start();
transRepeat.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
ObjectAnimator translation = ObjectAnimator.ofFloat(btnTransRepeat, "translationX", 10f,0f);
translation.setDuration(150);
translation.start();
super.onAnimationEnd(animation);
}
});
break;
case R.id.btn_color:
ObjectAnimator colorAnim = ObjectAnimator.ofInt(btnColor, "backgroundColor", Color.BLUE, Color.YELLOW, Color.RED);
colorAnim.setDuration(3000);
colorAnim.start();
break;
case R.id.btn_jibu:
strepView.setCurrentCount(7000, 1000);
break;
}
}}
仿QQ运动的自定义控件:
/**
* Created by DylanAndroid on 2016/5/26.
* 显示步数的圆弧
*/
public class StepArcView extends View {/**
* 圆弧的宽度
*/
private float borderWidth = 38f;
/**
* 画步数的数值的字体大小
*/
private float numberTextSize = 0;
/**
* 步数
*/
private String stepNumber = "0";
/**
* 开始绘制圆弧的角度
*/
private float startAngle = 135;
/**
* 终点对应的角度和起始点对应的角度的夹角
*/
private float angleLength = 270;
/**
* 所要绘制的当前步数的红色圆弧终点到起点的夹角
*/
private float currentAngleLength = 0;
/**
* 动画时长
*/
private int animationLength = 3000;
public StepArcView(Context context) {
super(context);
}public StepArcView(Context context, AttributeSet attrs) {
super(context, attrs);
}public StepArcView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
/**中心点的x坐标*/
float centerX = (getWidth()) / 2;
/**指定圆弧的外轮廓矩形区域*/
RectF rectF = new RectF(0 + borderWidth, borderWidth, 2 * centerX - borderWidth, 2 * centerX - borderWidth);
/**【第一步】绘制整体的黄色圆弧*/
drawArcYellow(canvas, rectF);
/**【第二步】绘制当前进度的红色圆弧*/
drawArcRed(canvas, rectF);
/**【第三步】绘制当前进度的红色数字*/
drawTextNumber(canvas, centerX);
/**【第四步】绘制"步数"的红色数字*/
drawTextStepString(canvas, centerX);
}/**
* 1.绘制总步数的黄色圆弧
*
* @param canvas 画笔
* @param rectF参考的矩形
*/
private void drawArcYellow(Canvas canvas, RectF rectF) {
Paint paint = new Paint();
/** 默认画笔颜色,黄色 */
paint.setColor(Color.YELLOW);
/** 结合处为圆弧*/
paint.setStrokeJoin(Paint.Join.ROUND);
/** 设置画笔的样式 Paint.Cap.Round ,Cap.SQUARE等分别为圆形、方形*/
paint.setStrokeCap(Paint.Cap.ROUND);
/** 设置画笔的填充样式 Paint.Style.FILL:填充内部;
Paint.Style.FILL_AND_STROKE:填充内部和描边;
Paint.Style.STROKE:仅描边*/
paint.setStyle(Paint.Style.STROKE);
/**抗锯齿功能*/
paint.setAntiAlias(true);
/**设置画笔宽度*/
paint.setStrokeWidth(borderWidth);
/**绘制圆弧的方法
* drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint)//画弧,
参数一是RectF对象,一个矩形区域椭圆形的界限用于定义在形状、大小、电弧,
参数二是起始角(度)在电弧的开始,圆弧起始角度,单位为度。
参数三圆弧扫过的角度,顺时针方向,单位为度,从右中间开始为零度。
参数四是如果这是true(真)的话,在绘制圆弧时将圆心包括在内,通常用来绘制扇形;如果它是false(假)这将是一个弧线,
参数五是Paint对象;
*/
canvas.drawArc(rectF, startAngle, angleLength, false, paint);
}/**
* 2.绘制当前步数的红色圆弧
*/
private void drawArcRed(Canvas canvas, RectF rectF) {
Paint paintCurrent = new Paint();
paintCurrent.setStrokeJoin(Paint.Join.ROUND);
paintCurrent.setStrokeCap(Paint.Cap.ROUND);
//圆角弧度
paintCurrent.setStyle(Paint.Style.STROKE);
//设置填充样式
paintCurrent.setAntiAlias(true);
//抗锯齿功能
paintCurrent.setStrokeWidth(borderWidth);
//设置画笔宽度
paintCurrent.setColor(Color.RED);
//设置画笔颜色
canvas.drawArc(rectF, startAngle, currentAngleLength, false, paintCurrent);
}/**
* 3.圆环中心的步数
*/
private void drawTextNumber(Canvas canvas, float centerX) {
Paint vTextPaint = new Paint();
vTextPaint.setTextAlign(Paint.Align.CENTER);
vTextPaint.setAntiAlias(true);
//抗锯齿功能
vTextPaint.setTextSize(numberTextSize);
Typeface font = Typeface.create(Typeface.SANS_SERIF, Typeface.NORMAL);
vTextPaint.setTypeface(font);
//字体风格
vTextPaint.setColor(Color.RED);
Rect bounds_Number = new Rect();
vTextPaint.getTextBounds(stepNumber, 0, stepNumber.length(), bounds_Number);
canvas.drawText(stepNumber, centerX, getHeight() / 2 + bounds_Number.height() / 2, vTextPaint);
}/**
* 4.圆环中心[步数]的文字
*/
private void drawTextStepString(Canvas canvas, float centerX) {
Paint vTextPaint = new Paint();
vTextPaint.setTextSize(dipToPx(16));
vTextPaint.setTextAlign(Paint.Align.CENTER);
vTextPaint.setAntiAlias(true);
//抗锯齿功能
vTextPaint.setColor(Color.BLUE);
String stepString = "步数";
Rect bounds = new Rect();
vTextPaint.getTextBounds(stepString, 0, stepString.length(), bounds);
canvas.drawText(stepString, centerX, getHeight() / 2 + bounds.height() + getFontHeight(numberTextSize), vTextPaint);
}/**
* 获取当前步数的数字的高度
*
* @param fontSize 字体大小
* @return 字体高度
*/
public int getFontHeight(float fontSize) {
Paint paint = new Paint();
paint.setTextSize(fontSize);
Rect bounds_Number = new Rect();
paint.getTextBounds(stepNumber, 0, stepNumber.length(), bounds_Number);
return bounds_Number.height();
}/**
* dip 转换成px
*
* @param dip
* @return
*/private int dipToPx(float dip) {
float density = getContext().getResources().getDisplayMetrics().density;
return (int) (dip * density + 0.5f * (dip >= 0 ? 1 : -1));
}/**
* 所走的步数进度
*
* @param totalStepNum设置的步数
* @param currentCounts 所走步数
*/
@RequiresApi(api = Build.VERSION_CODES.HONEYCOMB)
public void setCurrentCount(int totalStepNum, int currentCounts) {
stepNumber = currentCounts + "";
setTextSize(currentCounts);
/**如果当前走的步数超过总步数则圆弧还是270度,不能成为园*/
if (currentCounts > totalStepNum) {
currentCounts = totalStepNum;
}
/**所走步数占用总共步数的百分比*/
float scale = (float) currentCounts / totalStepNum;
/**换算成弧度最后要到达的角度的长度-->弧长*/
float currentAngleLength = scale * angleLength;
/**开始执行动画*/
setAnimation(0, currentAngleLength, animationLength);
}/**
* 为进度设置动画
* ValueAnimator是整个属性动画机制当中最核心的一个类,属性动画的运行机制是通过不断地对值进行操作来实现的,
* 而初始值和结束值之间的动画过渡就是由ValueAnimator这个类来负责计算的。
* 它的内部使用一种时间循环的机制来计算值与值之间的动画过渡,
* 我们只需要将初始值和结束值提供给ValueAnimator,并且告诉它动画所需运行的时长,
* 那么ValueAnimator就会自动帮我们完成从初始值平滑地过渡到结束值这样的效果。
*
* @param last
* @param current
*/
@RequiresApi(api = Build.VERSION_CODES.HONEYCOMB)
private void setAnimation(float last, float current, int length) {
ValueAnimator progressAnimator = ValueAnimator.ofFloat(last, current);
progressAnimator.setDuration(length);
progressAnimator.setTarget(currentAngleLength);
progressAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
currentAngleLength = (float) animation.getAnimatedValue();
invalidate();
}
});
progressAnimator.start();
}/**
* 设置文本大小,防止步数特别大之后放不下,将字体大小动态设置
*
* @param num
*/
public void setTextSize(int num) {
String s = String.valueOf(num);
int length = s.length();
if (length <= 4) {
numberTextSize = dipToPx(50);
} else if (length > 4 && length <= 6) {
numberTextSize = dipToPx(40);
} else if (length > 6 && length <= 8) {
numberTextSize = dipToPx(30);
} else if (length > 8) {
numberTextSize = dipToPx(25);
}
}}
layout页面:
以上就是属性动画的实现方式。 大家直接cy就可以了。。