美文网首页
Android UI-属性动画(三)

Android UI-属性动画(三)

作者: twinsnan | 来源:发表于2016-11-20 16:07 被阅读0次

概述

前两篇已经讲了属性动画的使用和源码的实现。但是大家应该发现了还有非常重要的一部分没有提及,那就是插值器。无论是在属性动画还是在View动画中,都有一个非常重要的类Interporator,这个类获取到当前的时间进度,然后根据时间进入计算得到我们想要的插值。通俗易懂的语言来说,这就是一个y=f(t)的函数,输入的是时间,输出的是变换后的值。为了自己实现一个属性动画的效果,就先来看下这个重要的类的实现,系统提供的默认Interporator。

插值器

在动画中应用插值器非常简单,在View动画中只要设置setInterpolator就可以了。

 outerScaleAnimation.setInterpolator(new AccelerateDecelerateInterpolator());

在属性动画中也可以一样简单地设置:

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

系统提供了蛮多插值器的,如线性插值器,先加速后减速插值器,加速插值器,减速插值器,末尾震荡插值器等。


image.png

这些插值器都是从BaseInterpolator继承而来,BaseInterpolator实现了Interpolator接口,Interpolator继承自TimeInterpolator。在TimeInterpolator中,只有一个需要实现的方法。这个方法的意思就是说,我们需要输入0-1.0的表示进度的数,而输出可已超过1.0的表示已经经由此方法转换了的值。

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的方法,说明不是我们应该调用的,就不去管它。然后看下简单的几种插值器的实现。
首先来看下最简单的线性插值器,可以发现非常简单。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();
    }
}

然后再来看下复杂点的AccelerateInterpolator 。仔细分析下,发现除了在XML布局设置需要的构造器外,逻辑代码非常简单明了。只有一个方法起到了作用

@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);
    }
}

看下这个方法,如果我们设置的mFactor等于1的话,那么就是y=x2这个形式的函数。如果不是,那么是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方法,用处是将最后的结果通知调用者。fraction = mInterpolator.getInterpolation(fraction);这个前面已经讲到了。

  @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(当然首先要去设置)去获取最终的值。

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

getValue是抽象的方法,需要子类去实现。setEvaluator也是Keyframes中的抽象方法,除了系统实现的几个外(在PathKeyframes中),我们需要自己定义,然后用setEvaluator设置。

  /**
     * 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);

系统提供的默认的Evaluator,有Int和float还有颜色属性的Evaluator,也可以自己继承TypeEvaluator去实现。


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);

}

实现一个自定义的属性动画

我们想实现一个阻尼正震荡效果,频率也是随着时间的增加而上升。为了效果,我们在Interpolator中换算时间,在Evaluator中计算位移的值。
代码如下,只是模拟了一下,并没有真正的按照公式来实现。。

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来实现,这样就不用自己设置位移了

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

总结

这样我们的属性动画算是介绍完了,属性动画可以做出一些复杂的动画效果,可以实现些有意思的效果。接下去可能会介绍自定义Drawable的实现。

相关文章

  • Android UI-属性动画(三)

    概述 前两篇已经讲了属性动画的使用和源码的实现。但是大家应该发现了还有非常重要的一部分没有提及,那就是插值器。无论...

  • Android UI-属性动画(二)

    概述 上一篇讲到了属性动画,主要讲到了用法和大概的思路。但是没有讲到具体是如何实现动画的。这里我们分析下View动...

  • Android UI-属性动画(一)

    概述 自定义控件中,如果要做比较好的动画效果,除了理解动画的类型和动画的效果,深入理解动画的原理也是必不可少的一环...

  • Android属性动画完全解析

    Android属性动画完全解析(上)Android属性动画完全解析(中)Android属性动画完全解析(下)

  • 笔记:Android 动画

    Android 动画 Android动画分为三种: 属性动画 帧动画 View动画 View动画支持4种效果 平移...

  • Android源码相关分析(a)

    1、Android动画框架实现原理 android 动画框架分为三种类别:渐变动画、帧动画、属性动画Android...

  • Android 属性动画Property Animation(下

    Android 属性动画Property Animation(上) Android 属性动画Property An...

  • 属性动画

    参考-Android属性动画完全解析(上) 参考-Android属性动画完全解析(中) 参考-Android属性动...

  • Android 动画

    android动画分为三种 帧动画,视图动画(补间动画),属性动画逐帧动画 视图动画 属性动画 Window和A...

  • Android属性动画完全解析

    Android属性动画完全解析(上),初识属性动画的基本用法Android属性动画完全解析(中),ValueAni...

网友评论

      本文标题:Android UI-属性动画(三)

      本文链接:https://www.haomeiwen.com/subject/qztypttx.html