弹性动画的实现

使用Facebook推出的rebound实现

先看下图片放大的效果,下文还有一个指示器弹性滑动的
在这里插入图片描述

一、导入依赖

implementation 'com.facebook.rebound:rebound:0.3.8'

二、代码中使用

先看一下官网的示例代码
官网示例

// Create a system to run the physics loop for a set of springs.
        SpringSystem springSystem = SpringSystem.create();
        // Add a spring to the system.
        Spring spring = springSystem.createSpring();
        // Add a listener to observe the motion of the spring.
        spring.addListener(new SimpleSpringListener() {
            @Override
            public void onSpringUpdate(Spring spring) {
                // You can observe the updates in the spring
                // state by asking its current value in onSpringUpdate.
                float value = (float) spring.getCurrentValue();
                float scale = 1f - (value * 0.5f);
                myView.setScaleX(scale);
                myView.setScaleY(scale);
            }
        });
        // Set the spring in motion; moving from 0 to 1
        spring.setEndValue(1);

首先创建一个spring,然后设置监听,在onSpringUpdate方法中做view的变化

例一 代码中直接使用:

 SpringSystem springSystem = SpringSystem.create();
        Spring spring = springSystem.createSpring();
        spring.setCurrentValue(1.0f);
        spring.setSpringConfig(new SpringConfig(50,5));
        spring.addListener(new SimpleSpringListener(){
            @Override
            public void onSpringUpdate(Spring spring) {
                super.onSpringUpdate(spring);
                float currentValue = (float) spring.getCurrentValue();
                imageView.setScaleX(currentValue);
                imageView.setScaleY(currentValue);
            }
        });
        spring.setEndValue(1.8f);
  • 使用时在SpringConfig构造方法中需要提供两个参数:拉力 摩擦力
  • 可以使用Spring的setCurrentValue、setEndValue方法来设置动画的初始值和结束值
  • 需要为spring添加一个listener,此处的SimpleSpringListener为Springlistener的实现类
  • onSpringUpdate方法中可以拿到动画执行过程中不断更新的值

例二 封装工具类使用:

弹性动画工具类:工具类是网上找的,可以根据自己需要进行修改

package com.leyard.filemanager.util;

import android.support.annotation.FloatRange;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;

import com.facebook.rebound.Spring;
import com.facebook.rebound.SpringConfig;
import com.facebook.rebound.SpringListener;
import com.facebook.rebound.SpringSystem;

public final class PrincipleSpring implements SpringListener {
    @SuppressWarnings("WeakerAccess")
    public interface PrincipleSpringListener {
        /**
         * @param value Spring 动画开始运行时的起始值
         */
        void onPrincipleSpringStart(float value);

        /**
         * @param value Spring 动画停止运行时的终点值
         */
        void onPrincipleSpringStop(float value);

        /**
         * @param value Spring 动画运行时的计算值
         */
        void onPrincipleSpringUpdate(float value);
    }

    private final Spring mSpring;
    private PrincipleSpringListener mListener;
    private boolean mIsActivate;

    public PrincipleSpring(@FloatRange(from = 0.0F) float tension, @FloatRange(from = 0.0F) float friction) {
        mSpring = SpringSystem.create().createSpring()
                .setSpringConfig(new SpringConfig(tension, friction))
                .addListener(this);
    }

    /**
     * @param listener 设置 Spring 动画的监听器
     * @return 当前 PrincipleSpring 实例,可以用于链式调用
     */
    public PrincipleSpring setListener(@Nullable PrincipleSpringListener listener) {
        mListener = listener;
        return this;
    }

    /**
     * 正向运行 Spring 动画;
     * 一般调用这个方法运行 Spring 动画即可
     */
    public void start() {
        mSpring.setEndValue(1.8F);
    }

    public void start(int from, int to) {
        mSpring.setCurrentValue(from);
        mSpring.setEndValue(to);
    }

    /**
     * 按照曲线倒过来运行 Spring 动画;
     * 如果你 {@link PrincipleSpring#start()} 之后想要倒过来运行的话
     */
    public void reset() {
        mSpring.setEndValue(0.0F);
    }

    /**
     * 取消(并重置)Spring 动画
     */
    public void cancel() {
        mSpring.setCurrentValue(0.0F);
    }

    /**
     * 获取 Spring 动画的 tension 值
     */
    @FloatRange(from = 0.0F)
    public float getTension() {
        return (float) mSpring.getSpringConfig().tension;
    }

