Android 属性动画基础,入门

为什么引入属性动画 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就可以了。。


    推荐阅读