在App中合理地使用動畫能夠獲得友好愉悅的用戶體驗悠就,Android中的動畫有View動畫千绪、屬性動畫、幀動畫梗脾、布局動畫荸型、轉(zhuǎn)場動畫等,在5.x以后有又新增了矢量動畫炸茧,這些動畫在平常開發(fā)中使用較為普遍瑞妇,所以有必要做一次完整的總結(jié)。
一梭冠、View動畫
View動畫定義了漸變Alpha辕狰、旋轉(zhuǎn)Rotate、縮放Scale控漠、平移Translate四種基本動畫蔓倍,并且通過這四種基本動畫的組合使用悬钳,可以實現(xiàn)多種交互效果。
View動畫使用非常簡單偶翅,不僅可以通過XML文件來定義動畫默勾,同樣可以通過Java代碼來實現(xiàn)動畫過程。
1.Xml文件定義View動畫
通過xml來定義View動畫涉及到一些公有的屬性(在AndroidStudio上不能提示):
android:duration 動畫持續(xù)時間
android:fillAfter 為true動畫結(jié)束時聚谁,View將保持動畫結(jié)束時的狀態(tài)
android:fillBefore 為true動畫結(jié)束時母剥,View將還原到開始開始時的狀態(tài)
android:repeatCount 動畫重復執(zhí)行的次數(shù)
android:repeatMode 動畫重復模式 ,重復播放時restart重頭開始形导,reverse重復播放時倒敘回放环疼,該屬性需要和android:repeatCount一起使用
android:interpolator 插值器,相當于變速器朵耕,改變動畫的不同階段的執(zhí)行速度
這些屬性是從Animation中繼承下來的炫隶,在alpha
、rotate
憔披、scale
等限、translate
標簽中都可以直接使用。
利用xml文件定義View動畫需要在工程的res目錄下創(chuàng)建anim文件夾芬膝,所有的xml定義的View動畫都要放在anim目錄下望门。
漸變view_anim_alpha.xml
:
<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="2000"
android:fromAlpha="1.0"
android:toAlpha="0">
</alpha>
旋轉(zhuǎn)view_anim_rotate.xml
:
<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="2000"
android:fillAfter="true"
android:fromDegrees="0"
android:pivotX="50%"
android:pivotY="50%"
android:toDegrees="360">
</rotate>
縮放view_anim_scale.xml
:
<?xml version="1.0" encoding="utf-8"?>
<scale xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="2000"
android:fromXScale="1.0"
android:fromYScale="1.0"
android:pivotX="50%"
android:pivotY="50%"
android:toXScale="0.5"
android:toYScale="0.5">
</scale>
平移view_anim_translate.xml
:
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="2000"
android:fromXDelta="0"
android:fromYDelta="0"
android:toXDelta="100%"
android:toYDelta="100%">
</translate>
rotate
、scale
動畫的android:pivotX
和android:pivotY
屬性锰霜、translate
動畫的android:toXDelta
和android:toYDelta
屬性的取值都可以是都可以數(shù)值筹误、百分數(shù)、百分數(shù)p
癣缅,比如:50
厨剪、50%
、50%p
友存,他們?nèi)≈档拇淼囊饬x各不相同:
50
表示以View左上角為原點沿坐標軸正方向(x
軸向右祷膳,y
軸向下)偏移50px
的位置;
50%
表示以View左上角為原點沿坐標軸正方向(x
軸向右屡立,y
軸向下)偏移View寬度或高度的50%處的位置直晨;
50%p
表示以View左上角為原點沿坐標軸正方向(x
軸向右,y
軸向下)偏移父控件寬度或高度的50%處的位置(p
表示相對于ParentView
的位置)膨俐。
通過定義xml動畫資源文件勇皇,在Activity中調(diào)用:
public void clickToAlpha(View view) {
Animation alphaAnim = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.view_anim_alpha);
mTargetView.startAnimation(alphaAnim);
}
public void clickToRotate(View view) {
Animation rotateAnim = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.view_anim_rotate);
mTargetView.startAnimation(rotateAnim);
}
public void clickToScale(View view) {
Animation scaleAnim = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.view_anim_scale);
mTargetView.startAnimation(scaleAnim);
}
public void clickToTranslate(View view) {
Animation translateAnim = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.view_anim_translate);
mTargetView.startAnimation(translateAnim);
}
public void clickToSet(View view) {
Animation setAnim = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.view_anim_set);
mTargetView.startAnimation(setAnim);
}
2.Java代碼實現(xiàn)View動畫
在平常的業(yè)務(wù)邏輯中也可以直接用Java代碼來實現(xiàn)Veiw動畫,Android系統(tǒng)給我們提供了AlphaAnimation
焚刺、RotateAnimation
敛摘、ScaleAnimation
、TranslateAnimation
四個動畫類分別來實現(xiàn)View的漸變乳愉、旋轉(zhuǎn)兄淫、縮放屯远、平移動畫。
漸變:
public void clickToAlpha(View view) {
AlphaAnimation alphaAnimation = new AlphaAnimation(1, 0);
alphaAnimation.setDuration(2000);
mTargetView.startAnimation(alphaAnimation);
}
旋轉(zhuǎn):
public void clickToRotate(View view) {
RotateAnimation rotateAnimation = new RotateAnimation(
0, 360,
Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f);
rotateAnimation.setDuration(2000);
mTargetView.startAnimation(rotateAnimation);
}
縮放:
public void clickToScale(View view) {
ScaleAnimation scaleAnimation = new ScaleAnimation(
1, 0.5f,
1, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f);
scaleAnimation.setDuration(2000);
mTargetView.startAnimation(scaleAnimation);
}
平移:
public void clickToTranslate(View view) {
TranslateAnimation translateAnimation = new TranslateAnimation(
Animation.RELATIVE_TO_SELF, 0,
Animation.RELATIVE_TO_SELF, 1,
Animation.RELATIVE_TO_SELF, 0,
Animation.RELATIVE_TO_SELF, 1);
translateAnimation.setDuration(2000);
mTargetView.startAnimation(translateAnimation);
}
組合:
public void clickToSet(View view) {
AlphaAnimation alphaAnimation = new AlphaAnimation(1, 0);
alphaAnimation.setDuration(2000);
RotateAnimation rotateAnimation = new RotateAnimation(
0, 360,
Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f);
rotateAnimation.setDuration(2000);
ScaleAnimation scaleAnimation = new ScaleAnimation(
1, 0.5f,
1, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f);
scaleAnimation.setDuration(2000);
TranslateAnimation translateAnimation = new TranslateAnimation(
Animation.RELATIVE_TO_SELF, 0,
Animation.RELATIVE_TO_SELF, 1,
Animation.RELATIVE_TO_SELF, 0,
Animation.RELATIVE_TO_SELF, 1);
translateAnimation.setDuration(2000);
AnimationSet animationSet = new AnimationSet(true);
animationSet.addAnimation(alphaAnimation);
animationSet.addAnimation(rotateAnimation);
animationSet.addAnimation(scaleAnimation);
animationSet.addAnimation(translateAnimation);
mTargetView.startAnimation(animationSet);
}
View動畫可以設(shè)置一個動畫執(zhí)行的監(jiān)聽器:
animation.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
// 動畫開始
}
@Override
public void onAnimationEnd(Animation animation) {
// 動畫結(jié)束
}
@Override
public void onAnimationRepeat(Animation animation) {
//動畫重復
}
});
通過設(shè)置監(jiān)聽器可以在動畫執(zhí)行的開始拖叙、結(jié)束氓润、重復時做一些其他的業(yè)務(wù)邏輯。
二薯鳍、屬性動畫
所謂屬性動畫,就是改變對象Object
的屬性來實現(xiàn)動畫過程挨措。屬性動畫是對View的動畫的擴展,通過它可以實現(xiàn)更多漂亮的動畫效果浅役。同時屬性動畫的作用對象不僅僅是View斩松,任何對象都可以。
屬性動畫的作用效果就是:在一個指定的時間段內(nèi)將對象的一個屬性的屬性值動態(tài)地變化到另一個屬性值觉既。
1.ObjectAnimator
ObjectAnimator
是最常用的屬性動畫執(zhí)行類惧盹。
private void startJavaPropertyAnimator() {
ObjectAnimator
.ofFloat(mImageView, "rotationY", 0f, 360f)
.setDuration(2000)
.start();
}
上面的代碼就是通過ObjectAnimator
在2000ms內(nèi)將mImageView
的rotationY
屬性的屬性值從0f
變化的360f
。
ObjectAnimtor
可以用ofInt
瞪讼、ofFloat
钧椰、ofObject
等靜態(tài)方法,傳入動畫作用的目標Object
符欠、屬性字段嫡霞、屬性開始值、屬性中間值希柿、屬性結(jié)束值等參數(shù)來構(gòu)造動畫對象诊沪。在動畫更新的過程中,通過不斷去調(diào)用對象屬性的
setter
方法改變屬性值曾撤,不斷重繪實現(xiàn)動畫過程端姚。如果沒有給定動畫開始屬性值,那么系統(tǒng)會通過反射去獲取Object
對象的初始值作為動畫的開始值挤悉。屬性動畫也同樣可以通過xml文件來定義渐裸,同樣在工程的res目錄下創(chuàng)建animator文件夾,xml文件定義的
objectAnimator
動畫要放在該文件夾下尖啡。
property_animator.xml
:
<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="2000"
android:propertyName="rotationY"
android:valueFrom="0"
android:valueTo="360"
android:valueType="floatType">
</objectAnimator>
Java代碼調(diào)用:
private void startXmlPropertyAnimator() {
Animator animator = AnimatorInflater.loadAnimator(getApplicationContext(),
R.animator.property_animator);
animator.setTarget(mImageView);
animator.start();
}
最終效果如上圖橄仆。
屬性動畫也同樣可以組合使用,通過AnimatorSet
類和xml文件的set
標簽都可以同時改變對象的多個屬性衅斩,實現(xiàn)更加豐富的動畫效果盆顾。
通過AnimatorSet
創(chuàng)建動畫集:
private void startJavaPropertyAnimatorSet() {
Animator scaleXAnimator = ObjectAnimator.ofFloat(mImageView, "scaleX", 1, 0.5f);
scaleXAnimator.setDuration(2000);
Animator scaleYAnimator = ObjectAnimator.ofFloat(mImageView, "scaleY", 1, 0.5f);
scaleYAnimator.setDuration(2000);
Animator rotationXAnimator = ObjectAnimator.ofFloat(mImageView, "rotationX", 0, 360);
rotationXAnimator.setDuration(2000);
Animator rotationYAnimator = ObjectAnimator.ofFloat(mImageView, "rotationY", 0, 360);
rotationYAnimator.setDuration(2000);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.play(scaleXAnimator)
.with(scaleYAnimator)
.before(rotationXAnimator)
.after(rotationYAnimator);
animatorSet.start();
}
AnimatorSet
通過before
、with
畏梆、after
三個方法可以組合多個屬性動畫您宪,with
表示與給定動畫同時執(zhí)行奈懒,before
在給定動畫執(zhí)行之前執(zhí)行,after
表示在給定動畫執(zhí)行之后執(zhí)行宪巨。
通過xml文件定義屬性動畫集:
property_animator_set.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:duration="2000"
android:propertyName="scaleX"
android:valueFrom="1"
android:valueTo="0.5"
android:valueType="floatType"/>
<objectAnimator
android:duration="2000"
android:propertyName="scaleY"
android:valueFrom="1"
android:valueTo="0.5"
android:valueType="floatType"/>
<objectAnimator
android:duration="2000"
android:propertyName="alpha"
android:valueFrom="1"
android:valueTo="0.5"
android:valueType="floatType"/>
</set>
在Java代碼中調(diào)用屬性動畫集:
private void startxmlPropertyAnimatorSet() {
Animator animator = AnimatorInflater.loadAnimator(getApplicationContext(),
R.animator.property_animator_set);
animator.setTarget(mImageView);
animator.start();
}
同樣磷杏,屬性動畫也可以添加動畫執(zhí)行監(jiān)聽器:
animator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
// 動畫開始
}
@Override
public void onAnimationEnd(Animator animation) {
// 動畫結(jié)束
}
@Override
public void onAnimationCancel(Animator animation) {
// 動畫取消
}
@Override
public void onAnimationRepeat(Animator animation) {
// 動畫重復
}
});
在監(jiān)聽到屬性動畫開始、結(jié)束捏卓、取消极祸、重復時可以去做一些其他的邏輯業(yè)務(wù)。
2.ValueAnimator
ValueAnimator
是ObjectAnimator
的父類怠晴,它繼承自Animator
遥金。ValueAnimaotor
同樣提供了ofInt
、ofFloat
蒜田、ofObject
等靜態(tài)方法稿械,傳入的參數(shù)是動畫過程的開始值、中間值冲粤、結(jié)束值來構(gòu)造動畫對象美莫。可以將ValueAnimator
看著一個值變化器梯捕,即在給定的時間內(nèi)將一個目標值從給定的開始值變化到給定的結(jié)束值厢呵。在使用ValueAnimator
時通常需要添加一個動畫更新的監(jiān)聽器,在監(jiān)聽器中能夠獲取到執(zhí)行過程中的每一個動畫值科阎。
private void startValueAnimator() {
ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1);
valueAnimator.setDuration(300);
valueAnimator.start();
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
// 動畫更新過程中的動畫值述吸,可以根據(jù)動畫值的變化來關(guān)聯(lián)對象的屬性,實現(xiàn)屬性動畫
float value = (float) animation.getAnimatedValue();
Log.d("ValueAnimator", "動畫值:" + value);
}
});
}
在300ms內(nèi)將數(shù)值0變化到1的動畫值的變化log:
02-25 23:16:57.586 D/ValueAnimator: 動畫值:0.0
02-25 23:16:57.596 D/ValueAnimator: 動畫值:0.007902175
02-25 23:16:57.616 D/ValueAnimator: 動畫值:0.029559612
02-25 23:16:57.636 D/ValueAnimator: 動畫值:0.066987276
02-25 23:16:57.646 D/ValueAnimator: 動畫值:0.118102014
02-25 23:16:57.666 D/ValueAnimator: 動畫值:0.18128797
02-25 23:16:57.686 D/ValueAnimator: 動畫值:0.2545482
02-25 23:16:57.706 D/ValueAnimator: 動畫值:0.33063102
02-25 23:16:57.716 D/ValueAnimator: 動畫值:0.4166157
02-25 23:16:57.736 D/ValueAnimator: 動畫值:0.5052359
02-25 23:16:57.746 D/ValueAnimator: 動畫值:0.5936906
02-25 23:16:57.766 D/ValueAnimator: 動畫值:0.67918396
02-25 23:16:57.786 D/ValueAnimator: 動畫值:0.7545208
02-25 23:16:57.796 D/ValueAnimator: 動畫值:0.82671034
02-25 23:16:57.826 D/ValueAnimator: 動畫值:0.88857293
02-25 23:16:57.836 D/ValueAnimator: 動畫值:0.93815327
02-25 23:16:57.856 D/ValueAnimator: 動畫值:0.9721882
02-25 23:16:57.876 D/ValueAnimator: 動畫值:0.99384415
02-25 23:16:57.886 D/ValueAnimator: 動畫值:1.0
ValueAnimator
的使用一般會結(jié)合更新監(jiān)聽器AnimatorUpdateListener
锣笨,大多數(shù)時候是在自定義控件時使用蝌矛。
下面是用ValueAnimator
自定義控件實現(xiàn)動畫打開關(guān)閉效果。
expanded_veiw.xml
:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white"
android:divider="@drawable/divider"
android:orientation="vertical"
android:showDividers="middle">
<LinearLayout
android:id="@+id/ll_expanded_question"
android:layout_width="match_parent"
android:layout_height="48dp"
android:gravity="center_vertical">
<TextView
android:id="@+id/tv_expanded_question"
android:layout_width="0dp"
android:layout_height="48dp"
android:layout_weight="1"
android:gravity="center_vertical"
android:padding="8dp"
android:text="如何把一本很難的書看懂错英?"
android:textColor="#999999"
android:textSize="16sp"/>
<ImageView
android:id="@+id/iv_expanded_indicator"
android:layout_width="16dp"
android:layout_height="16dp"
android:layout_marginRight="16dp"
android:src="@drawable/img_up"/>
</LinearLayout>
<TextView
android:id="@+id/tv_expanded_answer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="8dp"
android:text="多讀幾遍入撒。真的特別有用。至少看三遍椭岩。從開頭看茅逮,看到中間,重頭再來判哥,再看得多一點献雅,在從新開始,建議看到快結(jié)束時再從新開始塌计。"
android:textColor="#999999"
android:textSize="16sp"/>
</LinearLayout>
public class ExpandedView extends FrameLayout {
private TextView mTvAnswer;
private boolean isClosed;
private ImageView mIvIndicator;
public ExpandedView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
private void init(Context context) {
View view = LayoutInflater.from(context).inflate(R.layout.expanded_view, this, true);
LinearLayout llQuestion = (LinearLayout) view.findViewById(R.id.ll_expanded_question);
llQuestion.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
anim();
}
});
mTvAnswer = (TextView) view.findViewById(R.id.tv_expanded_answer);
mIvIndicator = (ImageView) view.findViewById(R.id.iv_expanded_indicator);
}
private void anim() {
// 指示器旋轉(zhuǎn)
ValueAnimator valueAnimator1 = isClosed
? ValueAnimator.ofFloat(180, 0)
: ValueAnimator.ofFloat(0, 180);
valueAnimator1.setDuration(500);
valueAnimator1.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float value = (float) animation.getAnimatedValue();
mIvIndicator.setRotation(value);
}
});
valueAnimator1.start();
// 打開開關(guān)閉操作
final int answerHeight = mTvAnswer.getMeasuredHeight();
ValueAnimator valueAnimator2 = isClosed
? ValueAnimator.ofInt(-answerHeight, 0)
: ValueAnimator.ofInt(0, -answerHeight);
valueAnimator2.setDuration(500);
final MarginLayoutParams params = (MarginLayoutParams) mTvAnswer.getLayoutParams();
valueAnimator2.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int value = (int) animation.getAnimatedValue();
params.bottomMargin = value;
mTvAnswer.setLayoutParams(params);
}
});
valueAnimator2.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
isClosed = !isClosed;
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
valueAnimator2.start();
}
}
3.TypeEvaluator
ObjectAnimator
和ValueAnimator
都有ofObject
方法挺身,傳入的都有一個TypeEvaluator
類型的參數(shù)。TypeEvaluator
是一個接口锌仅,里面也只有一個抽象方法:
public T evaluate(float fraction, T startValue, T endValue);
再看ofInt
方法中沒有傳入該參數(shù)章钾,但實際上調(diào)用ofInt
方法時墙贱,系統(tǒng)已經(jīng)有實現(xiàn)了TypeEvaluator
接口的IntEvaluator
,它的源碼也非常簡單:
public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
int startInt = startValue;
return (int)(startInt + fraction * (endValue - startInt));
}
fraction
范圍為0到1贱傀,表示動畫執(zhí)行過程中已完成程度惨撇。
泛型T
即為動畫執(zhí)行的屬性類型。
所以我們要用屬性動畫來執(zhí)行復雜對象的動畫過程府寒,就需要自定義TypeEvaluator
魁衙,實現(xiàn)動畫邏輯。
先來定義一個對象
public class Circle {
private int raduis; // 半徑
private int color; // 顏色
private int elevation; // 高度
public Circle(int raduis, int color, int elevation) {
this.raduis = raduis;
this.color = color;
this.elevation = elevation;
}
public int getRaduis() {
return raduis;
}
public void setRaduis(int raduis) {
this.raduis = raduis;
}
public int getColor() {
return color;
}
public void setColor(int color) {
this.color = color;
}
public int getElevation() {
return elevation;
}
public void setElevation(int elevation) {
this.elevation = elevation;
}
}
CircleEvaluator
椰棘,實現(xiàn)TypeEvaluator
接口:
public class CircleEvaluator implements TypeEvaluator<Circle> {
@Override
public Circle evaluate(float fraction, Circle startValue, Circle endValue) {
int startRaduis = startValue.getRaduis();
int endRaduis = endValue.getRaduis();
int raduis = (int) (startRaduis + fraction * (endRaduis - startRaduis));
int startColor = startValue.getColor();
int endColor = endValue.getColor();
int startColorRed = Color.red(startColor);
int startColorGreen = Color.green(startColor);
int startColorBlue = Color.blue(startColor);
int endColorRed = Color.red(endColor);
int endColorGreen = Color.green(endColor);
int endColorBlue = Color.blue(endColor);
int colorRed = (int) (startColorRed + fraction * (endColorRed - startColorRed));
int colorGreen = (int) (startColorGreen + fraction * (endColorGreen - startColorGreen));
int colorBlue = (int) (startColorBlue + fraction * (endColorBlue - startColorBlue));
int color = Color.rgb(colorRed, colorGreen, colorBlue);
int startElevation = startValue.getElevation();
int endElevation = endValue.getElevation();
int elevation = (int) (startElevation + fraction * (endElevation - startElevation));
return new Circle(raduis, color, elevation);
}
}
自定義控件CircleView纺棺,將Circle作為它的一個屬性:
public class CircleView extends View {
private Circle circle;
private Paint mPaint;
public CircleView(Context context, AttributeSet attrs) {
super(context, attrs);
circle = new Circle(168, Color.RED, 0);
mPaint = new Paint();
mPaint.setAntiAlias(true);
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
setElevation(circle.getElevation());
mPaint.setColor(circle.getColor());
canvas.drawCircle(getMeasuredHeight() / 2, getMeasuredHeight() / 2, circle.getRaduis(), mPaint);
}
public void setCircle(Circle circle) {
this.circle = circle;
postInvalidate();
}
public Circle getCircle() {
return circle;
}
}
ObjectAnimator
使用:
private void start1() {
Circle startCircle = new Circle(168, Color.RED, 0);
Circle middleCircle = new Circle(300, Color.GREEN, 15);
Circle endCircle = new Circle(450, Color.BLUE, 30);
ObjectAnimator.ofObject(mCircleView, "circle", new CircleEvaluator(), startCircle, middleCircle, endCircle)
.setDuration(5000)
.start();
}
ValueAnimator
使用:
private void start2() {
Circle startCircle = new Circle(168, Color.RED, 0);
Circle middleCircle = new Circle(300, Color.GREEN, 15);
Circle endCircle = new Circle(450, Color.BLUE, 30);
ValueAnimator valueAnimator = ValueAnimator.ofObject(new CircleEvaluator(), startCircle, middleCircle, endCircle);
valueAnimator.setDuration(5000);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
Circle circle = (Circle) animation.getAnimatedValue();
mCircleView.setCircle(circle);
}
});
valueAnimator.start();
}
需要注意的是,系統(tǒng)調(diào)用獲取控件setter邪狞、getter方法是通過反射獲取的,屬性的名稱必須和getter茅撞、setter方法名稱后面的字符串一致帆卓,比如上面的getter、setter方法分別為
getCircle
米丘、setCircle
剑令,那么屬性名字就必須為circle
。
三拄查、幀動畫
幀動畫需要開發(fā)者制定好動畫每一幀,系統(tǒng)一幀一幀的播放圖片。
private void start1() {
AnimationDrawable ad = new AnimationDrawable();
for (int i = 0; i < 7; i++) {
Drawable drawable = getResources().getDrawable(getResources().getIdentifier("ic_fingerprint_" + i, "drawable", getPackageName()));
ad.addFrame(drawable, 100);
}
ad.setOneShot(false);
mImageView.setImageDrawable(ad);
ad.start();
}
幀動畫同樣也可以在xml文件中配置溶推,直接在工程drawable
目錄新建animation-list
標簽:
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="false">
<item android:drawable="@drawable/ic_fingerprint_0" android:duration="100"/>
<item android:drawable="@drawable/ic_fingerprint_1" android:duration="100"/>
<item android:drawable="@drawable/ic_fingerprint_2" android:duration="100"/>
<item android:drawable="@drawable/ic_fingerprint_3" android:duration="100"/>
<item android:drawable="@drawable/ic_fingerprint_4" android:duration="100"/>
<item android:drawable="@drawable/ic_fingerprint_5" android:duration="100"/>
<item android:drawable="@drawable/ic_fingerprint_6" android:duration="100"/>
</animation-list>
private void start2() {
mImageView.setImageResource(R.drawable.frame_anim);
AnimationDrawable animationDrawable = (AnimationDrawable) mImageView.getDrawable();
animationDrawable.start();
}
其中android:onshot
屬性和setOneShot
方法表示是否只執(zhí)行一次否灾。
AnimationDrawable
實際是上是一個Drawable
動畫,動畫執(zhí)行的過程總會給你不斷重繪Drawable
的每一幀圖像稍算,實現(xiàn)動態(tài)播放效果典尾。