目錄
- 思維導(dǎo)圖
- 幀動(dòng)畫(huà)
- 使用方式
- 優(yōu)缺點(diǎn)
- 應(yīng)用場(chǎng)景
- 補(bǔ)間動(dòng)畫(huà)
- 位移鹃觉、旋轉(zhuǎn)、縮放、透明度動(dòng)畫(huà)
- 優(yōu)缺點(diǎn)
- 應(yīng)用場(chǎng)景
- 屬性動(dòng)畫(huà)
- 層次關(guān)系
- ValueAnimator
- ObjectAnimator
- TimeAnimator
- AnimatorSet
- 插值器、估值器
- TypeEvaluator
- IntEvaluator
- FloutEvaluator
- ArgbEvaluator
- TimeInterpolator / Interpolator / BaseInterpolator
- LinearInterpolator
- AccelerateDecelerateInterpolator
- TypeEvaluator
- 參考
思維導(dǎo)圖
幀動(dòng)畫(huà)
使用方式
-
xml 定義方式
在 drawable 目錄下定義一個(gè) drawable-list闲擦,然后給圖片設(shè)置 res 就好了流济。
<?xml version="1.0" encoding="utf-8"?> <animation-list xmlns:android="http://schemas.android.com/apk/res/android"> <item android:drawable="@drawable/ic_vip" android:duration="100"/> <item android:drawable="@drawable/ic_account" android:duration="100"/> <item android:drawable="@drawable/ic_clouse" android:duration="100"/> //... </animation-list>
mIvFrame.setImageResource(R.drawable.anim_frame); AnimationDrawable drawable = (AnimationDrawable) mIvFrame.getDrawable(); drawable.start(); drawable.stop();
-
Java 代碼實(shí)現(xiàn)
其實(shí)就是用代碼構(gòu)建 AnimationDrawable炭庙。
mAnimationDrawable=new AnimationDrawable(); for (int i = 0; i < 10; i++) { if (i<5){ mAnimationDrawable.addFrame(getDrawable(R.drawable.ic_vip),100); }else { mAnimationDrawable.addFrame(getDrawable(R.drawable.ic_clouse),100); } } mIvFrame.setImageDrawable(mAnimationDrawable); mAnimationDrawable.start(); mAnimationDrawable.stop();
優(yōu)缺點(diǎn)
優(yōu)點(diǎn):使用簡(jiǎn)單
缺點(diǎn):圖片是全部加載到內(nèi)存中先嬉,可能導(dǎo)致 OOM钻蔑。
應(yīng)用場(chǎng)景
基本上很少使用啥刻,可以看成 GIF 圖。
補(bǔ)間動(dòng)畫(huà)
位移動(dòng)畫(huà)
-
xml 定義方式
<?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android"> <translate android:duration="1000" android:toXDelta="100" android:toYDelta="100" android:fromYDelta="0" android:fromXDelta="0"/> </set>
mAnimation = AnimationUtils.loadAnimation(mContext, R.anim.anim_trans); mIvFrame.startAnimation(mAnimation);
-
Java 代碼實(shí)現(xiàn)方式
TranslateAnimation animation = new TranslateAnimation(0, 100, 0, 100); animation.setDuration(1000); animation.setFillAfter(true); mIvFrame.startAnimation(animation);
其他動(dòng)畫(huà)
類(lèi)似咪笑,不舉例了可帽。
優(yōu)缺點(diǎn)
優(yōu)點(diǎn):使用簡(jiǎn)單
缺點(diǎn):只改變顯示,不改變實(shí)際屬性
應(yīng)用場(chǎng)景
基本上大部分動(dòng)畫(huà)都能實(shí)現(xiàn)窗怒,常見(jiàn)于 Activity 轉(zhuǎn)場(chǎng)動(dòng)畫(huà)等等
屬性動(dòng)畫(huà)
層次關(guān)系
Animator
- AnimatorSet
- ValueAnimator
- ObjectAnimator
- TimeAnimator
ValueAnimator
常用使用方式:
private void setAnimator() {
mValueAnimator = new ValueAnimator();
mValueAnimator.setIntValues(0, 500);
mValueAnimator.setDuration(2000);
mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
Log.i(TAG, "onAnimationUpdate: " + animation.getAnimatedValue());
float y = ((int) animation.getAnimatedValue()) * 1.0f;
mIvFrame.setTranslationY(y);
}
});
mValueAnimator.start();
}
ObjectAnimator
ObjectAnimator animator = ObjectAnimator
.ofFloat(mBtnStart, "translationY", 0, 200)
.setDuration(2000);
animator.start();
ObjectAnimator 在每次更新的時(shí)候會(huì)自動(dòng)走 setXxx 方法映跟,所以就不需要像 ValueAnimator 一樣手動(dòng)添加監(jiān)聽(tīng)器,但是 ValueAnimator 的靈活性更好扬虚。
TimeAnimator
同樣是繼承至 ValueAnimator努隙,但是它只做一件事:提供一個(gè)時(shí)間流,每 18ms 回調(diào)一次辜昵。
mTimeAnimator = new TimeAnimator();
mTimeAnimator.setTimeListener(new TimeAnimator.TimeListener() {
@Override
public void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime) {
//動(dòng)畫(huà)執(zhí)行的總時(shí)間_動(dòng)畫(huà)從上一楨到當(dāng)前楨的間隔時(shí)間荸镊,單位都是毫秒
Log.i(TAG, "onTimeUpdate: " + totalTime + " " + deltaTime);
}
});
運(yùn)行結(jié)果:
2018-12-30 09:01:54.344 6656-6656/com.example.omooo.demoproject I/AnimatorActivity: onTimeUpdate: 0 0
2018-12-30 09:01:54.346 6656-6656/com.example.omooo.demoproject I/AnimatorActivity: onTimeUpdate: 0 0
2018-12-30 09:01:54.356 6656-6656/com.example.omooo.demoproject I/AnimatorActivity: onTimeUpdate: 7 7
2018-12-30 09:01:54.375 6656-6656/com.example.omooo.demoproject I/AnimatorActivity: onTimeUpdate: 24 17
2018-12-30 09:01:54.388 6656-6656/com.example.omooo.demoproject I/AnimatorActivity: onTimeUpdate: 42 18
2018-12-30 09:01:54.407 6656-6656/com.example.omooo.demoproject I/AnimatorActivity: onTimeUpdate: 60 18
2018-12-30 09:01:54.424 6656-6656/com.example.omooo.demoproject I/AnimatorActivity: onTimeUpdate: 78 18
2018-12-30 09:01:54.444 6656-6656/com.example.omooo.demoproject I/AnimatorActivity: onTimeUpdate: 96 18
估值器、插值器
估值器表示屬性的從初始值過(guò)渡到結(jié)束值變化的具體數(shù)值堪置,而插值器則表示變化率躬存,比如先加速后減速(默認(rèn))、勻速等等舀锨。
估值器
估值器都需要實(shí)現(xiàn) TypeEvaluator 接口:
public interface TypeEvaluator<T> {
public T evaluate(float fraction, T startValue, T endValue);
}
其實(shí)就是根據(jù)初始值和結(jié)束值算出一個(gè)值岭洲。
系統(tǒng)已經(jīng)有幾個(gè)默認(rèn)實(shí)現(xiàn),比如 ArgbEvaluator坎匿、IntEvaluator盾剩、FloatEvaluator 等等。其實(shí)我們?cè)谟?ValueAnimator.ofArgb() 的時(shí)候替蔬,內(nèi)部就是用 ArgbEvaluator 實(shí)現(xiàn)的告私,那對(duì)于 ValueAnimator.ofInt()、ValueAnimator.ofFloat() 方法是不是也是一樣的道理呢承桥?
下面將介紹如何自定義估值器驻粟,所實(shí)現(xiàn)的功能:
首先定義按鈕所需要的屬性:
public class ButtonInfo {
public int color;
public int x;
public int y;
public ButtonInfo(int color, int x, int y) {
this.color = color;
this.x = x;
this.y = y;
}
public ButtonInfo() {
}
}
自定義估值器:
public class ButtonEvaluator implements TypeEvaluator {
@Override
public Object evaluate(float fraction, Object startValue, Object endValue) {
ButtonInfo start = (ButtonInfo) startValue;
ButtonInfo end = (ButtonInfo) endValue;
ButtonInfo buttonInfo = new ButtonInfo();
ArgbEvaluator argbEvaluator = new ArgbEvaluator();
buttonInfo.color = (int) argbEvaluator.evaluate(fraction, ((ButtonInfo) startValue).color, ((ButtonInfo) endValue).color);
buttonInfo.x = (int) (fraction * (end.x - start.x) + start.x);
buttonInfo.y = (int) (fraction * (end.y - start.y) + start.y);
return buttonInfo;
}
}
運(yùn)用:
private void setTransAnimator() {
mValueAnimator = ValueAnimator.ofObject(new ButtonEvaluator(), new ButtonInfo(0xff94E1F7, 0, 0)
, new ButtonInfo(0xffF35519, 500, 500));
mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
ButtonInfo buttonInfo = (ButtonInfo) animation.getAnimatedValue();
mBtnStart.setBackgroundColor(buttonInfo.color);
mBtnStart.setTranslationX(buttonInfo.x);
mBtnStart.setTranslationY(buttonInfo.y);
}
});
mValueAnimator.setDuration(5000);
mValueAnimator.start();
}
可以,看出快毛,實(shí)際上自定義估值器還是很簡(jiǎn)單格嗅。
插值器
插值器需要實(shí)現(xiàn) TimeInterpolator 或 Interpolator:
public interface TimeInterpolator {
float getInterpolation(float input);
}
public interface Interpolator extends TimeInterpolator {
}
Android 系統(tǒng)內(nèi)置了幾種實(shí)現(xiàn),默認(rèn)是先加速后減速唠帝,即 AccelerateDecelerateInterpolator 屯掖,看一下源碼實(shí)現(xiàn):
public class AccelerateDecelerateInterpolator extends BaseInterpolator
implements NativeInterpolatorFactory {
public float getInterpolation(float input) {
return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
}
//...
}
四五十行代碼,首先明確的就是 value 值是 0 ~ 1襟衰,所以以上就是表示余旋函數(shù)在 pi/2 和 pi 之間贴铜,就是先加速后減速。
那我們?cè)诳匆幌?LinearInterpolator 的源碼:
/**
* An interpolator where the rate of change is constant
*/
@HasNativeInterpolator
public class LinearInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {
public float getInterpolation(float input) {
return input;
}
//...
}
那在自定義插值器就易如反掌了呀瀑晒,定義一個(gè)先減速后加速的插值器绍坝,如下:
public class DeceAcceInterpolator implements TimeInterpolator {
@Override
public float getInterpolation(float input) {
//余旋函數(shù) 0 ~ PI/2
return (float) Math.cos(input * Math.PI / 2);
}
}
然后給 ValueAnimator 設(shè)置自定義的插值器:
mValueAnimator.setInterpolator(new DeceAcceInterpolator());
啊苔悦?怎么反向了轩褐?看來(lái)設(shè)置變化率需要在 x 軸的下方呀。至于為什么玖详?留個(gè)坑把介。