聊聊對Android視圖滾動狀態(tài)的監(jiān)聽

在官方support.v4包里,提供給我們一個兼容類ViewCompat。ViewCompat里面針對幾個版本有不同的實現(xiàn)鞋邑,根據(jù)不同版本進行判斷, 但是要注意的是偷仿,ViewCompat僅僅讓你調(diào)用不崩潰受裹,并不保證你調(diào)用的結(jié)果在不同版本的機器上一致稼跳。
ViewCompat代碼組織的很優(yōu)雅匾效,有興趣可以看它的源碼惩妇。這幾天在看代碼時注意到ViewCompatcanScrollVerticallycanScrollHorizontally方法碧聪,官方注釋是這樣寫的:

Check if this view can be scrolled vertically in a certain direction.

也就是說冒版,在ViewCompatcanScrollVerticallycanScrollHorizontally方法中,我們可以來判斷一個控件是否可以繼續(xù)滾動逞姿,這就很有意思了辞嗡。如果我可以判斷出當前視圖是否可以繼續(xù)滾動,可以做出很多有意思的功能滞造,比如下拉刷新或者上拉加載更多等等续室。

那今天我們就來聊聊這個APIcanScrollVerticallycanScrollHorizontally與之同理谒养,希望可以幫助我們在自定義控件中提供新的思路挺狰。

我們先看看這個API的具體使用,首先买窟,我在布局文件中放入ScrollView控件丰泊,來看看效果:

//在MainActivity中
 @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
            Log.e(TAG, "canScrollVertically: "+ViewCompat.canScrollVertically(mScrollView,-1));
            return super.dispatchTouchEvent(ev);
        }

ViewCompat.canScrollVertically的第一個參數(shù)表示我們要判斷的控件,而第二個參數(shù)代表方向蔑祟,Negative to check scrolling up, positive to check scrolling down.也就是說趁耗,負數(shù)代表對下滑檢測,整數(shù)表示對上滑檢測疆虚,可以看下源碼:

private boolean canScrollingViewScrollVertically(ScrollingView view, int direction) {
           final int offset = view.computeVerticalScrollOffset();
           final int range = view.computeVerticalScrollRange() -
                   view.computeVerticalScrollExtent();
           if (range == 0) return false;
           if (direction < 0) {
               return offset > 0;
           } else {
               return offset < range - 1;
           }
       }

打印如下:

01-20 22:24:59.621 20224-20224/example.view.com.myapplication5 E/MainActivity: canScrollVertically: true
01-20 22:24:59.638 20224-20224/example.view.com.myapplication5 E/MainActivity: canScrollVertically: true
01-20 22:24:59.654 20224-20224/example.view.com.myapplication5 E/MainActivity: canScrollVertically: true
01-20 22:24:59.671 20224-20224/example.view.com.myapplication5 E/MainActivity: canScrollVertically: false
01-20 22:24:59.688 20224-20224/example.view.com.myapplication5 E/MainActivity: canScrollVertically: false
01-20 22:24:59.704 20224-20224/example.view.com.myapplication5 E/MainActivity: canScrollVertically: false
01-20 22:24:59.721 20224-20224/example.view.com.myapplication5 E/MainActivity: canScrollVertically: false
01-20 22:24:59.738 20224-20224/example.view.com.myapplication5 E/MainActivity: canScrollVertically: false
01-20 22:24:59.754 20224-20224/example.view.com.myapplication5 E/MainActivity: canScrollVertically: false

當手指向下滑到視圖最頂端的時候苛败,方法返回false,而當我將ViewCompat.canScrollVertically第二個參數(shù)設置為正數(shù)1時径簿,在手指上滑到頁面最低端時罢屈,方法返回false。

這樣的話篇亭,寫個小demo進一步熟悉這個API的使用缠捌,感覺以后可以在其他地方更多的用到這個。假設我們控件內(nèi)部是ScrollView或者ListViewRecyclerView译蒂,我們就可以很容易的判斷內(nèi)部控件的滑動狀態(tài)從而控制外部控件的滑動曼月,比如整個外部控件在ScrollView滑動到頂部時可以下拉滑出界面,我們就可以在滑動過程中用ViewCompat.canScrollVertically來判斷是否子控件滑動到頂部或者底部了柔昼,從而控制外部控件下滑操作:

