Android 图片弹跳动画2

在上一篇博文Android 图片弹跳动画里,我用两种方法实现了一个弹跳的动画,实现效果上篇博文里面有,这里就不再贴了,虽说是两种方法,但实现机制是大同小异,核心思想就是递归的不断启动动画,来实现View的上升和下降,后来发现还有另一种方法,不需要递归的去启动多个动画,只需要启动一个动画即可。


这里核心思想是自定义Interpolator,也就是插值器,关于插值器是什么网上有很多详细的介绍,我们上篇博文也用到了,只不过用的是系统定义好的。这里简单说就是它不会改变动画的执行顺序,但可以改变动画的执行效果,它的原理就是实现一个插值器的公共接口,完后具体实现getInterpolation方法,这个方法有一个参数是input,它表示了动画执行的过程,它的值从0到1,在动画执行过程中会不断调用这个方法,并且input线性递增,通过这个值,我们就可以根据自己动画的函数来返回我们想要的效果。


在上篇博文中,我们说过这个动画的几个要点,这里重新贴下:

1. 动画弹起的高度越来越小,我这里是第一次弹起屏幕的高度的1/2,第二次弹起1/4,第三次弹起1/8,以此类推

2. 我们将图片的一次落下或弹起看成一次动画,动画的时间越来越短,假设第一次落下动画需要1秒,那第一次弹起就需要1/2秒,第二次落下也是1/2秒,第二次弹起则需要1/4秒,以此类推

3. 下落的时候,速度越来越快,弹起的时候,速度越来越慢


其实Android本身已经写好了一个弹起落下的Interpolator,叫BounceInterpolator,我们先简单看看

public class BounceInterpolator implements Interpolator, NativeInterpolatorFactory {
    public BounceInterpolator() {
    }

    @SuppressWarnings({"UnusedDeclaration"})
    public BounceInterpolator(Context context, AttributeSet attrs) {
    }

    private static float bounce(float t) {
        return t * t * 8.0f;
    }

    public float getInterpolation(float t) {
        // _b(t) = t * t * 8
        // bs(t) = _b(t) for t < 0.3535
        // bs(t) = _b(t - 0.54719) + 0.7 for t < 0.7408
        // bs(t) = _b(t - 0.8526) + 0.9 for t < 0.9644
        // bs(t) = _b(t - 1.0435) + 0.95 for t <= 1.0
        // b(t) = bs(t * 1.1226)
        t *= 1.1226f;
        if (t < 0.3535f) return bounce(t);
        else if (t < 0.7408f) return bounce(t - 0.54719f) + 0.7f;
        else if (t < 0.9644f) return bounce(t - 0.8526f) + 0.9f;
        else return bounce(t - 1.0435f) + 0.95f;
    }

    /** @hide */
    @Override
    public long createNativeInterpolator() {
        return NativeInterpolatorFactoryHelper.createBounceInterpolator();
    }
}
这里我们要关注的就是getInterpolation这个方法,上面已经解释过它的实现原理,我们来看看对应的图


这是网上找的图片,这里x轴是时间,也就是上面提到的input,y轴是动画的过程,对照上面的实现,我们可以看到t < 0.3535f时,是一个递增的抛物线,在0.3535f时,y为1,也就是动画第一次落下,而t < 0.7408f时,是一个先递减,再递增的抛物线,也就是弹起又落下,而t < 0.9644f类似的,再一次弹起又落下,最后的0.9644到1是最后一次弹起落下,至于这里的函数为什么这样定义,说实话我没看懂,物理渣伤不起,估计可能跟重力感应还多少有些联系,不管这些,物理不是我们研究的重点,我们自己来实现类似的。为了简化问题,我们就以图片弹起两次为例进行说明,至于弹起三次、四次、五次,原理都是类似的。


我们将动画过程进行分解,图片弹起两次,经历了5个过程:

1. 图片第一次落下,从屏幕的顶部出现,直到屏幕底部

2. 图片第一次弹起,从屏幕的底部弹起到屏幕高度的1/2

