自定義具有拉伸阻尼效果的ScrollerView

自定義具有拉伸阻尼效果的ScrollerView

引言
一切的自定義都是來(lái)自于需求,而在項(xiàng)目開(kāi)發(fā)中由于界面條目太多,所以自然而然的使用到了ScrollerView瑟捣,當(dāng)把效果給產(chǎn)品經(jīng)理的時(shí)候呢末荐,ios和Android的效果完全不一樣,ios自帶的上下拉伸回彈的效果勃救,而Android沒(méi)有碍讨,所以自定義一個(gè)具有拉伸效果的ScrollerView迫在眉睫啊.

首先來(lái)看一下效果圖,妹子很漂亮蒙秒,但是注意重點(diǎn)2颉!晕讲!

這里寫(xiě)圖片描述

好了覆获,下面進(jìn)入正題,通過(guò)繼承ScrollerView來(lái)進(jìn)行相關(guān)滑動(dòng)回彈的效果實(shí)現(xiàn)瓢省,先來(lái)定義幾個(gè)變量:

 private View childView;// 子View(ScrollerView的唯一子類)
 private int y;// 點(diǎn)擊時(shí)y坐標(biāo)
 private Rect rect = new Rect();// 矩形(用來(lái)保存inner的初始狀態(tài)弄息,判斷是夠需要?jiǎng)赢?huà)回彈效果)
 

注釋打的也很清楚,然后我們先在該ScrollerView的xml布局加載完成后獲取ScrollerView的唯一子布局賦值給上面定義的childView:

/**
     * 在xml布局繪制為界面完成時(shí)調(diào)用勤婚,
     * 獲取ScrollerView中唯一的直系子布局(ScrollerView中不許包含一層ViewGroup摹量,有且只有一個(gè))
     */
    @Override
    protected void onFinishInflate() {
        if (getChildCount() > 0) {
            childView = getChildAt(0);
        }
        super.onFinishInflate();
    }

下面就是處理Touch事件了:

/**
     * touch 事件處理
     **/
    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        if (childView != null) {
           handleTouchEvent(ev);
        }
        return super.onTouchEvent(ev);
    }

    /***
     * 觸摸事件
     *
     * @param ev
     */
    public void handleTouchEvent(MotionEvent ev) {
        int action = ev.getAction();
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                y = (int) ev.getY();//按下的時(shí)候獲取到y(tǒng)坐標(biāo)
                break;
            case MotionEvent.ACTION_MOVE:
                int nowY = (int) ev.getY(); // 移動(dòng)時(shí)的實(shí)時(shí)y坐標(biāo)
                int delayY = y - nowY;  // 移動(dòng)時(shí)的間隔
                y = nowY;  // 將本次移動(dòng)結(jié)束時(shí)的y坐標(biāo)賦值給下次移動(dòng)的起始坐標(biāo)(也就是nowY)
                if (isNeedMove()) {
                    if (rect.isEmpty()) {
                        //rect保存childView的初始位置信息
                        rect.set(childView.getLeft(), childView.getTop(), childView.getRight(), childView.getBottom());
                    }
                    //移動(dòng)布局(屏幕移動(dòng)的距離等于手指滑動(dòng)距離的一般)
                    childView.layout(childView.getLeft(), childView.getTop() - delayY / 2, childView.getRight(), childView.getBottom() - delayY / 2);
                }

                break;
            case MotionEvent.ACTION_UP:
                if (isNeedAnimation()) {// 判斷rect是否為空,也就是是否被重置了
                    startAnim();//開(kāi)始回彈動(dòng)畫(huà)
                }
                break;
            default:
                break;

        }
    }
    

對(duì)于Touch事件的處理,我注釋說(shuō)的應(yīng)該很清楚缨称,但是里面有需要調(diào)用的四個(gè)方法:

  • 判斷布局是否需要移動(dòng)
  /**
     *  判斷布局是否需要移動(dòng)
     * @return
     */
    private boolean isNeedMove() {
        int offset = childView.getMeasuredHeight() - getHeight();
        int scrollY = getScrollY();
        // 0是頂部凝果,后面那個(gè)是底部(需要仔細(xì)想一下這個(gè)過(guò)程)
        if (scrollY == 0 || scrollY == offset) {
            return true;
        }
        return false;
    }

其中childView.getMeasuredHeight()為獲取到該布局的實(shí)際高度,getHeight是該布局在屏幕中顯示的高度睦尽,getScrollY()是滑動(dòng)的時(shí)候相對(duì)于起始位置的距離器净。

  • 判斷rect是否為空

