Property Animation:属性动画是针对于3.0(API 11)以上版本的系统有效;
动画特点:可以设置给任意的Object,包括还没有渲染到屏幕上的对象;
可扩展性:自定义任何类型和属性的动画;
常用的View属性成员:
属性动画概述:
/**
* This is the superclass for classes which provide basic support for animations which can be
* started, ended, and have AnimatorListeners
added to them.
*/
public abstract class Animator implements Cloneable {
......
}
所有属性动画的BaseClass就是Animator这个抽象类;
由Animator派生出来的类之间的关系:(我们能看到:AnimatorSet 和ValueAnimator是直接子类,实际我们使用的单个动画效果的时候使用的是ObjectAnimator,ObjectAnimator是ValueAnimator的直接子类,关于ValueAnimator的作用和使用稍后分析,关于TimeAnimator本人使用比较少)
属性动画的属性:
以上扯了那么多用的,其实属性动画就是Animator,补间动画就是Animation先记住这么多区别熟练运用之后再去尝试理解它的设计模式,底层实现方式
/**
* 旋转
* @param view
*/
public void rotateAnimator(View view){
ObjectAnimator.ofFloat(view,"rotationX",50f,95f)
.setDuration(3000)
.start();//x轴的话默认的就是出于0度,往屏幕里面的方向转的
}
对于ObjectAnimator:提供的几个方法,提供了ofInt、ofFloat、ofObject,这几个方法都是设置动画作用的元素、作用的属性、动画开始、结束、以及中间的任意个属性值。当对于属性值,只设置一个的时候,会认为当然对象该属性的值为开始(getPropName反射获取),然后设置的值为终点。如果设置两个,则一个为开始、一个为结束~~~
动画更新的过程中,会不断调用setPropName更新元素的属性,所有使用ObjectAnimator更新某个属性,必须得有getter(设置一个属性值的时候)和setter方法~
所以对于我们自定以的View或者说对于控件的设置某一个属性值的方法中并没有重新绘制View的操作,我们就需要自己写代码去更新视图的操作:
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
view.invalidate();
view.postInvalidate();
}
})
在不是用AnimatorSet的情况下如何设置多个属性同时变化:
public void rotateAnimator(final View view){
ObjectAnimator anim = ObjectAnimator.ofFloat(view,"abc",1.0f,0f)
.setDuration(1000);
//这里的abc属性是随便写的 ,因为我们要使用的是ValueAnimator
//每一个ObjectAnimator都有一个ValueAnimator,很明先就是指值的变化
anim.start();
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
float cVal = (float) valueAnimator.getAnimatedValue();
view.setAlpha(cVal);
view.setScaleX(cVal);
view.setScaleY(cVal);
}
});
}
//从之前的解释我们知道,属性动画是不断的刷新来重新为View设置新的值来达到动画的效果的
实现一个动画更改多个效果:使用propertyValuesHolder
public void rotateAnimator(final View view){
PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("alpha",1f,0f,1f);//这里后面的值是可以赋多个值的,
PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("scaleX",1f,0f,1f);
PropertyValuesHolder pvhZ = PropertyValuesHolder.ofFloat("scaleY",1f,0f,1f);
ObjectAnimator.ofPropertyValuesHolder(view,pvhX,pvhY,pvhZ).setDuration(1000).start();//同理这里的holder中也可以同时放进去多个holder
}
接下来看一下和ValueAnimator的用法,简单看一下用view垂直移动的动画代码
public void rotateAnimator(final View view){
ValueAnimator animator = ValueAnimator.ofFloat(1.0f,0f);
animator.setTarget(view);
animator.setDuration(1000);
animator.start();
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
view.setScaleY((Float) valueAnimator.getAnimatedValue());
}
});
}
//这里的和ObjectAnimator的区别就是没有设置属性值,属性值的更改是在监听事件中进行的,所以好处就是我们不需要关心这个属性值是够有get和set方法
TypeEvaluator的用法
public void rotateAnimator(final View view){
ValueAnimator valueAnimator = new ValueAnimator();
valueAnimator.setDuration(3000);
valueAnimator.setObjectValues(new PointF(0,0));
valueAnimator.setInterpolator(new LinearInterpolator());
valueAnimator.setEvaluator(new TypeEvaluator(){
@Override
public PointF evaluate(float v, PointF pointF, PointF t1) {
Log.i("TAG",200 * v * 3+"");
PointF point = new PointF();
point.x = 200 * v * 3;//此时x的值就是在0-600之间均匀的增加的 至于速率是多少我也暂时不知道
point.y = 0.5f * 200 * (v * 3) * (v * 3);//这里的v就相当于是自变量x从0-1变化所以y的值就类似于900*x的平方
return point;
}
});
valueAnimator.start();
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
PointF point = (PointF) valueAnimator.getAnimatedValue();
//这里可以获取得到PointF中的值x和y对要操作的控件进行赋值操作了
}
});
}
//这里我们为什么要使用这种方式也很明显 因为x和y的值要同时变化,ofInt和ofFloat就不能使用,所以我们自定义一种TypeEvaluator,如果两个参数仍然不够使用的话我们可以自定义一个Bean作为泛型传递进去就ok
动画的监听事件:
ObjectAnimator animator = ObjectAnimator.ofFloat(view,"alpha",0.5f);//只有一个参数的就是代表初始值
animator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
//动画开始的时候
}
@Override
public void onAnimationEnd(Animator animation) {
//动画结束的时候
}
@Override
public void onAnimationCancel(Animator animation) {
//动画取消
}
@Override
public void onAnimationRepeat(Animator animation) {
//动画取消的
}
});
//对于之上的方法太长 要复写所有的方法,我们可以使用AnimatorListenerAdapter 去实现其中的一个方法就可以
AnimatorSet的使用
ObjectAnimator animator1 = ObjectAnimator.ofFloat(view,"alpha",0.5f);
ObjectAnimator animator2 = ObjectAnimator.ofFloat(view,"translateX",0.5f);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playTogether(animator1,animator2);
//或者
animatorSet.play(animator1).with(animator2);
animatorSet.setDuration(1000);
animatorSet.setInterpolator(new LinearInterpolator());
animatorSet.start();
以上是使用代码来实现动画,下面是使用xml文件加载出来动画
1.文件路径 res/animator/动画xml文件
先看一个最简单的:
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000"
android:propertyName="scaleX"
android:valueFrom="1.0"
android:valueTo="2.0"
android:valueType="floatType">
objectAnimator>
Animator animator = AnimatorInflater.loadAnimator(this, R.animator.scalex);
animator.setTarget(view);
animator.start();
动画集合的xml实现
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:ordering="together">
<objectAnimator
android:duration="1000"
android:propertyName="scaleX"
android:valueFrom="1.0"
android:valueTo="0.5"
android:valueType="floatType"/>
<objectAnimator
android:duration="1000"
android:propertyName="scaleY"
android:valueFrom="1.0"
android:valueTo="0.5"
android:valueType="floatType"/>
set>
在Activity中的实现了上面没什么区别
Animator animator = AnimatorInflater.loadAnimator(this, R.animator.myset);
animator.setTarget(view);
animator.start();
关于布局动画的实现方式:主要是通过LayoutTransition来实现
一个布局动画的Demo提供参考
package com.example.administrator.animatortest;
import android.animation.Animator;
import android.animation.AnimatorInflater;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.Keyframe;
import android.animation.LayoutTransition;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.animation.TypeEvaluator;
import android.animation.ValueAnimator;
import android.graphics.Color;
import android.graphics.PointF;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.animation.LinearInterpolator;
import android.widget.Button;
import android.widget.GridLayout;
import android.widget.LinearLayout;
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
private Button mButtonAdd;
private Button mButtonReset;
private GridLayout mGridLayout;
private int buttonNumbers = 1;
private LayoutTransition mLayoutTransition;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mButtonAdd = (Button) findViewById(R.id.layout_animator_addbutton);
mButtonAdd.setOnClickListener(this);
mButtonReset = (Button) findViewById(R.id.layout_animator_resetbutton);
mButtonReset.setOnClickListener(this);
mGridLayout = (GridLayout) findViewById(R.id.layout_animator_gridview);
//为GridView设置LayoutTransition对象
mLayoutTransition = new LayoutTransition();
mGridLayout.setLayoutTransition(mLayoutTransition);
mLayoutTransition.setDuration(300);
mLayoutTransition.setStagger(LayoutTransition.CHANGE_APPEARING,30);
mLayoutTransition.setStagger(LayoutTransition.CHANGE_DISAPPEARING,30);
//和setStagger的区别就是setStagger是通过第一个参数来作为条件单独为这种情况下设置动画时间
//所以duration是要放在前面的说 不然放在后面 就把之前的属性重新覆盖了
//初始化自定义的动画效果
customLayoutTransition();
}
/**
* 自定义效果
*/
private void customLayoutTransition() {
//这里的话是为出现的View设置的动画效果
ObjectAnimator mAnimatorAppearing = ObjectAnimator.
ofFloat(null,"rotationY",90.0f,0.0f)
.setDuration(mLayoutTransition.getDuration(LayoutTransition.APPEARING));//添加的动画时间
//为LayoutTransition设置动画和类型
mLayoutTransition.setAnimator(LayoutTransition.APPEARING,mAnimatorAppearing);
/**
* Add Button
* LayoutTransition.CHANGE_APPEARING
* 当增加一个Button时,设置其他Button的动画效果
*/
PropertyValuesHolder pvhLeft =
PropertyValuesHolder.ofInt("left", 0, 1);
PropertyValuesHolder pvhTop =
PropertyValuesHolder.ofInt("top", 0, 1);
PropertyValuesHolder pvhRight =
PropertyValuesHolder.ofInt("right", 0, 1);
PropertyValuesHolder pvhBottom =
PropertyValuesHolder.ofInt("bottom", 0, 1);
PropertyValuesHolder mHolderScaleX = PropertyValuesHolder.ofFloat("scaleX", 1.0f,0.0f,1.0f);
PropertyValuesHolder mHolderScaleY = PropertyValuesHolder.ofFloat("scaleY", 1.0f,0.0f,1.0f);
ObjectAnimator mObjectAnimatorChangeAppearing = ObjectAnimator.ofPropertyValuesHolder(this, pvhLeft,
pvhTop,pvhRight,pvhBottom,mHolderScaleX,mHolderScaleY).setDuration(mLayoutTransition
.getDuration(LayoutTransition.CHANGE_APPEARING));
mLayoutTransition.setAnimator(LayoutTransition.CHANGE_APPEARING, mObjectAnimatorChangeAppearing);
mObjectAnimatorChangeAppearing.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
// TODO Auto-generated method stub
super.onAnimationEnd(animation);
View view = (View) ((ObjectAnimator) animation).getTarget();
view.setScaleX(1f);
view.setScaleY(1f);
}
});
/**
* Delete Button
* LayoutTransition.DISAPPEARING
* 当删除一个Button时,设置该Button的动画效果
*/
ObjectAnimator mObjectAnimatorDisAppearing = ObjectAnimator.ofFloat(null, "rotationX", 0.0f,90.0f)
.setDuration(mLayoutTransition.getDuration(LayoutTransition.DISAPPEARING));
mLayoutTransition.setAnimator(LayoutTransition.DISAPPEARING, mObjectAnimatorDisAppearing);
mObjectAnimatorDisAppearing.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
// TODO Auto-generated method stub
super.onAnimationEnd(animation);
View view = (View) ((ObjectAnimator) animation).getTarget();
view.setRotationX(0.0f);
}
});
/**
* Delete Button
* LayoutTransition.CHANGE_DISAPPEARING
* 当删除一个Button时,设置其它Button的动画效果
*/
//Keyframe 对象中包含了一个时间/属性值的键值对,用于定义某个时刻的动画状态。
Keyframe mKeyframeStart = Keyframe.ofFloat(0.0f, 0.0f);
Keyframe mKeyframeMiddle = Keyframe.ofFloat(0.5f, 180.0f);
Keyframe mKeyframeEndBefore = Keyframe.ofFloat(0.999f, 360.0f);
Keyframe mKeyframeEnd = Keyframe.ofFloat(1.0f, 0.0f);
PropertyValuesHolder mPropertyValuesHolder = PropertyValuesHolder.ofKeyframe("rotation",
mKeyframeStart,mKeyframeMiddle,mKeyframeEndBefore,mKeyframeEnd);
ObjectAnimator mObjectAnimatorChangeDisAppearing = ObjectAnimator.ofPropertyValuesHolder(this, pvhLeft,pvhTop,pvhRight,pvhBottom,mPropertyValuesHolder)
.setDuration(mLayoutTransition.getDuration(LayoutTransition.CHANGE_DISAPPEARING));
mLayoutTransition.setAnimator(LayoutTransition.CHANGE_DISAPPEARING, mObjectAnimatorChangeDisAppearing);
mObjectAnimatorChangeDisAppearing.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
// TODO Auto-generated method stub
super.onAnimationEnd(animation);
View view = (View) ((ObjectAnimator) animation).getTarget();
view.setRotation(0.0f);
}
});
}
/**
* 点击事件
* @param v
*/
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.layout_animator_addbutton:
addButton();
break;
case R.id.layout_animator_resetbutton:
resetButton();
break;
default:
break;
}
}
/**
* 删除所有的View重置GridLayout
*/
private void resetButton() {
mGridLayout.removeAllViews();
buttonNumbers=1;
}
/**
* 增加一个Button
*/
private void addButton() {
Button mButton = new Button(this);
LinearLayout.LayoutParams mLayoutParams = new LinearLayout.LayoutParams(50,50);
mButton.setLayoutParams(mLayoutParams);
mButton.setText(String.valueOf(buttonNumbers++));
mButton.setTextColor(Color.rgb(0,0,0));
if(buttonNumbers % 2 ==1){
mButton.setBackgroundColor(Color.rgb(45,118,87));
}else {
mButton.setBackgroundColor(Color.rgb(225,24,0));
}
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mGridLayout.removeView(v);
}
});
mGridLayout.addView(mButton,Math.min(1,mGridLayout.getChildCount()));//这里使用的是插入的方法 所以添加的话也会导致 其他view的变化
}
}
布局文件
这个Dmeo 基本上涵盖了属性动画的用法和布局动画的用法,手动敲一下还是能更深入的理解的