canScrollVertically
    float mDownMotionY;
    boolean mChildHasScrolled;
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {

        switch (ev.getActionMasked()) {
            case MotionEvent.ACTION_DOWN:
                mDownMotionY = ev.getY();
                mChildHasScrolled = false;
                break;
            case MotionEvent.ACTION_MOVE:
                float offsetY = mDownMotionY - ev.getY();
                if (!canScrollVertically(view, (int) offsetY,ev)){
                    //說明滾動到底了
                    Log.e("TAG", "dispatchTouchEvent: "+offsetY);
                    if (offsetY > 0){
                        //說明滾動到底部了
//                        mScrollView.setTranslationY(-offsetY);
//                        return true;
                    }else {
                        //說明滾動到頂部了
                        view.setTranslationY(-offsetY);
                        return true;
                    }
                }
                break;
            case MotionEvent.ACTION_UP:

                if (view.getTranslationY() < (getWindow().getDecorView().findViewById(Window.ID_ANDROID_CONTENT).getHeight()/2)){
                    view.animate().translationY(0).setDuration(300).start();
                }else {
                    view.animate().translationY(getWindow().getDecorView().getHeight()).setDuration(300).start();
                }
                break;
            default:
                break;
        }
         
            return super.dispatchTouchEvent(ev);
    }
    /**
      * 判斷內(nèi)部子控件是否滑動到頂部/底部
      */
    private boolean canScrollVertically(View view, int offSet, MotionEvent ev) {

        if (!mChildHasScrolled && !isTransformedTouchPointInView(ev, view)) {
            return false;
        }
        if (ViewCompat.canScrollVertically(view, offSet)) {
            mChildHasScrolled = true;
            return true;
        }
        if (view instanceof ViewGroup) {
            ViewGroup vGroup = (ViewGroup) view;
            for (int i = 0; i < vGroup.getChildCount(); i++) {
                if (canScrollVertically(vGroup.getChildAt(i), offSet, ev)) {
                    mChildHasScrolled = true;
                    return true;
                }
            }
        }
        return false;
    }
    /***
     * 判斷MotionEvent是否處于View上面
     */
    protected boolean isTransformedTouchPointInView(MotionEvent ev, View view) {
        float x = ev.getRawX();
        float y = ev.getRawY();
        int[] rect = new int[2];
        view.getLocationInWindow(rect);
        float localX = x - rect[0];
        float localY = y - rect[1];
        return localX >= 0 && localX < (view.getRight() - view.getLeft())
                && localY >= 0 && localY < (view.getBottom() - view.getTop());
    }

當然如果發(fā)散思維想一下的話哑芹,那上拉加載更多我們就完全可以將ScrollViewListViewRecyclerView放在我們自己定義的控件中捕透,判斷其狀態(tài)顯示上拉加載或者下拉顯示更多聪姿,從而與各個控件處理邏輯完全解耦碴萧。當然,我覺得自己還有其他的更有意思的玩法末购,以后拿來嘗試嘗試吧~

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末破喻,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子盟榴,更是在濱河造成了極大的恐慌曹质,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,386評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件曹货,死亡現(xiàn)場離奇詭異咆繁,居然都是意外死亡,警方通過查閱死者的電腦和手機顶籽,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評論 3 394
  • 文/潘曉璐 我一進店門玩般,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人礼饱,你說我怎么就攤上這事坏为。” “怎么了镊绪?”我有些...
    開封第一講書人閱讀 164,704評論 0 353
  • 文/不壞的土叔 我叫張陵匀伏,是天一觀的道長。 經(jīng)常有香客問我蝴韭,道長够颠,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,702評論 1 294
  • 正文 為了忘掉前任榄鉴,我火速辦了婚禮履磨,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘庆尘。我一直安慰自己剃诅,他們只是感情好,可當我...
    茶點故事閱讀 67,716評論 6 392
  • 文/花漫 我一把揭開白布驶忌。 她就那樣靜靜地躺著矛辕,像睡著了一般。 火紅的嫁衣襯著肌膚如雪付魔。 梳的紋絲不亂的頭發(fā)上聊品,一...
    開封第一講書人閱讀 51,573評論 1 305
  • 那天,我揣著相機與錄音几苍,去河邊找鬼杨刨。 笑死,一個胖子當著我的面吹牛擦剑,可吹牛的內(nèi)容都是我干的妖胀。 我是一名探鬼主播,決...
    沈念sama閱讀 40,314評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼惠勒,長吁一口氣:“原來是場噩夢啊……” “哼赚抡!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起纠屋,我...
    開封第一講書人閱讀 39,230評論 0 276
  • 序言:老撾萬榮一對情侶失蹤涂臣,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后售担,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體赁遗,經(jīng)...
    沈念sama閱讀 45,680評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,873評論 3 336
  • 正文 我和宋清朗相戀三年族铆,在試婚紗的時候發(fā)現(xiàn)自己被綠了岩四。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,991評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡哥攘,死狀恐怖剖煌,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情逝淹,我是刑警寧澤耕姊,帶...
    沈念sama閱讀 35,706評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站栅葡,受9級特大地震影響茉兰,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜欣簇,卻給世界環(huán)境...
    茶點故事閱讀 41,329評論 3 330
  • 文/蒙蒙 一规脸、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧醉蚁,春花似錦燃辖、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,910評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至滥玷,卻和暖如春氏身,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背惑畴。 一陣腳步聲響...
    開封第一講書人閱讀 33,038評論 1 270
  • 我被黑心中介騙來泰國打工蛋欣, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人如贷。 一個月前我還...
    沈念sama閱讀 48,158評論 3 370
  • 正文 我出身青樓陷虎,卻偏偏與公主長得像到踏,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子尚猿,可洞房花燭夜當晚...
    茶點故事閱讀 44,941評論 2 355

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