在加載布局的時(shí)候rect進(jìn)行了初始化,當(dāng)確定需要滑動(dòng)時(shí)骂删,再判斷一下rect是否為空掌动,因?yàn)樵搑ect在布局執(zhí)行動(dòng)畫(huà)回彈之后就會(huì)被置空,如果當(dāng)Scroller頂部對(duì)其或者底部對(duì)其宁玫,未在回彈過(guò)程就會(huì)將該時(shí)刻Scroller的位置信息傳入到rect粗恢,方便回彈的時(shí)候根據(jù)rect保存的scrollerview的位置信息完成回彈作用。

  • 判斷是否需要?jiǎng)赢?huà)
 public boolean isNeedAnimation() {
        return !rect.isEmpty();
    }
  • 執(zhí)行動(dòng)畫(huà)回彈
  private void startAnim() {
        TranslateAnimation anim = new TranslateAnimation(0, 0, childView.getTop(), rect.top);
        anim.setDuration(200);
        anim.setInterpolator(new OvershootInterpolator());//加速器
        childView.startAnimation(anim);
        // 將inner布局重新回到起始位置
        childView.layout(rect.left, rect.top, rect.right, rect.bottom);
        rect.setEmpty();

    }

在執(zhí)行完動(dòng)畫(huà)回彈后即ScrollerView回歸了原始狀態(tài)欧瘪,于是rect也就置空眷射,方便下一次繼續(xù)記錄,好了佛掖,下面直接貼一份完整代碼吧:

import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.OvershootInterpolator;
import android.view.animation.TranslateAnimation;
import android.widget.ScrollView;

/**
 * Author   : luweicheng on 2017/7/20 0020 15:15
 * E-mail   :1769005961@qq.com
 * GitHub   : https://github.com/luweicheng24
 * funcation: 具有拉伸效果的ScrollerView
 */

public class CustomScroller extends ScrollView {
    private View childView;// 子View(ScrollerView的唯一子類)
    private int y;// 點(diǎn)擊時(shí)y坐標(biāo)
    private Rect rect = new Rect();// 矩形(用來(lái)保存inner的初始狀態(tài)妖碉,判斷是夠需要?jiǎng)赢?huà)回彈效果)
    public CustomScroller(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    /**
     * 在xml布局繪制為界面完成時(shí)調(diào)用,
     * 獲取ScrollerView中唯一的直系子布局(ScrollerView中不許包含一層ViewGroup芥被,有且只有一個(gè))
     */
    @Override
    protected void onFinishInflate() {
        if (getChildCount() > 0) {
            childView = getChildAt(0);
        }
        super.onFinishInflate();
    }
    /**
     * touch 事件處理
     **/
    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        if (childView != null) {
           handleTouchEvent(ev);
        }
        return super.onTouchEvent(ev);
    }

    /***
     * 觸摸事件
     *
     * @param ev
     */
    public void handleTouchEvent(MotionEvent ev) {
        int action = ev.getAction();
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                y = (int) ev.getY();//按下的時(shí)候獲取到y(tǒng)坐標(biāo)
                break;
            case MotionEvent.ACTION_MOVE:
                int nowY = (int) ev.getY(); // 移動(dòng)時(shí)的實(shí)時(shí)y坐標(biāo)
                int delayY = y - nowY;  // 移動(dòng)時(shí)的間隔
                y = nowY;  // 將本次移動(dòng)結(jié)束時(shí)的y坐標(biāo)賦值給下次移動(dòng)的起始坐標(biāo)(也就是nowY)
                if (isNeedMove()) {
                    if (rect.isEmpty()) {
                        //rect保存childView的初始位置信息
                        rect.set(childView.getLeft(), childView.getTop(), childView.getRight(), childView.getBottom());
                    }
                    //移動(dòng)布局(屏幕移動(dòng)的距離等于手指滑動(dòng)距離的一般)
                    childView.layout(childView.getLeft(), childView.getTop() - delayY / 2, childView.getRight(), childView.getBottom() - delayY / 2);
                }

                break;
            case MotionEvent.ACTION_UP:
                if (isNeedAnimation()) {// 判斷rect是否為空欧宜,也就是是否被重置了
                    startAnim();//開(kāi)始回彈動(dòng)畫(huà)
                }
                break;
            default:
                break;

        }
    }
    private void startAnim() {
        TranslateAnimation anim = new TranslateAnimation(0, 0, childView.getTop(), rect.top);
        anim.setDuration(200);
        anim.setInterpolator(new OvershootInterpolator());//加速器
        childView.startAnimation(anim);
        // 將inner布局重新回到起始位置
        childView.layout(rect.left, rect.top, rect.right, rect.bottom);
        rect.setEmpty();

    }
    /**
     *  判斷布局是否需要移動(dòng)
     * @return
     */
    private boolean isNeedMove() {
        int offset = childView.getMeasuredHeight() - getHeight();
        int scrollY = getScrollY();
        // 0是頂部,后面那個(gè)是底部(需要仔細(xì)想一下這個(gè)過(guò)程)
        if (scrollY == 0 || scrollY == offset) {
            return true;
        }
        return false;
    }
    public boolean isNeedAnimation() {
        return !rect.isEmpty();
    }
}

