使用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的效果是模擬彈簧振子的阻尼振動,根據牛頓運動定律,可以用如下微分方程表示: