彈性動畫的實現

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

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