Android UI-屬性動畫(三)

概述

前兩篇已經(jīng)講了屬性動畫的使用和源碼的實現(xiàn)。但是大家應(yīng)該發(fā)現(xiàn)了還有非常重要的一部分沒有提及,那就是插值器。無論是在屬性動畫還是在View動畫中,都有一個非常重要的類Interporator,這個類獲取到當(dāng)前的時間進度,然后根據(jù)時間進入計算得到我們想要的插值。通俗易懂的語言來說,這就是一個y=f(t)的函數(shù),輸入的是時間,輸出的是變換后的值。為了自己實現(xiàn)一個屬性動畫的效果,就先來看下這個重要的類的實現(xiàn),系統(tǒng)提供的默認(rèn)Interporator。

插值器

在動畫中應(yīng)用插值器非常簡單,在View動畫中只要設(shè)置setInterpolator就可以了。

 outerScaleAnimation.setInterpolator(new AccelerateDecelerateInterpolator());

在屬性動畫中也可以一樣簡單地設(shè)置:

ObjectAnimator animator = ObjectAnimator.ofFloat(view, "TranslateX", 0.f, 1.f);
animator.setInterpolator(new DecelerateInterpolator());
animator.start();

系統(tǒng)提供了蠻多插值器的,如線性插值器,先加速后減速插值器,加速插值器,減速插值器,末尾震蕩插值器等。


image.png

這些插值器都是從BaseInterpolator繼承而來,BaseInterpolator實現(xiàn)了Interpolator接口,Interpolator繼承自TimeInterpolator。在TimeInterpolator中,只有一個需要實現(xiàn)的方法。這個方法的意思就是說,我們需要輸入0-1.0的表示進度的數(shù),而輸出可已超過1.0的表示已經(jīng)經(jīng)由此方法轉(zhuǎn)換了的值。

public interface TimeInterpolator {

    /**
     * Maps a value representing the elapsed fraction of an animation to a value that represents
     * the interpolated fraction. This interpolated value is then multiplied by the change in
     * value of an animation to derive the animated value at the current elapsed animation time.
     *
     * @param input A value between 0 and 1.0 indicating our current point
     *        in the animation where 0 represents the start and 1.0 represents
     *        the end
     * @return The interpolation value. This value can be more than 1.0 for
     *         interpolators which overshoot their targets, or less than 0 for
     *         interpolators that undershoot their targets.
     */
    float getInterpolation(float input);
}

BaseInterpolator的代碼中,有兩個hide的方法,說明不是我們應(yīng)該調(diào)用的,就不去管它。然后看下簡單的幾種插值器的實現(xiàn)。
首先來看下最簡單的線性插值器,可以發(fā)現(xiàn)非常簡單。getInterpolation方法直接將輸入返回。這也非常好理解,既然是線性的,那么就是y=x的這種變換。

@HasNativeInterpolator
public class LinearInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {

    public LinearInterpolator() {
    }

    public LinearInterpolator(Context context, AttributeSet attrs) {
    }

    public float getInterpolation(float input) {
        return input;
    }

    /** @hide */
    @Override
    public long createNativeInterpolator() {
        return NativeInterpolatorFactoryHelper.createLinearInterpolator();
    }
}

然后再來看下復(fù)雜點的AccelerateInterpolator 。仔細分析下,發(fā)現(xiàn)除了在XML布局設(shè)置需要的構(gòu)造器外,邏輯代碼非常簡單明了。只有一個方法起到了作用

@HasNativeInterpolator
public class AccelerateInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {
    private final float mFactor;
    private final double mDoubleFactor;

    public AccelerateInterpolator() {
        mFactor = 1.0f;
        mDoubleFactor = 2.0;
    }

    /**
     * Constructor
     *
     * @param factor Degree to which the animation should be eased. Seting
     *        factor to 1.0f produces a y=x^2 parabola. Increasing factor above
     *        1.0f  exaggerates the ease-in effect (i.e., it starts even
     *        slower and ends evens faster)
     */
    public AccelerateInterpolator(float factor) {
        mFactor = factor;
        mDoubleFactor = 2 * mFactor;
    }

    public AccelerateInterpolator(Context context, AttributeSet attrs) {
        this(context.getResources(), context.getTheme(), attrs);
    }

    /** @hide */
    public AccelerateInterpolator(Resources res, Theme theme, AttributeSet attrs) {
        TypedArray a;
        if (theme != null) {
            a = theme.obtainStyledAttributes(attrs, R.styleable.AccelerateInterpolator, 0, 0);
        } else {
            a = res.obtainAttributes(attrs, R.styleable.AccelerateInterpolator);
        }

        mFactor = a.getFloat(R.styleable.AccelerateInterpolator_factor, 1.0f);
        mDoubleFactor = 2 * mFactor;
        setChangingConfiguration(a.getChangingConfigurations());
        a.recycle();
    }

    public float getInterpolation(float input) {
        if (mFactor == 1.0f) {
            return input * input;
        } else {
            return (float)Math.pow(input, mDoubleFactor);
        }
    }

    /** @hide */
    @Override
    public long createNativeInterpolator() {
        return NativeInterpolatorFactoryHelper.createAccelerateInterpolator(mFactor);
    }
}

看下這個方法,如果我們設(shè)置的mFactor等于1的話,那么就是y=x2這個形式的函數(shù)。如果不是,那么是y=xfactor的形式。到達終點前會加速趨近于1。

    public float getInterpolation(float input) {
        if (mFactor == 1.0f) {
            return input * input;
        } else {
            return (float)Math.pow(input, mDoubleFactor);
        }
    }

上面介紹了Interpolator的用法,但是這只是0-1.0的變換的插值。如何將這個值再次換算到最終顯示的值呢。其實在上篇中的源碼中也有,但是沒有詳細講。如下的代碼是animateValue方法,用處是將最后的結(jié)果通知調(diào)用者。fraction = mInterpolator.getInterpolation(fraction);這個前面已經(jīng)講到了。

  @CallSuper
    void animateValue(float fraction) {
        fraction = mInterpolator.getInterpolation(fraction);
        mCurrentFraction = fraction;
        int numValues = mValues.length;
        for (int i = 0; i < numValues; ++i) {
            mValues[i].calculateValue(fraction);
        }
        if (mUpdateListeners != null) {
            int numListeners = mUpdateListeners.size();
            for (int i = 0; i < numListeners; ++i) {
                mUpdateListeners.get(i).onAnimationUpdate(this);
            }
        }
    }

然后看下calculateValue這個方法,這個方法中只有兩行代碼,主要是第一行,getValue中可以通過mEvaluator(當(dāng)然首先要去設(shè)置)去獲取最終的值。

    void calculateValue(float fraction) {
        Object value = mKeyframes.getValue(fraction);
        mAnimatedValue = mConverter == null ? value : mConverter.convert(value);
    }

getValue是抽象的方法,需要子類去實現(xiàn)。setEvaluator也是Keyframes中的抽象方法,除了系統(tǒng)實現(xiàn)的幾個外(在PathKeyframes中),我們需要自己定義,然后用setEvaluator設(shè)置。

  /**
     * Sets the TypeEvaluator to be used when calculating animated values. This object
     * is required only for Keyframes that are not either IntKeyframes or FloatKeyframes,
     * both of which assume their own evaluator to speed up calculations with those primitive
     * types.
     *
     * @param evaluator The TypeEvaluator to be used to calculate animated values.
     */
    void setEvaluator(TypeEvaluator evaluator);

系統(tǒng)提供的默認(rèn)的Evaluator,有Int和float還有顏色屬性的Evaluator,也可以自己繼承TypeEvaluator去實現(xiàn)。


image.png

TypeEvaluator是一個接口,只有一個方法evaluate。我們只要繼承就可以了。

public interface TypeEvaluator<T> {

    /**
     * This function returns the result of linearly interpolating the start and end values, with
     * <code>fraction</code> representing the proportion between the start and end values. The
     * calculation is a simple parametric calculation: <code>result = x0 + t * (x1 - x0)</code>,
     * where <code>x0</code> is <code>startValue</code>, <code>x1</code> is <code>endValue</code>,
     * and <code>t</code> is <code>fraction</code>.
     *
     * @param fraction   The fraction from the starting to the ending values
     * @param startValue The start value.
     * @param endValue   The end value.
     * @return A linear interpolation between the start and end values, given the
     *         <code>fraction</code> parameter.
     */
    public T evaluate(float fraction, T startValue, T endValue);

}

實現(xiàn)一個自定義的屬性動畫

我們想實現(xiàn)一個阻尼正震蕩效果,頻率也是隨著時間的增加而上升。為了效果,我們在Interpolator中換算時間,在Evaluator中計算位移的值。
代碼如下,只是模擬了一下,并沒有真正的按照公式來實現(xiàn)。。

public class ValueAnimatorActivity extends AppCompatActivity {

    private static final String TAG = ValueAnimatorActivity.class.getSimpleName();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_value_animator);
    }

    @Override
    protected void onResume() {
        super.onResume();
        final ImageView view = (ImageView) findViewById(R.id.iv_animation);
        if (view != null) {
            view.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    performAnimator(v);
                }
            });
        }
    }

    ValueAnimator animator =  ValueAnimator.ofFloat(0, 100);

    private void performAnimator(final View view) {
        animator.setDuration(1000);
        animator.setInterpolator(new MyInterpolator());
        animator.setEvaluator(new MyEvaluator());
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float value = (float) animation.getAnimatedValue();
                Log.e(TAG, "value:" + value);
                if (view != null) {
                    view.setTranslationY(value);
                }
            }
        });
        animator.start();
    }

    private class MyInterpolator implements Interpolator {
        // 我們需要先慢后快
        @Override
        public float getInterpolation(float input) {
            Log.e(TAG, "input:" + input);
            return (float) Math.pow(input, 3);
        }
    }

    private class MyEvaluator implements TypeEvaluator<Float> {
        private final float tau = 5.0f;
        private final float omiga = 100.0f;
        private final float A = 200.0f;

        @Override
        public Float evaluate(float fraction, Float startValue, Float endValue) {
            double t = Math.exp(-fraction * tau);
            double sin = Math.sin(fraction * omiga);
            return (float) (A * t * sin);
        }
    }
}

效果如下,錄制時達不到很高的幀率,所以有點失真。


1.gif

也可以直接用ObjectAnimator來實現(xiàn),這樣就不用自己設(shè)置位移了

ObjectAnimator animator = ObjectAnimator.ofFloat(view,"TranslationX",0,100);
animator.setDuration(1000);
animator.setInterpolator(new MyInterpolator());
animator.setEvaluator(new MyEvaluator());
animator.start();

總結(jié)

這樣我們的屬性動畫算是介紹完了,屬性動畫可以做出一些復(fù)雜的動畫效果,可以實現(xiàn)些有意思的效果。接下去可能會介紹自定義Drawable的實現(xiàn)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1 背景 不能只分析源碼呀,分析的同時也要整理歸納基礎(chǔ)知識,剛好有人微博私信讓全面說說Android的動畫,所以今...
    未聞椛洺閱讀 2,850評論 0 10
  • 轉(zhuǎn)載一篇高質(zhì)量博文,原地址請戳這里轉(zhuǎn)載下來方便今后查看。1 背景不能只分析源碼呀,分析的同時也要整理歸納基礎(chǔ)知識,...
    Elder閱讀 2,007評論 0 24
  • 戀上一座城,大抵是因為這座城有你愛的人,有你的親人。從一座城到另一座城,無數(shù)次,在火車的轟鳴聲中丈量著他們的距離。...
    寒塘鶴影_閱讀 677評論 0 2
  • 一顧傾人城。再顧傾人國。寧不知傾城與傾國。佳人難再得。 說到氣質(zhì),想起來這首詩歌。 每個人都有自己與眾不同的氣質(zhì),...
    羽扇綸巾_Q醬閱讀 183評論 3 4

友情鏈接更多精彩內(nèi)容