定义
属性设置
属性动画和View Animation(补间动画)的区别
Animator类
ValueAnimator
ValueAnimator animation = ValueAnimator.ofFloat(0f, 1f);
animation.setDuration(
1000
);
animation.start();
|
ValueAnimator animation = ValueAnimator.ofObject(
new
MyTypeEvaluator(), startPropertyValue, endPropertyValue);
animation.setDuration(
1000
);
animation.start();
|
实例
<
RelativeLayout
xmlns:android
=
"<a href="http://schemas.android.com/apk/res/android" "="" style="text-decoration: none; color: blue !important; border-top-left-radius: 0px !important; border-top-right-radius: 0px !important; border-bottom-right-radius: 0px !important; border-bottom-left-radius: 0px !important; border: 0px !important; bottom: auto !important; float: none !important; height: auto !important; left: auto !important; margin: 0px !important; outline: 0px !important; overflow: visible !important; padding: 0px !important; position: static !important; right: auto !important; top: auto !important; vertical-align: baseline !important; width: auto !important; box-sizing: content-box !important; background-image: none !important; background-position: initial initial !important; background-repeat: initial initial !important;">http://schemas.android.com/apk/res/android"
xmlns:tools
=
"<a href="http://schemas.android.com/tools" "="" style="text-decoration: none; color: blue !important; border-top-left-radius: 0px !important; border-top-right-radius: 0px !important; border-bottom-right-radius: 0px !important; border-bottom-left-radius: 0px !important; border: 0px !important; bottom: auto !important; float: none !important; height: auto !important; left: auto !important; margin: 0px !important; outline: 0px !important; overflow: visible !important; padding: 0px !important; position: static !important; right: auto !important; top: auto !important; vertical-align: baseline !important; width: auto !important; box-sizing: content-box !important; background-image: none !important; background-position: initial initial !important; background-repeat: initial initial !important;">http://schemas.android.com/tools"
android:layout_width
=
"match_parent"
android:layout_height
=
"match_parent"
tools:context
=
".MainActivity"
>
<
FrameLayout
android:id
=
"@+id/container"
android:layout_width
=
"match_parent"
android:layout_height
=
"match_parent"
android:layout_alignParentTop
=
"true"
/>
<
LinearLayout
android:id
=
"@+id/controlBtns"
android:layout_width
=
"match_parent"
android:layout_height
=
"wrap_content"
android:layout_alignParentBottom
=
"true"
android:orientation
=
"horizontal"
>
<
Button
android:id
=
"@+id/btnStart"
android:layout_width
=
"wrap_content"
android:layout_height
=
"wrap_content"
android:text
=
"Start"
/>
<
Button
android:id
=
"@+id/btnReset"
android:layout_width
=
"wrap_content"
android:layout_height
=
"wrap_content"
android:text
=
"Reset"
/>
</
LinearLayout
>
<
ImageView
android:id
=
"@+id/image"
android:layout_width
=
"50dp"
android:layout_height
=
"50dp"
android:layout_above
=
"@+id/controlBtns"
android:src
=
"#ffff4444"
/>
</
RelativeLayout
>
|
MainActivty类
import
android.os.Bundle;
import
android.support.v7.app.AppCompatActivity;
import
android.view.View;
import
android.widget.ImageView;
import
com.nineoldandroids.animation.PropertyValuesHolder;
import
com.nineoldandroids.animation.ValueAnimator;
import
com.nineoldandroids.view.animation.AnimatorProxy;
public
class
MainActivity
extends
AppCompatActivity
implements
ValueAnimator.AnimatorUpdateListener {
private
ImageView imageView;
private
View container;
private
AnimatorProxy animatorProxy;
@Override
protected
void
onCreate(Bundle savedInstanceState) {
super
.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imageView = (ImageView)
this
.findViewById(R.id.image);
container = findViewById(R.id.container);
animatorProxy = AnimatorProxy.wrap(imageView);
final
float
originX = animatorProxy.getX();
final
float
originY = animatorProxy.getY();
findViewById(R.id.btnStart).setOnClickListener(
new
View.OnClickListener() {
@Override
public
void
onClick(View v) {
PropertyValuesHolder widthPropertyHolder = PropertyValuesHolder.ofFloat(
"posX"
, animatorProxy.getX(), container.getWidth() - imageView.getWidth());
PropertyValuesHolder heightPropertyHolder = PropertyValuesHolder.ofFloat(
"posY"
, animatorProxy.getY(),
0
);
ValueAnimator translationAnimator = ValueAnimator.ofPropertyValuesHolder(widthPropertyHolder, heightPropertyHolder);
translationAnimator.addUpdateListener(MainActivity.
this
);
translationAnimator.setDuration(
1000
);
translationAnimator.start();
findViewById(R.id.btnStart).setEnabled(
false
);
}
});
findViewById(R.id.btnReset).setOnClickListener(
new
View.OnClickListener() {
@Override
public
void
onClick(View v) {
findViewById(R.id.btnStart).setEnabled(
true
);
animatorProxy.setX(originX);
animatorProxy.setY(originY);
}
});
}
@Override
public
void
onAnimationUpdate(ValueAnimator animation) {
float
posX = (Float) animation.getAnimatedValue();
float
posY = (Float) animation.getAnimatedValue();
animatorProxy.setX(posX);
animatorProxy.setY(posY);
}
}
|
运行的效果如下:
我们还可以自己创建一个对象来封装动画的变化值,如下所示
修改MainActivity的代码如下:
import
android.os.Bundle;
import
android.support.v7.app.AppCompatActivity;
import
android.util.Log;
import
android.view.View;
import
android.widget.ImageView;
import
com.nineoldandroids.animation.TypeEvaluator;
import
com.nineoldandroids.animation.ValueAnimator;
import
com.nineoldandroids.view.animation.AnimatorProxy;
public
class
MainActivity
extends
AppCompatActivity
implements
ValueAnimator.AnimatorUpdateListener {
private
ImageView imageView;
private
View container;
private
AnimatorProxy animatorProxy;
float
originX, originY;
@Override
protected
void
onCreate(Bundle savedInstanceState) {
super
.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imageView = (ImageView)
this
.findViewById(R.id.image);
container = findViewById(R.id.container);
animatorProxy = AnimatorProxy.wrap(imageView);
findViewById(R.id.btnStart).setOnClickListener(
new
View.OnClickListener() {
@Override
public
void
onClick(View v) {
originX = animatorProxy.getX();
originY = animatorProxy.getY();
ValueAnimator translationAnimator = ValueAnimator.ofObject(
new
PositionTypeEvaluator(),
new
Position(originX, originY),
new
Position(container.getWidth() - imageView.getWidth(),
0
));
translationAnimator.addUpdateListener(MainActivity.
this
);
translationAnimator.setDuration(
1000
);
translationAnimator.start();
findViewById(R.id.btnStart).setEnabled(
false
);
}
});
findViewById(R.id.btnReset).setOnClickListener(
new
View.OnClickListener() {
@Override
public
void
onClick(View v) {
findViewById(R.id.btnStart).setEnabled(
true
);
animatorProxy.setX(originX);
animatorProxy.setY(originY);
}
});
}
@Override
public
void
onAnimationUpdate(ValueAnimator animation) {
Position currentPos = (Position) animation.getAnimatedValue();
animatorProxy.setX(currentPos.getPosX());
animatorProxy.setY(currentPos.getPosY());
}
private
class
PositionTypeEvaluator
implements
TypeEvaluator<Position> {
@Override
public
Position evaluate(
float
fraction, Position startValue, Position endValue) {
Log.d(
"postion"
,
"fraction"
+fraction);
float
posX = startValue.getPosX() + (endValue.getPosX() - startValue.getPosX()) * fraction;
float
posY = startValue.getPosY() + (endValue.getPosY() - startValue.getPosY()) * fraction;
return
new
Position(posX, posY);
}
}
private
class
Position {
private
float
posX;
private
float
posY;
public
Position(
float
posX,
float
posY) {
this
.posX = posX;
this
.posY = posY;
}
public
float
getPosX() {
return
posX;
}
public
void
setPosX(
float
posX) {
this
.posX = posX;
}
public
float
getPosY() {
return
posY;
}
public
void
setPosY(
float
posY) {
this
.posY = posY;
}
}
}
|
运行结果类似上图
那fraction是什么呢?截了个图。从0到1的小数。
fraction The elapsed, interpolated fraction of the animation.
这个应该动画过程中的插值小数。
ObjectAnimator
A subclass of ValueAnimator
that allows you to set a target object and object property to animate. This class updates the property accordingly when it computes a new value for the animation. You want to use ObjectAnimator
most of the time, because it makes the process of animating values on target objects much easier. However, you sometimes want to use ValueAnimator
directly becauseObjectAnimator
has a few more restrictions, such as requiring specific acessor methods to be present on the target object.
ObjectAnimator是ValueAnimator的子类,可以改变对象的属性值完成动画。我更愿意使用ObjectAnimator,因为它设置对象的动画更为简单。有时候你可能还得使用ValueAnimator,因为ObjectAnimator有一些限制,比如需要特定的方法展示在指定的对象上。
使用ObjectAnimator实现动画方式有两种,一种是XML,一种用JAVA代码实现。
就我自己使用而言,XML代码实现的动画,格式更清晰,一目了然就知道动画中包含哪些步骤,其次XML格式的不支持9和9以下的版本。Java代码实现,更容易做一些动态的处理,比如你的属性动画和屏幕的大小有直接的关系。动画的大小和比例需要根据运行屏幕的大小来动态计算,这个时候就推荐使用Java代码来实现。
其中的AnimatorSet提供组合动画能力的类。并可设置组中动画的时序关系,如同时播放、有序播放或延迟播放。
实例
XML方式实现
我们先用XML的方式实现一个动画。新建一个anim/animation.xml文件,表示动画的内容
<
set
xmlns:android
=
"<a href="http://schemas.android.com/apk/res/android" "="" style="text-decoration: none; color: blue !important; border-top-left-radius: 0px !important; border-top-right-radius: 0px !important; border-bottom-right-radius: 0px !important; border-bottom-left-radius: 0px !important; border: 0px !important; bottom: auto !important; float: none !important; height: auto !important; left: auto !important; margin: 0px !important; outline: 0px !important; overflow: visible !important; padding: 0px !important; position: static !important; right: auto !important; top: auto !important; vertical-align: baseline !important; width: auto !important; box-sizing: content-box !important; background-image: none !important; background-position: initial initial !important; background-repeat: initial initial !important;">http://schemas.android.com/apk/res/android"
android:ordering
=
"sequentially"
>
<
set
android:ordering
=
"together"
>
<
objectAnimator
android:duration
=
"500"
android:propertyName
=
"scaleX"
android:valueTo
=
"1.05f"
/>
<
objectAnimator
android:duration
=
"500"
android:propertyName
=
"scaleY"
android:valueTo
=
"1.05f"
/>
<
objectAnimator
android:duration
=
"500"
android:propertyName
=
"translationY"
android:valueTo
=
"100"
/>
</
set
>
<
set
android:ordering
=
"together"
>
<
objectAnimator
android:duration
=
"500"
android:propertyName
=
"scaleX"
android:valueFrom
=
"1.05f"
android:valueTo
=
"0.3f"
/>
<
objectAnimator
android:duration
=
"500"
android:propertyName
=
"scaleY"
android:valueFrom
=
"1.05f"
android:valueTo
=
"0.3f"
/>
<
objectAnimator
android:duration
=
"500"
android:propertyName
=
"translationY"
android:valueTo
=
"-100"
/>
</
set
>
</
set
>
|
通过xml文件很好分析动画的步骤。主要分为两个环节,一个是扩大下移的过程,一个是缩小上移的过程。
布局anim.xml文件如下所示
<
LinearLayout
xmlns:android
=
"<a href="http://schemas.android.com/apk/res/android" "="" style="text-decoration: none; color: blue !important; border-top-left-radius: 0px !important; border-top-right-radius: 0px !important; border-bottom-right-radius: 0px !important; border-bottom-left-radius: 0px !important; border: 0px !important; bottom: auto !important; float: none !important; height: auto !important; left: auto !important; margin: 0px !important; outline: 0px !important; overflow: visible !important; padding: 0px !important; position: static !important; right: auto !important; top: auto !important; vertical-align: baseline !important; width: auto !important; box-sizing: content-box !important; background-image: none !important; background-position: initial initial !important; background-repeat: initial initial !important;">http://schemas.android.com/apk/res/android"
android:layout_width
=
"match_parent"
android:layout_height
=
"match_parent"
android:gravity
=
"center"
android:orientation
=
"vertical"
>
<
FrameLayout
android:id
=
"@+id/layout"
android:layout_width
=
"match_parent"
android:layout_height
=
"150dp"
android:layout_marginLeft
=
"16dp"
android:layout_marginRight
=
"16dp"
android:background
=
"@android:color/white"
/>
<
Button
android:id
=
"@+id/click"
android:layout_width
=
"wrap_content"
android:layout_height
=
"wrap_content"
android:text
=
"start"
/>
</
LinearLayout
>
|
主类代码如下所示:
import
android.animation.AnimatorInflater;
import
android.animation.AnimatorSet;
import
android.os.Bundle;
import
android.support.v7.app.AppCompatActivity;
import
android.view.View;
import
android.widget.Button;
public
class
MainActivity
extends
AppCompatActivity
implements
View.OnClickListener {
private
View view;
private
Button button;
@Override
protected
void
onCreate(Bundle savedInstanceState) {
super
.onCreate(savedInstanceState);
setContentView(R.layout.anim);
view = findViewById(R.id.layout);
button = (Button) findViewById(R.id.click);
button.setOnClickListener(
this
);
}
@Override
public
void
onClick(View v) {
if
(v.getId() == R.id.click) {
AnimatorSet animatorSet = (AnimatorSet) AnimatorInflater.loadAnimator(
this
, R.anim.animation);
animatorSet.setTarget(view);
animatorSet.start();
}
}
}
|
动画效果如下:
Java代码实现(动态计算,缩小到固定的大小)
@Override
public
void
onClick(View v) {
if
(v.getId() == R.id.click) {
final
int
width=
97
;
final
int
height=
58
;
float
scaleTo=
1
.05f;
Util util=
new
Util();
float
widthRatio = util.dp2px(
this
, width) / (view.getWidth() * scaleTo);
float
heightRatio = util.dp2px(
this
, height) / (view.getHeight() * scaleTo);
AnimatorSet animatorSet =
new
AnimatorSet();
//放大并下移
Animator animator = zoomAndTranslateAnimation(view,
1
, scaleTo,
1
, scaleTo,
100
);
animator.setDuration(
500
);
//缩小并上移
Animator animator1 = zoomAndTranslateAnimation(view, scaleTo, widthRatio, scaleTo, heightRatio, -
100
);
animator1.setDuration(
500
);
animatorSet.playSequentially(animator, animator1);
animatorSet.start();
}
}
public
Animator zoomAndTranslateAnimation(View view,
float
xScaleValueFrom,
float
xScaleValueTo,
float
yScaleValueFrom,
float
yScaleValueTo,
float
translation) {
PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat(
"scaleX"
, xScaleValueFrom, xScaleValueTo);
PropertyValuesHolder scaleY = PropertyValuesHolder.ofFloat(
"scaleY"
, yScaleValueFrom, yScaleValueTo);
PropertyValuesHolder translationY = PropertyValuesHolder.ofFloat(
"translationY"
, translation);
ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(view, scaleX, scaleY, translationY);
return
animator;
}
class
Util {
public
int
dp2px(Context context,
int
dp) {
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
return
(
int
) (dp * metrics.density);
}
}
}
|
另外,还可以通过AnimatorSet.Builder的方式将要播放的动画联系在一起。
AnimatorSet animatorSet =
new
AnimatorSet();
//放大并下移
Animator animator = zoomAndTranslateAnimation(view,
1
, scaleTo,
1
, scaleTo,
100
);
animator.setDuration(
500
);
AnimatorSet.Builder builder=animatorSet.play(animator);
Aniamtor animator1=........
.......
....
builder.with(animator1);
|
参考链接:https://github.com/android-cn/android-open-project-analysis/blob/master/tech/animation/README.md(非常好的总结,推荐阅读)