    /**
     * @return 获取 Spring 动画 friction 值
     */
    @FloatRange(from = 0.0F)
    public float getFriction() {
        return (float) mSpring.getSpringConfig().friction;
    }

    @Override
    public void onSpringEndStateChange(@NonNull Spring spring) {
        // DO NOTHING
    }

    @Override
    public void onSpringActivate(@NonNull Spring spring) {
        if (!mIsActivate) {
            mIsActivate = true;
            if (mListener != null) {
                mListener.onPrincipleSpringStart((float) spring.getCurrentValue());
            }
        }
    }

    @Override
    public void onSpringAtRest(@NonNull Spring spring) {
        if (mIsActivate) {
            mIsActivate = false;
            if (mListener != null) {
                mListener.onPrincipleSpringStop((float) spring.getCurrentValue());
            }
        }
    }

    @Override
    public void onSpringUpdate(@NonNull Spring spring) {
        if (mListener != null) {
            mListener.onPrincipleSpringUpdate((float) spring.getCurrentValue());
        }
    }
}

实际应用中使用工具类:

图片弹性放大,效果如第一个gif

//start是一个button
start.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
    //这里传入两个参数,300是拉力  20是摩擦力
        spring = new PrincipleSpring(300, 20);
        spring.setListener(new PrincipleSpring.PrincipleSpringListener() {
            @Override
            public void onPrincipleSpringStart(float value) {
                Log.d(TAG, "onPrincipleSpringStart: value = " + value);
            }

            @Override
            public void onPrincipleSpringStop(float value) {
                Log.d(TAG, "onPrincipleSpringStop: value = " + value);
            }

            @Override
            public void onPrincipleSpringUpdate(float value) {
                Log.d(TAG, "onPrincipleSpringUpdate: value = " + value);
                float currentValue = value;
                img.setScaleX(currentValue);
                img.setScaleY(currentValue);
            }
        });
        spring.start();
    }
});

指示器弹性滑动,效果如下

弹性滑动
首先,黄色的指示器是一个imageview,我们通过改变它的leftMargin值,即属性动画来达到滑动的效果,要用属性动画给view设置动画的时候,要求view必须具有set和get,但是这有一个问题,imageview的leftMargin属性并没有提供get方法和set方法,因此我们要写一个包装类为其添加这两个方法

public  class ViewWrapper {
    private View mTarget;
    //这里的params必须是传入控件的父布局的params!!
    // 当然也可以直接在构造函数中传入params, 这样会保险点 ,
    // 不过params需要有下面的mParams.leftMargin 。。等对应方法!!
    private RelativeLayout.LayoutParams mParams ;

    public ViewWrapper(View target) {
        mTarget = target;
        mParams = (RelativeLayout.LayoutParams) mTarget.getLayoutParams();
    }
    public int getLeftMargin() {
        return mParams.leftMargin;
    }
    public void setLeftMargin(int margin) {
        mParams.setMargins(margin,0,0,0);
        mTarget.requestLayout();
    }
}

如上所示,我们提供了getLeftMargin和setLeftMargin方法;
接着在我们点击指示器对应的tab时,调用setMarginAnimation方法,传入LeftMargin的起始值和结束值。setMarginAnimation方法中我们使用了上文提到的工具类

private void setMarginAnimation(int startValue,int endValue){
spring = new PrincipleSpring(300, 20);
spring.setListener(new PrincipleSpring.PrincipleSpringListener() {
    @Override
    public void onPrincipleSpringStart(float value) {
        Log.d(TAG, "onPrincipleSpringStart: value = " + value);
    }

    @Override
    public void onPrincipleSpringStop(float value) {
        Log.d(TAG, "onPrincipleSpringStop: value = " + value);
    }

    @Override
    public void onPrincipleSpringUpdate(float value) {
        Log.d(TAG, "onPrincipleSpringUpdate: value = " + value);
        int currentValue = (int) value;
        new ViewWrapper(iv_tab_line).setLeftMargin(currentValue);
    }
});
spring.start(startValue, endValue);
}

rebound实现的弹性动画执行时间是跟传入的摩擦力和拉力有关。如果摩檫力为0,则动画会一直执行下去,摩擦力越大,动画执行时间越少。
rebound的效果是模拟弹簧振子的阻尼振动,根据牛顿运动定律,可以用如下微分方程表示:
在这里插入图片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章