3. 图片第二次落下,从屏幕高度的1/2落下到屏幕底部

4. 图片第二次弹起,从屏幕的底部弹起到屏幕高度的1/4

5. 图片第三次落下,从屏幕高度的1/4落下到屏幕底部


按照上面提到的动画的要点2,我们假设第一步花费时间为x,则第二步和第三步花费时间分别为x / 2,第四步和第五步花费时间分别为x / 4,则有公式:

x + x / 2 * 2 + x / 4 * 2 = 1

一元方程, x的值为 2 / 5

有了这个值后,我们就可以得到类似上图的一个分段抛物线,之前有免费画抛物线的软件,现在开始收费了,其它软件画有点麻烦,所以我就用文字描述下,对应上面动画过程的五步:
1. 在x轴的【0,2/5】这个区间,是一个递增的抛物线,y从0变为1,也就是图片第一次落下,我们可以得到抛物线的函数为 y = 25 / 4 * x * x(至于这个函数怎么来的,可能需要大家懂一点抛物线的知识,我们初中还是高中肯定学过,只不过时间长了忘了,我们可以先回忆一个简单的抛物线,y = x * x,也就是AccelerateInterpolator的实现中用到的,x轴从0开始匀速到1,y轴的值从0开始加速到1,我们这里只不过是将x的区间换成了【0,2/5】而已)

2. 在x轴的【2/5,3/5】这个区间,是一个递减的抛物线,y从1变为1/2,也就是图片第一次弹起

3. 在x轴的【3/5,4/5】这个区间,是一个递增的抛物线,y从1/2变为1,也就是图片第二次落下,2、3步是同一个抛物线,函数为y = 1/ 2 + 25 / 2 * (x - 3 / 5) * (x - 3 / 5)

4. 在x轴的【4/5,9/10】这个区间,是一个递减的抛物线,y从1变为3/4,也就是图片的第二次弹起

5. 在x轴的【9/10,1】这个区间,是一个递增的抛物线,y从3/4变为1,也就是图片的第三次落下,4、5步是同一个抛物线,函数为y = 3/4 + 25 * (x - 9/10) * (x - 9/10)

通过上面的分析,我们实现自己的插值器就很简单了

public class JumpInterpolator implements TimeInterpolator {

    @Override
    public float getInterpolation(float input) {

        if (input <= 2/5f) {
            return 25 / 4f * input * input;
        } else if (input <= 4/5f) {
            return 1 / 2f + 25 / 2f * (input - 3 / 5f) * (input - 3 / 5f);
        } else {
            return 3 / 4f + 25 * (input - 9 / 10f) * (input - 9 / 10f);
        }

    }
}

看下MainActivity的实现

public class MainActivity extends Activity {
    private ImageView jump;
    private int mHeight;

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

        jump = (ImageView) findViewById(R.id.move);
    }

    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        super.onWindowFocusChanged(hasFocus);

        if (hasFocus) {
            Rect outRect = new Rect();
            getWindow().findViewById(Window.ID_ANDROID_CONTENT).getDrawingRect(outRect);
            mHeight = outRect.height();

            beginTransAnimation();
        }
    }

    private void beginTransAtranslationYnimation() {
        ObjectAnimator animator = ObjectAnimator.ofFloat(jump, "translationY", -mHeight, 0);
        animator.setInterpolator(new JumpInterpolator());
        animator.setDuration(5000);
        animator.start();
    }
}

在页面加载完成时,我们首先获取屏幕的高度,并赋值给mHeight变量,完后启动动画,这里动画还是用的属性动画,指定动画变化的部分为translationY,也就是y轴的坐标,初始值为-mHeight,也就是初始时图片的下边缘跟屏幕的上边缘重合(所以初始时看不到图片),而动画的终点为0,也就是图片完全显示在屏幕中,这里最重要的是我们指定了插值器为我们刚刚定义的JumpInterpolator,完后指定动画时间,最后启动动画即可,比较上一篇博客的两种实现方式,我们发现过程还是简化了许多,没有递归,也不会多次启动动画,一次就搞定。

源码下载


  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值