如果有問(wèn)題拴魄,在下面留言冗茸,共同探討。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末匹中,一起剝皮案震驚了整個(gè)濱河市夏漱,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌顶捷,老刑警劉巖挂绰,帶你破解...
    沈念sama閱讀 212,884評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異服赎,居然都是意外死亡葵蒂,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,755評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)重虑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)刹勃,“玉大人,你說(shuō)我怎么就攤上這事嚎尤。” “怎么了伍宦?”我有些...
    開(kāi)封第一講書(shū)人閱讀 158,369評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵芽死,是天一觀的道長(zhǎng)乏梁。 經(jīng)常有香客問(wèn)我,道長(zhǎng)关贵,這世上最難降的妖魔是什么遇骑? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,799評(píng)論 1 285
  • 正文 為了忘掉前任,我火速辦了婚禮揖曾,結(jié)果婚禮上落萎,老公的妹妹穿的比我還像新娘。我一直安慰自己炭剪,他們只是感情好练链,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,910評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著奴拦,像睡著了一般媒鼓。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上错妖,一...
    開(kāi)封第一講書(shū)人閱讀 50,096評(píng)論 1 291
  • 那天绿鸣,我揣著相機(jī)與錄音,去河邊找鬼暂氯。 笑死潮模,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的痴施。 我是一名探鬼主播擎厢,決...
    沈念sama閱讀 39,159評(píng)論 3 411
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼晾剖!你這毒婦竟也來(lái)了锉矢?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,917評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤齿尽,失蹤者是張志新(化名)和其女友劉穎沽损,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體循头,經(jīng)...
    沈念sama閱讀 44,360評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡绵估,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,673評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了卡骂。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片国裳。...
    茶點(diǎn)故事閱讀 38,814評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖全跨,靈堂內(nèi)的尸體忽然破棺而出缝左,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 34,509評(píng)論 4 334
  • 正文 年R本政府宣布渺杉,位于F島的核電站蛇数,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏是越。R本人自食惡果不足惜耳舅,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,156評(píng)論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望倚评。 院中可真熱鬧浦徊,春花似錦、人聲如沸天梧。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,882評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)腿倚。三九已至纯出,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間敷燎,已是汗流浹背暂筝。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,123評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留硬贯,地道東北人焕襟。 一個(gè)月前我還...
    沈念sama閱讀 46,641評(píng)論 2 362
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像饭豹,于是被迫代替她去往敵國(guó)和親鸵赖。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,728評(píng)論 2 351

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,871評(píng)論 25 707
  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫(kù)拄衰、插件它褪、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,066評(píng)論 4 62
  • 遇見(jiàn)一對(duì)情侶,在很久以前翘悉。 兩人真的是不能再般配了茫打,一樣的體型一樣的高矮,很胖但胖的很可愛(ài)妖混。 又遇見(jiàn)這對(duì)情侶老赤,在很...
    啥也布時(shí)閱讀 164評(píng)論 2 1
  • 第三周一周培訓(xùn)每天來(lái)回跑,節(jié)奏有點(diǎn)打亂制市!真心覺(jué)得上班的節(jié)奏點(diǎn)可以做好多自己想做的事抬旺,很開(kāi)心!接下來(lái)是忙微課的事祥楣,加...
    HeidiQ閱讀 221評(píng)論 0 0
  • 有序組織原理: 5.僅僅因?yàn)橐恢倍及凑漳撤N特定方式做某事,并不意味著就該永遠(yuǎn)這樣做责鳍。 6.知識(shí)不是力量竭翠,共享知識(shí)才...
    懶兔少女閱讀 142評(píng)論 0 0