分类
Android动画分为两类:ViewAnimation(TweenAimation, FrameAnimation)和PropertyAnimator(ValueAnimator, ObjectAnimator)
引入PropertyAnimator的作用
- 能实现TweenAnimation无法实现的动画
- 改变View的某一属性做动画,ViewAnimation仅对View做动画
- ViewAnimation点击区域还在动画播放之前
1. TweenAimation
- xml设置
- AlphaAnimation
- ScaleAnimation
- TranslateAnimation
- RotateAnimation
用法
- xml共同属性:
- fillAfter:如果true,动画结束,将保持动画最后时的状态
- fillBefore、fillEnable:如果true,动画结束,将还原到开始动画前的状态
- repeatCount:重复次数;repeaterMode:重复类型,有reverse倒序重放和restart重新播放。一起使用
- interpolator:设置插值器,即指定动作效果。
android:fillBefore="true" android:repeatCount="1" android:repeatMode="restart"
- Interpolator 插值器
- AccelerateDecelerateInterpolator:动画开始结束比较慢,中间加速
- AccelerateInterpolator:动画开始比较慢,然后加速
- AnticipateInterpolator:开始向前,然后向后甩
- BounceInterpolator:开始向后,然后向前甩一定值后,返回最后的值
- CycleInterploator:循环播放特定的次数,速度改变沿着正弦曲线
- DecelerateInterpolator:开始快,然后慢
- LinearInterpolator:线性速度改变
- OvershootInterpolator:向前甩一定值,再回到原来位置
- 公共类Animation
- setFillAfter/Before/Enable(boolean)
- setRepeatCount(int), setRepeatMode(int)
- setInterpolator(Interpolator)
2. FrameAnimation
帧动画又称为Drawable Animation,就是通过一系列的Drawable依次显示来达到模拟动画的效果。
使用
-
xml
``` res/drawable/ drawable_animation.xml
```
ImageView image = (ImageView) findViewById(R.id.image);
image.setBackgroundResource(R.drawable.drawable_animation);
AnimationDrawable animation = (AnimationDrawable) image.getBackground();
animation.start();
-
代码
ImageView image = findViewById(R.id.image); image.setBackgroundResource(R.drawable.drawable_animation); AnimationDrawable animation = image.getBackground(); animation.addFrame(getResources().getDrawable(R.drawable.frame1), 200); animation.addFrame(getResources().getDrawable(R.drawable.frame2), 200); animation.addFrame(getResources().getDrawable(R.drawable.frame3), 200); animation.start();
方法
- start/stop()
- addFrame(Drawable frame, int duration), 设置一帧,和持续时间
- setOneShoe(boolean flag), true 播放一次,false 循环播放
- isRunning(), 是否正在播放
3. ValueAnimator
- ValueAnimator不会对View做任何操作,而是去设定值
-
用法:创建ValueAnimator,添加监听
ValueAnimator animator = ValueAnimator.ofInt(0,400); animator.setDuration(1000); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation){ int curValue = (int)animation.getAnimatedValue(); } }); animator.start();
-
监听器(ObjectAnimator也适用)
监听器一:监听动画变化时的实时值 public static interface AnimatorUpdateListener { void onAnimationUpdate(ValueAnimator animation); } //添加方法为:public void addUpdateListener(AnimatorUpdateListener listener) 监听器二:监听动画变化时四个状态 public static interface AnimatorListener { void onAnimationStart(Animator animation); void onAnimationEnd(Animator animation); void onAnimationCancel(Animator animation); void onAnimationRepeat(Animator animation); } //添加方法为:public void addListener(AnimatorListener listener) 取消监听(并未cancel动画) void removeUpdateListener(AnimatorUpdateListener listener); void removeAllUpdateListeners(); void removeListener(AnimatorListener listener); void removeAllListeners();
- 其他方法
- setStartDelay(long)
- clone:所有的动画效果都一样,但cancel需要自己调用(原来的Animator.cancel后会clone的不起作用)。
- ValueAnimator ofObject()(TypeEvalutor evaluator, Object … values);自定义的Evaluator,可变长度Object
- PropertyValuesHolder 保存了动画过程中所需要操作的属性和对应的值 ObjectAnimator ofPropertyValuesHoiler(Object target, PropertyValuesHodler…values) PropertyValuesHolder ofKeyframe(String propertyName, Keyframe… values)
4. ObjectAnimator
摘抄自http://wiki.jikexueyuan.com/project/android-animation/7.html
作用
- 动画直接与View相关联,从动画监听过程中解放出来
方法
- extends ValueAnimator, override ofInt(), ofFloat()
- ObjectAnimator ofFloat(Object target, String propertyName, float… values)
- target: 动画绑定的View
- propertyName:指定动画操作的属性
- values:可变长参数,属性值by,to
ObjectAnimator animator = ObjectAnimator.ofFloat(tv,"alpha",1,0,1); animator.setDuration(2000); animator.start();
-
ObjectAnimator做动画时会去
target View
中找对应的set方法来改变对应的属性。View中有关动画,包含以下几组set
方法//透明度 public void setAlpha(float alpha) //旋转 public void setRotation(float rotation) public void setRotationX(float rotationX) public void setRotationY(float rotationY) //位置 public void setTranslationX(float translationX) public void setTranslationY(float translationY) //缩放 public void setScaleX(float scaleX) public void setScaleY(float scaleY)
- 自定义动画注意:
- 设置set方法,动画使用的propertyName与此名称相同
- 动画(如ofInt)只有一个过渡值时,会调用get方法。
- 动画将View的属性从value1 -> value2 -> value3
- Evaluator
- 动画变化过程:定义动画数字区间通过监听器返回当前动画对应数值过程。
ofInt(0,400) --> 加速器 -----> Evaluator ----> 监听器返回 定义动画值 返回当前数字进度 根据进度计算当前值 AnimatorUpdateListener中返回
Evaluator计算当前值就是一个转换器。
- 各种Evaluator
ValueAnimator animator = ValueAnimator.ofInt(0, 600); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener()) { @Override public void onAnimatorUpdate(ValueAnimator animation) { int curValue = (int) animation.getAnimatedValue(); } }
对Evaluator而言,ofInt对应的是
IntEvaluator
。(int)(startIntent + fraction * (endValue - startInt))。即当前动画对应的数值。不能通用,也不可强转。- 使用ArgbEvaluator;设置颜色,在TextView中setBackground去修改颜色
ObjectAnimator animator = ObjectAnimator.ofInt(tv, "BackgroundColor", 0xffff00ff, 0xffffff00, 0xffff00ff); animator.setDuration(8000); animator.setEvaluator(new ArgbEvaluator()); animator.start();
- 插值器
//设置插值器 public void setInterpolator(TimeInterpolator value) //设置 Evaluator public void setEvaluator(TypeEvaluator value)
- 自定义Evaluator: 实现TypeEvaluator,保证定义动画使用的数值类型与Evaluator的返回值类型一样。
public class IntEvaluator implements TypeEvaluator<Integer> { //如果ofInt(0, 400),则此时返回值应该是(200, 600). reverse (400, 0); @Override public Integer evaluate(float fraction, Integer startValue, Integer endValue) { int startInt = startValue; return (int) (200 + startInt + fraction * (endValue - startInt)); // return (int) (endValue - fraction * (endValue - startValue)); } }
-
其他动画
// 设置动画时长,单位是毫秒 ValueAnimator setDuration(long duration) //获取 ValueAnimator 在运动时,当前运动点的值 Object getAnimatedValue(); //设置循环次数,设置为 INFINITE 表示无限循环 void setRepeatCount(int value) //设置循环模式 value 取值有 RESTART,REVERSE, void setRepeatMode(int value)
原理
5. Transition
1. Activity Fragment过渡动画
-
xml设置
```
2. 代码设置
```
getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);//onCreate
setExitTransition() - 当A start B时,A中View退出
setEnterTransition() - 当A start B时,B中View进入
setReturnTransition() - 当B 返回 A时,B中View退出
setReenterTransition() - 当B 返回 A时,A中View进入
- 共享元素
` android:transitionName=”shareName”
,在B中
startActivity(intent, ActivityOptions.
多个共享元素,可通过Pair.create()来创建,startActivity(intent, ActivityOptions.makeSceneTransitionAnimation
(this, Pair.create(view, "share"), Pair.create(fab,"fab")).toBundle());
2. ViewGroup过渡动画
res/layout/activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/master_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/title"
...
android:text="Title"/>
<FrameLayout
android:id="@+id/scene_root">
<include layout="@layout/scene_first" />
</FrameLayout>
</LinearLayout>
res/layout/scene_first.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/scene_container"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TextView
android:id="@+id/text_view1
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Text Line 1" />
<TextView
android:id="@+id/text_view2
android:text="Text Line 2" />
</RelativeLayout>
代码设置
Scene mFirstScene;
Scene mSecondScene;
// Create the scene root for the scenes in this app
mSceneRoot = (ViewGroup) findViewById(R.id.scene_root);
// Create the scenes
mFirstScene = Scene.getSceneForLayout(mSceneRoot, R.layout.scene_first, this);
mSecondScene = Scene.getSceneForLayout(mSceneRoot, R.layout.scene_second, this);
mFirstScene = new Scene(mSceneRoot, mSceneRoot.findViewById(R.id.scene_first))
TransitionManager.go(mSecondScene, new Fade());
3. 使用
- explode,从屏幕中间,slide,fade
- xml 与 代码
res/transition/fade_transition.xml
<fade xmlns:android="http://schemas.android.com/apk/res/android" />
在代码中调用 Transition mFadeTransition =
TransitionInflater.from(this).
inflateTransition(R.transition.fade_transition);
也可以完全通过代码创建:
Transition mFadeTransition = new Fade();
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
<changeBounds />
<fade android:fadingMode="fade_in" />
<targets>
<!-- 应用到某个对象上 -->
<target android:targetId="@id/transition_title" />
</targets>
</fade>
</transitionSet>
changeBound 这个是最长使用的 改变View 大小和位置
changeClipBounds 改变 Clip 边界的大小
changeImageTransform 改变ImageView 的大小 和 martix
ChangeTransform 改变普通的 View 一些Scalex 值
ChangeScroll 改变滑动位置
</transitionSet>
或者在代码中
TransitionSet set = new TransitionSet();
set.addTransition(new Fade())
.addTransition(new ChangeBounds())
.addTransition(new AutoTransition());
res/transition/transition_manager.xml
<transitionManager xmlns:app="http://schemas.android.com/apk/res-auto">
<transition
app:fromScene="@layout/scene1"
app:toScene="@layout/scene2"
app:transition="@transition/trans_scene1_to_scene2" />
<transition
app:fromScene="@layout/scene2"
app:toScene="@layout/scene1"
app:transition="@transition/trans_scene2_to_scene1" />
...
</transitionManager>
5. 原理
使用Transitions API为安卓应用创建动画 英文原文
采用Scene(场景),即对VIewGroup的一层封装,描述所有View的状态。Transition机制:读取不同场景之间View属性的变化,从而产生让这种变化看起来更加平滑的动画。
public static interface TransitionListener {
void onTransitionStart(Transition transition);
void onTransitionEnd(Transition transition);
void onTransitionCancel(Transition transition);
void onTransitionPause(Transition transition);
void onTransitionResume(Transition transition);
}
6. ViewFilpper
- method
- start/stopFlipping()
- isAutoStart(), setAutoStart()
- setFlipInterval(int milliseconds)
- onAttached/DetachedFromWindow(), getWindowVisibilityChanged
7. ViewPropertyAnimator
` view.animate().x(50).setAlpha(0.5f)`
- animate():从调用 View 对象的 animate() 方法开始,该方法返回一个 ViewPropertyAnimator 实例,然后就可以调用它的方法来设置动画参数。
- 自动开始:要注意上述代码并未调用 start() 方法,因为在新 API 中动画启动是隐式的,一旦设置完成后,这些动画就「立刻」「一起」启动了。如果要深究细节的话,其实也并非「立刻」启动,而是在下一次 UI 刷新时。ViewPropertyAnimator 正是利用了这个时间差,把所有声明的动画收集在一起。只要还在继续声明新的动画,那么所有动画就会继续等待下一次 UI 刷新。一旦完成声明,所有动画就在下一次 UI 刷新时一起启动了。当然我们也可以显示调用start()来启动动画。
- 流式接口:ViewPropertyAnimator 拥有流式接口(Fluent interface),允许函数链式调用,使得在一行代码中可以构建多个属性动画。
关于性能
-
能够直接修改目标视图的属性。
-
只需构建一个 Animator 就可以实现多个动画。如果采用 AnimatorSet 实现的话,需要这样做,这段代码创建了两个 Animator 对象(也就是两个独立的动画),然后通过 AnimatorSet 同时播放:
ObjectAnimator animX = ObjectAnimator.ofFloat(myView, "x", 50f);
ObjectAnimator animY = ObjectAnimator.ofFloat(myView, "y", 100f);
AnimatorSet animSetXY = new AnimatorSet();
animSetXY.playTogether(animX, animY);
animSetXY.start();
或者使用 PropertyValuesHolder,只构造一个 Animator 对象来同时实现多个属性动画:
PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("x", 50f);
PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("y", 100f);
ObjectAnimator.ofPropertyValuesHolder(myView, pvhX, pvyY).start(); 而使用 ViewPropertyAnimator 实现的代码已经在上节内容中提到了。
- ViewPropertyAnimator 会在每一个动画帧,计算变化的属性值并设置给属性,然后只调用一次 invalidate 进行重绘。代替每一个属性单独计算、单独重绘。
功能
1. TweenAnimation
1.AlphaAnimation
- xml属性:form/toAlpha:0.0-1.0透明度
- 代码设置: AlphaAnimation(float fromAlpha, float toAlpha)
2. ScaleAnimation
- xml属性:
- from/toX/YScale:起始/结束X/Y方向相对自身缩放比例。
- pivotX/Y:缩放起点X/Y坐标,可以是50,50%,50%p;缩放点:数值–View的左上角+Value(px);%–View左上角+view.getWidth()/2;%p–View左上角+parentView.getWidth()/2.
``` <?xml version=”1.0” encoding=”utf-8”?>
``` 2. 代码设置:
* ScaleAnimation(float fromX, float toX, float fromY, float toY)
* ScaleAnimation(float fromX, float toX, float fromY, float toY, float pivotX, float pivotY)
* ScaleAnimation(float fromX, float toX, float fromY, float toY, int pivotXType, float pivotXValue, int pivotYType, float pivotYValue)
* pivotXType 和 pivotYType 的选择,同样有三个取值:Animation.ABSOLUTE、Animation.RELATIVE_TO_SELF 和 Animation.RELATIVE_TO_PARENT;
3. TranslateAnimation
- xml属性:
- form/toX/YDelta:起始/结束X/Y坐标,有三种值50,50%,50%p,yu Scale同
```
</translate>
2. 代码设置:
* TranslateAnimation(float fromXDelta, float toXDelta, float fromYDelta, float toYDelta)
* TranslateAnimation(int fromXType, float fromXValue, int toXType, float toXValue, int fromYType, float fromYValue, int toYType, float toYValue)
#### 4. RotateAnimation
1. xml属性:
* form/toDegrees:开始/结束旋转的角度位置,+顺时针,-逆时针度数
* pivotX/Y:与Scale同
```
<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:fromDegrees="0"
android:toDegrees="-650"
android:pivotX="50%"
android:pivotY="50%"
android:duration="3000"
android:fillAfter="true">
</rotate>
- 代码设置:
- RotateAnimation(float fromDegrees, float toDegrees)
- RotateAnimation(float fromDegrees, float toDegrees, float pivotX, float pivotY)
- RotateAnimation(float fromDegrees, float toDegrees, int pivotXType, float pivotXValue, int pivotYType, float pivotYValue)
2. 自定义动画
1. (Tween)set动作合集
set标签extends Animation的属性。 * xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="3000"
android:fillAfter="true">
<alpha
android:fromAlpha="0.0"
android:toAlpha="1.0"/>
<scale
android:fromXScale="0.0"
android:toXScale="1.4"
android:fromYScale="0.0"
android:toYScale="1.4"
android:pivotX="50%"
android:pivotY="50%"/>
<rotate
android:fromDegrees="0"
android:toDegrees="720"
android:pivotX="50%"
android:pivotY="50%"/>
</set>
* AnimationSet(boolean shareInterpolator) shareInterpolator 取值 true 或 false,取 true 时,指在 AnimationSet 中定义一个插值器(interpolater),它下面的所有动画共同。如果设为 false,则表示它下面的动画自己定义各自的插值器。
alphaAnim = new AlphaAnimation(1.0f,0.1f);
scaleAnim = new ScaleAnimation(0.0f,1.4f,0.0f,1.4f,Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
rotateAnim = new RotateAnimation(0, 720, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
setAnim=new AnimationSet(true);
setAnim.addAnimation(alphaAnim);
setAnim.addAnimation(scaleAnim);
setAnim.addAnimation(rotateAnim);
setAnim.setDuration(3000);
setAnim.setFillAfter(true);
2. 3d旋转
转自http://www.bkjia.com/Androidjc/997572.html 类似以下动画,需自定义完成。
步骤
- extends Animation, override applyTransformation(float interpolatedTime, Transformation t), initialize两个方法。
- applyTranslation params:
- interpolatedTime: 时间的进行程度(0-1s,1s动画结束)
- transformation: 动画在不同时刻对图形和组件的变形程度。该对象封装了一个Matrix对象,translation会控制组件随着Matrix的变换而进行相应变换。
- initialize(int width, int height, int parentWidth, int parentHeight),该回调函数通知Animation目标View的大小参数。可初始化一些相关参数,如动画持续时间,interpolator,动画参考点等。
- 为了控制view进行三维变换,需要借助于Android提供的Camera类,其作用类似于Matrix,提供如下方法:
- getMatrix(Matrix matrix);将Camera所做的变换应用到制定的matrix上
- rotateX/Y/Z(float degrees);将view沿X/Y/Z轴旋转
- translate(float x, float y, float z);在三维空间进行位置变换
- applyToCanvas(Canvas canvas);将Camera所做的变换应用到Canvas上
- getLocationX/Y/Z();
- save(), restore();
- dotWithNormal();
2. 自定义Interpolator
- 以此为例LinearInterpolator extends Interpolator implements TimeInterpolator
- float getInterpolation(float input): 0-1动画在时间上的进度,return值是动画的Value进度
- 自定义:override getInterpolation()
3 源码敬上
4 自定义View的动画 postOnAnimation
public class PathView extends RelativeLayout {
private Paint mPaint = new Paint();
private Path drawingPath = new Path();
private int mPaintColor = Color.WHITE;
private int mLastRaidus = 0;
private int mCurRaidus = 20;
private PathItemView mItemView;
public PathView(Context context) {
super(context);
init();
}
private void init() {
setWillNotDraw(false);
mItemView = new PathItemView(getContext());
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.BLACK);
mPaint.setStrokeWidth(4f);
mPaint.setColor(mPaintColor);
mPaint.setStyle(Paint.Style.STROKE);
drawingPath.moveTo(10, 10);
drawingPath.lineTo(200, 140);
canvas.drawPath(drawingPath, mPaint);
setCircle(canvas);
}
private void setCircle(Canvas canvas) {
if (mCurRaidus >= mLastRaidus && mCurRaidus < 20) {
mCurRaidus++;
} else if (mCurRaidus <= mLastRaidus && mCurRaidus > 11) {
mCurRaidus--;
} else {
mLastRaidus = mCurRaidus;
}
mPaint.setStrokeWidth(3f);
mPaint.setStyle(Paint.Style.FILL);
mPaint.setColor(Color.WHITE);
mPaint.setAlpha(255);
canvas.drawCircle(200, 200, mCurRaidus, mPaint);
postOnAnimation(new Runnable() {
@override
public void run() {
postInvalidate();
}});
}
}