使用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的效果是模拟弹簧振子的阻尼振动,根据牛顿运动定律,可以用如下微分方程表示: