實現(xiàn)一個帶下拉彈簧動畫的 ScrollView

在剛推出的 Support Library 25.3.0 里面新增了一個叫 SpringAnimation 的動畫,也就是彈簧動畫。要是用它來做一個滑動控件下拉回彈的效果,應該不錯吧。

SpringAnimation

開始之前,別忘了在 app 的 build.gradle 加上:

compile 'com.android.support:appcompat-v7:25.3.0'
compile 'com.android.support:design:25.3.0'
compile 'com.android.support:support-dynamic-animation:25.3.0'

然后我們看看 SpringAnimation 的基本用法,首先是它的構造方法:

public SpringAnimation(View v, ViewProperty property, float finalPosition) {
    super(v, property);
    mSpring = new SpringForce(finalPosition);
    setSpringThreshold();
}

看命名可以大概猜到參數(shù)的意義了:

  • v - 要執(zhí)行動畫的控件
  • property - 動畫的性質,可以選擇平移、縮放、旋轉等
  • finalPosition - 動畫結束時,控件所在位置的坐標偏移量

這里實現(xiàn)的滑動控件是上下滑動的,所以我們這樣來獲取 SpringAnimation :

springAnim = new SpringAnimation(this, SpringAnimation.TRANSLATION_Y, 0);

SpringAnimation 里面有兩個比較重要的屬性,分別是:

  • Stiffness - 剛度,值越大回彈的速度越快,類似于勁度系數(shù),默認值是 1500f
  • DampingRatio - 阻尼,值越小,回彈后,動畫來回的次數(shù)越多,就是更有「DUANG」的感覺,默認值是 0.5f

通過

springAnim.getSpring().setStiffness(float stiffness) 

springAnim.getSpring().setDampingRatio(float dampingRatio) 

來設置上面兩個屬性。

再調用 springAnim.start() 就可以開始動畫啦。

SpringScrollView

我們自定義一個 SpringScrollView 繼承 NestedScrollView,重寫 onTouchEvent 方法讓它有回彈的效果:

@Override
public boolean onTouchEvent(MotionEvent e) {
    switch (e.getAction()) {
        case MotionEvent.ACTION_MOVE:
            if (getScrollY() <= 0) {
                //頂部下拉
                if (startDragY == 0) {
                    startDragY = e.getRawY();
                }
                if (e.getRawY() - startDragY > 0) {
                    setTranslationY((e.getRawY() - startDragY) / 3);
                    return true;
                } else {
                    springAnim.cancel();
                    setTranslationY(0);
                }
            } 
            break;
        case MotionEvent.ACTION_UP:
        case MotionEvent.ACTION_CANCEL:
            if (getTranslationY() != 0) {
                springAnim.start();
            }
            startDragY = 0;
            break;
    }
    return super.onTouchEvent(e);
}

簡單解釋一下哈。

當 ScrollView 在頂部時,記錄下手指所在的 y 軸位置。在頂部并且是往下滑動的時候,給 ScrollView 設置一個縱向的偏移。之所以除以 3,是為了讓控件有種要用力才能拖動的感覺。

在頂部的時候如果是往上滑動,則把動畫效果取消,把控件位置復原,否則可能出現(xiàn)控件一直偏移的情況。

最后當手指抬起時,執(zhí)行彈簧動畫就好了。

為什么這里用 getRawY() 獲取坐標,而不是用 getY() 來獲取。因為 getY() 是相對于控件的坐標,當設置了 TranslationY 之后會改變它的值,也就是在滑動的時候 getY() 的值是不連續(xù)的,會出現(xiàn)卡頓的現(xiàn)象。而 getRawY() 是相對于屏幕的位置,管你控件怎么動,屏幕都是固定的。

下拉回彈的效果就已經(jīng)完成了。對了,我們順便把底部上拉的回彈也做一下唄。由于ScrollView只有一個子布局,所以可以通過

getScrollY() + getHeight()) >= getChildAt(0).getMeasuredHeight()

判斷是否滑動到了底部。

完整代碼如下:

public class SpringScrollView extends NestedScrollView {

    private float startDragY;
    private SpringAnimation springAnim;

    public SpringScrollView(Context context) {
        this(context, null);
    }

    public SpringScrollView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public SpringScrollView(Context context, @Nullable AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        springAnim = new SpringAnimation(this, SpringAnimation.TRANSLATION_Y, 0);
        //剛度 默認1200 值越大回彈的速度越快
        springAnim.getSpring().setStiffness(800.0f);
        //阻尼 默認0.5 值越小,回彈之后來回的次數(shù)越多
        springAnim.getSpring().setDampingRatio(0.50f);
    }

    @Override
    public boolean onTouchEvent(MotionEvent e) {
        switch (e.getAction()) {
            case MotionEvent.ACTION_MOVE:
                if (getScrollY() <= 0) {
                    //頂部下拉
                    if (startDragY == 0) {
                        startDragY = e.getRawY();
                    }
                    if (e.getRawY() - startDragY > 0) {
                        setTranslationY((e.getRawY() - startDragY) / 3);
                        return true;
                    } else {
                        springAnim.cancel();
                        setTranslationY(0);
                    }
                } else if ((getScrollY() + getHeight()) >= getChildAt(0).getMeasuredHeight()) {
                    //底部上拉
                    if (startDragY == 0) {
                        startDragY = e.getRawY();
                    }
                    if (e.getRawY() - startDragY < 0) {
                        setTranslationY((e.getRawY() - startDragY) / 3);
                        return true;
                    } else {
                        springAnim.cancel();
                        setTranslationY(0);
                    }
                }
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                if (getTranslationY() != 0) {
                    springAnim.start();
                }
                startDragY = 0;
                break;
        }
        return super.onTouchEvent(e);
    }

}

最后看看效果吧:

同樣的思路也可以用在別的滑動控件里面。

妥妥的。

源碼地址

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,527評論 6 544
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,687評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,640評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,957評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,682評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 56,011評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,009評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,183評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,714評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,435評論 3 359
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,665評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,148評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,838評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,251評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,588評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,379評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,627評論 2 380

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,765評論 25 708
  • 發(fā)現(xiàn) 關注 消息 iOS 第三方庫、插件、知名博客總結 作者大灰狼的小綿羊哥哥關注 2017.06.26 09:4...
    肇東周閱讀 12,179評論 4 61
  • 項目中用到了商品詳情展示效果,所以立馬想到借鑒天貓商品詳情界面,看了天貓的詳情頁面想到了兩套解決方案。1,使用Li...
    ___ayo閱讀 9,632評論 10 23
  • 今天晚上刷牙的時候牙齦出血了,正好大米進來洗手,大米很擔心的問我:媽媽你怎么了?我說:沒事,牙齦出血啦,牙周炎。小...
    萱玥媽閱讀 208評論 0 1
  • 在iOS開發(fā)中,會經(jīng)常用到RunLoop,面試的時候更是必問的東西,RunLoop也是iOS中非常重要的東西,趁著...
    我系哆啦閱讀 321評論 0 3