動(dòng)畫分類
1,幀動(dòng)畫
顧名思義,就是連續(xù)播放多張不同的圖片,形成的動(dòng)畫效果,這個(gè)必須是放在Drawable文件夾下,下面是XML的方式
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@color/loading0" android:duration="120"/>
<item android:drawable="@color/loading1" android:duration="120"/>
<item android:drawable="@color/loading2" android:duration="120"/>
</animation-list>
代碼的方式
private AnimationDrawable mAnimationDrawable;
public AnimationDrawable getAnimationDrawableList() {
if(mAnimationDrawable == null) {
mAnimationDrawable = new AnimationDrawable();
for (int i = 0; i < 5; i++) {
int identifier = getResources().getIdentifier("loading"+i, "drawable", getPackageName());
mAnimationDrawable.addFrame(getResources().getDrawable(identifier), 120);
}
}
return mAnimationDrawable;
}
2,補(bǔ)間動(dòng)畫
這類動(dòng)畫只需要定義動(dòng)畫的開始和結(jié)束,時(shí)長,不需要定義動(dòng)畫的每一幀,通過對View的內(nèi)容進(jìn)行一系列的圖像變換來實(shí)現(xiàn)的
主要包括四種基本的效果
- 透明度變化Alpha
- 大小變化Scale
- 位移變化 Translate
- 旋轉(zhuǎn)變化 Retate
提到動(dòng)畫不得不提到插值器Interpolator,負(fù)責(zé)控制動(dòng)畫變化的速度。
通俗的可以理解為物理上的加速度。
同理,它能實(shí)現(xiàn)的基本效果就是勻速,加速,減速,拋物線等多種速度變化
- Interpolator其實(shí)是一個(gè)接口,繼承自TimeInterpolator
-
public interface Interpolator extends TimeInterpolator {
}public interface TimeInterpolator {
float getInterpolation(float input);
}
該接口只有一個(gè)float getInterpolation(float input);方法,入?yún)⑹且粋€(gè)0.0-1.0的值,返回值可以小于0.0,也可以大于1.0
Android已經(jīng)默認(rèn)幫我們實(shí)現(xiàn)了好幾種的插值器,具體看Android
的源碼
補(bǔ)間動(dòng)畫使用比較簡單一些,這里就不放源碼了,大家可以自行閱讀Android
源碼,查看實(shí)現(xiàn)方式
3,屬性動(dòng)畫
屬性動(dòng)畫是Android3.0引入的,補(bǔ)間動(dòng)畫我們只是改變了View的繪制效果,而View的實(shí)際屬性是沒有發(fā)生變化的。而屬性動(dòng)畫正是直接View的屬性來實(shí)現(xiàn)的,同時(shí)屬性動(dòng)畫可以在任何對象上,不僅僅限于View
定義屬性動(dòng)畫需要的一些基本屬性
- 動(dòng)畫持續(xù)時(shí)間-通過android:duration來指定
- 動(dòng)畫的插值方式,這個(gè)和補(bǔ)間動(dòng)畫的插值器理解類似通過android:interpolater來指定
- 動(dòng)畫的重復(fù)次數(shù)-通過android:repeatCount來指定
- 動(dòng)畫的重復(fù)模式-通過android:repeatMode來指定
- 幀刷新頻率
- 動(dòng)畫集合 -實(shí)現(xiàn)多個(gè)屬性動(dòng)畫的組合使用-通過android:ordeing來指定這組動(dòng)畫是按照次序還是同時(shí)播放,資源文件中通過<set></set>來表示
屬性動(dòng)畫中,另外一個(gè)重要的概念就是 Evaluator
Evaluator是用來控制屬性動(dòng)畫是如何來計(jì)算屬性值的-可以理解為每次對象屬性的變化率
public interface TypeEvaluator<T> {
public T evaluate(float fraction, T startValue, T endValue);
}
常見的實(shí)現(xiàn)類就是IntEvaluator,F(xiàn)loatEvaluator,ArgbEvaluator,我們來看下ArgbEvaluator的實(shí)現(xiàn)方式,實(shí)現(xiàn)的邏輯就是根據(jù)輸入的初始值和結(jié)束值及一個(gè)進(jìn)度比,計(jì)算出每一個(gè)進(jìn)度的ARGB值
public class ArgbEvaluator implements TypeEvaluator {
private static final ArgbEvaluator sInstance = new ArgbEvaluator();
public static ArgbEvaluator getInstance() {
return sInstance;
}
public Object evaluate(float fraction, Object startValue, Object endValue) {
int startInt = (Integer) startValue;
float startA = ((startInt >> 24) & 0xff) / 255.0f;
float startR = ((startInt >> 16) & 0xff) / 255.0f;
float startG = ((startInt >> 8) & 0xff) / 255.0f;
float startB = ( startInt & 0xff) / 255.0f;
int endInt = (Integer) endValue;
float endA = ((endInt >> 24) & 0xff) / 255.0f;
float endR = ((endInt >> 16) & 0xff) / 255.0f;
float endG = ((endInt >> 8) & 0xff) / 255.0f;
float endB = ( endInt & 0xff) / 255.0f;
// convert from sRGB to linear
startR = (float) Math.pow(startR, 2.2);
startG = (float) Math.pow(startG, 2.2);
startB = (float) Math.pow(startB, 2.2);
endR = (float) Math.pow(endR, 2.2);
endG = (float) Math.pow(endG, 2.2);
endB = (float) Math.pow(endB, 2.2);
// compute the interpolated color in linear space
float a = startA + fraction * (endA - startA);
float r = startR + fraction * (endR - startR);
float g = startG + fraction * (endG - startG);
float b = startB + fraction * (endB - startB);
// convert back to sRGB in the [0..255] range
a = a * 255.0f;
r = (float) Math.pow(r, 1.0 / 2.2) * 255.0f;
g = (float) Math.pow(g, 1.0 / 2.2) * 255.0f;
b = (float) Math.pow(b, 1.0 / 2.2) * 255.0f;
return Math.round(a) << 24 | Math.round(r) << 16 | Math.round(g) << 8 | Math.round(b);
}
}
4,屬性動(dòng)畫-AnimatorSet
AnimatorSet是Animator的子類,它可以指定多個(gè)屬性動(dòng)畫是順序執(zhí)行還是同時(shí)執(zhí)行
5,屬性動(dòng)畫-ValueAnimator實(shí)現(xiàn)
ValueAnimator是屬性動(dòng)畫中最重要的一個(gè)類,它定義了屬性動(dòng)畫大部分的核心功能,包括計(jì)算各個(gè)幀的屬性值,處理更新事件,按照屬性值的類型控制計(jì)算規(guī)則等
一個(gè)完整的屬性動(dòng)畫可以由倆部分組成
- 計(jì)算動(dòng)畫各個(gè)幀的屬性值
- 將這些屬性值設(shè)置給指定的對象
ValueAnimator為我們實(shí)現(xiàn)了第一部分的功能,第二部分由我們開發(fā)者自己來實(shí)現(xiàn)。ValueAnimator的構(gòu)造函數(shù)是空實(shí)現(xiàn),一般都是使用靜態(tài)工廠方法來實(shí)現(xiàn)
下面是實(shí)現(xiàn)一個(gè)完整的ValueAnimator的動(dòng)畫流程
1,定義一個(gè)Evaluator
public class IntEvaluator implements TypeEvaluator<Integer> {
public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
int startInt = startValue;
return (int)(startInt + fraction * (endValue - startInt));
}
}
2,定義一個(gè)Interpolator
public class LinearInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {
public LinearInterpolator() {
}
public LinearInterpolator(Context context, AttributeSet attrs) {
}
public float getInterpolation(float input) {
return input;
}
}
3,開始動(dòng)畫
public void startAnim(Object target, int from, int to, int duration) {
ValueAnimator va = ValueAnimator.ofInt(from, to);
va.setInterpolator(new LinearInterpolator());
va.setEvaluator(new IntEvaluator());
va.setDuration(duration);
va.setTarget(this);
va.addUpdateListener(animator -> {
int value = (int) animator.getAnimatedValue();//
animator.getAnimatedFraction();//動(dòng)畫執(zhí)行的百分比0-1
target.update(value);
});
va.start();
}
4,停止動(dòng)畫
public void stopAnim() {
va.cancel();
}
5,屬性動(dòng)畫-ObjectAnimator實(shí)現(xiàn)
ObjectAnimator動(dòng)畫是ValueAnimator的子類,實(shí)現(xiàn)的上面提到的第二部分功能。它和ValueAnimator最大的不同就是構(gòu)造的時(shí)候需要指定作用的對象和對象的屬性名,而且不需要實(shí)現(xiàn)addUpdateListener接口
public class CustomProgressBar extends ProgressBar {
public CustomProgressBar(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void setProgressWithAnim(int progress, int duration) {
ObjectAnimator animator = ObjectAnimator.ofInt(this, "progress", progress);
animator.setDuration(duration);
animator.start();
}
}
使用ObjectAnimator有以下幾點(diǎn)需要注意:
需要為對象對應(yīng)的屬性提高setter方法,例如上面的setProgress方法
如果動(dòng)畫的對象是View,那么為了能讓其顯示動(dòng)畫效果,某些情況下可能還是需要注冊addUpdateListener,在回調(diào)中刷新view的顯示
-
屬性動(dòng)畫也可以在XML中定義
<set xmlns:android="http://schemas.android.com/apk/res/android"> <!--消失--> <objectAnimator android:duration="0" android:propertyName="alpha" android:valueFrom="1.0" android:valueTo="0.0"/> <!--旋轉(zhuǎn)--> <objectAnimator android:duration="300" android:propertyName="rotationY" android:valueFrom="360" android:valueTo="0"/> </set>
獲取XML屬性動(dòng)畫
AnimatorSet as = AnimatorInflater.loadAnimator(context, R.animator.animator);
as.setTarget(mBackView);
as.start();
6,屬性動(dòng)畫-XxxAnimator.ofPropertyValuesHolder使用
先上代碼
public static ObjectAnimator getPulseAnimator(View labelToAnimate, float decreaseRatio,
float increaseRatio) {
Keyframe k0 = Keyframe.ofFloat(0f, 1f);
Keyframe k1 = Keyframe.ofFloat(0.275f, decreaseRatio);
Keyframe k2 = Keyframe.ofFloat(0.69f, increaseRatio);
Keyframe k3 = Keyframe.ofFloat(1f, 1f);
PropertyValuesHolder scaleX = PropertyValuesHolder.ofKeyframe("scaleX", k0, k1, k2, k3);
PropertyValuesHolder scaleY = PropertyValuesHolder.ofKeyframe("scaleY", k0, k1, k2, k3);
ObjectAnimator pulseAnimator =
ObjectAnimator.ofPropertyValuesHolder(labelToAnimate, scaleX, scaleY);
pulseAnimator.setDuration(PULSE_ANIMATOR_DURATION);
return pulseAnimator;
}
PropertyValuesHolder這個(gè)類的意義就是,它其中保存了動(dòng)畫過程中所需要操作的屬性和對應(yīng)的值
Keyframe直譯過來就是關(guān)鍵幀
關(guān)鍵幀這個(gè)概念是從動(dòng)畫里學(xué)來的,類似幀動(dòng)畫,我們指定的每一張圖片就是一個(gè)關(guān)鍵幀
Keyframe就相當(dāng)于定義幀動(dòng)畫中的每一個(gè)圖片顯示的時(shí)間和位置
fraction:表示當(dāng)前的顯示進(jìn)度,即從加速器中g(shù)etInterpolation()函數(shù)的返回值;
value:表示當(dāng)前應(yīng)該在的位置
public static Keyframe ofFloat(float fraction, float value)
- 比如Keyframe.ofFloat(0f, 1f);表示動(dòng)畫進(jìn)度為0時(shí),動(dòng)畫所在的數(shù)值位置為0
- Keyframe.ofFloat(0.275f, decreaseRatio)表示動(dòng)畫進(jìn)度為27.5%時(shí),動(dòng)畫所在的數(shù)值位置為decreaseRatio
- Keyframe.ofFloat(1f,1f)表示動(dòng)畫結(jié)束時(shí),動(dòng)畫所在的數(shù)值位置為1