滑動回彈與內層listview的滑動沖突


title: 滑動回彈與內層listview的滑動沖突
date: 2016-12-06 10:33:27
tags: problems


  • 需求:

1蛛勉、隨著下拉孤澎,view發(fā)生位移蛙酪,松開回彈到原來的位置

2、內部的listview可以正常的上下滑動

3熊赖、listview滑到頂部的時候疲牵,繼續(xù)下拉承二,則是拉動整個外部view,并且松開回彈

這3個需求就會造成事件沖突纲爸,那么處理方式就是:listview不是初始狀態(tài)就是listview自己處理事件矢洲,listview還原到了初始狀態(tài),外部view處理下拉回彈事件缩焦。


需求一個一個的實現读虏,首先第一個下拉回彈

因為里面還要套一個listview,所以我們自定義一個view繼承自viewGroup袁滥,這里選擇的是LinearLayout

package com.aidebar.demo;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.LinearLayout;

/**
 * @author xzj
 * @date 2016/12/6 10:54.
 */

public class MyView extends LinearLayout {
    private int startY;
    private int moveY;
    private int diffY;

    public MyView(Context context) {
        super(context);
    }

    public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
      
    }
  
  @Override
    public boolean onTouchEvent(MotionEvent event) {
        int y = (int) event.getY(); //getY()獲取的是按下去的位置在view中的縱坐標
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                startY = y;
                break;
            case MotionEvent.ACTION_MOVE:
                moveY = y;
                Log.d("myview", "moveY="+moveY+"|startY="+startY);
                //當往下滑動的時候
                if ((moveY - startY) > 0) {
                    //獲取到位移距離盖桥,并改變布局參數讓view動起來
                    layout(getLeft(), getTop() + (moveY - startY), getRight(), getBottom() + (moveY - startY));
                    //diffY是用來記錄總共的位移數據的,用于在ACTION_UP中還原
                   //每次走進move都位移了一點點题翻,就重新布局一次揩徊,把每次位移的這一點點累加起來
                    diffY += (moveY - startY);
                }
                break;
            case MotionEvent.ACTION_UP:
                //  在ACTION_UP中就不能用moveY-startY了
                //  因為每次走到ACTION_MOVE的時候moveY獲取的是觸摸點離view上邊界的距離腰鬼,在ACTION_MOVE里重新布局后moveY離上邊界肯定是固定的,startY在不放手的情況下也是固定的
                //  所以如果用moveY-startY的話會是0塑荒,就無法回彈了
                layout(getLeft(), getTop() - diffY, getRight(), getBottom() - diffY);
                diffY = 0;
                break;
        }
        return true;
    }
}


OK熄赡,實現第二條需求,讓內部listview可以滑動齿税,要讓子view可以接受到MotionEvent彼硫,首先我們自己就不能攔截,那么重寫onInterceptTouchEvent()

@Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        boolean isIntercept=false;
        int y = (int) ev.getY();
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                startY = y;
                break;
            case MotionEvent.ACTION_MOVE:
                moveY = y;
                //當往下滑動的時候才攔截凌箕,
                if ((moveY - startY) > 0) {
                    isIntercept = true;
                }else {
                    isIntercept = false;
                }
                break;
            case MotionEvent.ACTION_UP:
                break;
        }
        return isIntercept;
    }

好拧篮,攔截方法寫完了,但這樣的話牵舱,所有向下滑動都被我們攔截了串绩,listview就不能向下滑了。

但這是listview的事情芜壁,應該由listview來做判斷礁凡,什么時候攔截什么時候不攔截。

自定義一個MyListView慧妄,繼承自ListView顷牌,并重寫onTouchEvent()

  @Override
    public boolean onTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                 //down被攔截了,后續(xù)所有事件就都收不到了
                getParent().requestDisallowInterceptTouchEvent(true); 
                break;
            case MotionEvent.ACTION_MOVE:
                if (getScrollY() == 0) {
                    //只有當listview還原了腰涧,才將事件交給外層,由外層攔截事件
                    getParent().requestDisallowInterceptTouchEvent(false); 
                }else {
                    getParent().requestDisallowInterceptTouchEvent(true);
                }
                break;
        }
        return super.onTouchEvent(ev);
    }

這么寫會發(fā)現無效紊浩,因為listview不想scrollview窖铡,它沒有重寫getScrollY()方法,直接調用的是父類view的方法坊谁,返回值永遠是0. 沒寫也沒關系费彼,我們自己寫

//listview沒有重寫getScrollY方法,我們只能自己寫口芍,可是這方法是final的箍铲。。所以不要吐槽名字
    public int getScrollY1() {
        View v = getChildAt(0);
        if (v == null) {
            return 0;
        }
        int firstVisiblePosition = getFirstVisiblePosition();
      //top的值肯定是<=0的鬓椭,因為第一個view完全展示的時候top為0颠猴,滑上去了就是負數
        int top = v.getTop();
        return -top + firstVisiblePosition * v.getHeight() ;
    }

將上面的getScrollY()替換成getScrollY1()即可。

OK小染,listview可以正城涛停滑動了,第二條需求完成


大功告成裤翩?太年輕了资盅。。

你會發(fā)現可以下拉回彈,listview可以上下滑動并且下拉回彈呵扛,但是每庆!

你先把listview往上滑一下,松手今穿,然后再下拉試試

會發(fā)現在臨界狀態(tài)下缤灵,外部的view突然往下移動了一大截。

為什么不松手的情況下荣赶,listview可以上下滑動凤价,滑到頂了外部view可以正常下拉并回彈,而先滑動一次listview就不行了呢拔创?

因為我們在外部view的onInterceptTouchEvent()里獲取到了startY利诺,所以當listview復原的時候的moveY和這個startY是相等的,外部view就可以正常的下拉回彈剩燥。

而先滑動一次listview后慢逾,再次點擊滑動,獲取到的是一個新的startY灭红,而此時你要把listview復原的moveY是大于startY的侣滩,所以listview滑到頂的時候再下拉,布局會突然下降一截变擒。

知道原因就好解決了君珠,在listview處理滑動事件的時候,復原的時候娇斑,將moveY的值給外部view的startY賦值就行了唄策添!

怎么傳值,請看RxBus工具類毫缆,這是用rxjava寫的一個取代EventBus的工具唯竹。


在MyView中初始化的時候注冊一下

public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }
    
    private void init() {
        RxBus.getInstance().toObservable(Integer.class,"startY")
                .subscribe(new RxBusSubscriber<Integer>() {
                    @Override
                    public void receive(Integer data) {
                        startY = data;
                    }
                });
    }

在MyListView中加一句

@Override
    public boolean onTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                getParent().requestDisallowInterceptTouchEvent(true); 
                break;
            case MotionEvent.ACTION_MOVE:
                if (getScrollY1() == 0) {
                  //這里加一句,將此時的坐標發(fā)給MyView
                    RxBus.getInstance().send((int)ev.getY(),"startY");  
                    getParent().requestDisallowInterceptTouchEvent(false); 
                }else {
                    getParent().requestDisallowInterceptTouchEvent(true);
                }
                break;
        }

OK苦丁,全部搞定浸颓,收工~

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市旺拉,隨后出現的幾起案子产上,更是在濱河造成了極大的恐慌,老刑警劉巖蛾狗,帶你破解...
    沈念sama閱讀 221,548評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蒂秘,死亡現場離奇詭異,居然都是意外死亡淘太,警方通過查閱死者的電腦和手機姻僧,發(fā)現死者居然都...
    沈念sama閱讀 94,497評論 3 399
  • 文/潘曉璐 我一進店門规丽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人撇贺,你說我怎么就攤上這事赌莺。” “怎么了松嘶?”我有些...
    開封第一講書人閱讀 167,990評論 0 360
  • 文/不壞的土叔 我叫張陵艘狭,是天一觀的道長。 經常有香客問我翠订,道長巢音,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,618評論 1 296
  • 正文 為了忘掉前任尽超,我火速辦了婚禮官撼,結果婚禮上,老公的妹妹穿的比我還像新娘似谁。我一直安慰自己傲绣,他們只是感情好,可當我...
    茶點故事閱讀 68,618評論 6 397
  • 文/花漫 我一把揭開白布巩踏。 她就那樣靜靜地躺著秃诵,像睡著了一般。 火紅的嫁衣襯著肌膚如雪塞琼。 梳的紋絲不亂的頭發(fā)上菠净,一...
    開封第一講書人閱讀 52,246評論 1 308
  • 那天,我揣著相機與錄音彪杉,去河邊找鬼毅往。 笑死,一個胖子當著我的面吹牛在讶,可吹牛的內容都是我干的煞抬。 我是一名探鬼主播霜大,決...
    沈念sama閱讀 40,819評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼构哺,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了战坤?” 一聲冷哼從身側響起曙强,我...
    開封第一講書人閱讀 39,725評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎途茫,沒想到半個月后碟嘴,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 46,268評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡囊卜,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,356評論 3 340
  • 正文 我和宋清朗相戀三年娜扇,在試婚紗的時候發(fā)現自己被綠了错沃。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,488評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡雀瓢,死狀恐怖枢析,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情刃麸,我是刑警寧澤醒叁,帶...
    沈念sama閱讀 36,181評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站泊业,受9級特大地震影響把沼,放射性物質發(fā)生泄漏。R本人自食惡果不足惜吁伺,卻給世界環(huán)境...
    茶點故事閱讀 41,862評論 3 333
  • 文/蒙蒙 一饮睬、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧箱蝠,春花似錦续捂、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,331評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至间校,卻和暖如春矾克,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背憔足。 一陣腳步聲響...
    開封第一講書人閱讀 33,445評論 1 272
  • 我被黑心中介騙來泰國打工胁附, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人滓彰。 一個月前我還...
    沈念sama閱讀 48,897評論 3 376
  • 正文 我出身青樓控妻,卻偏偏與公主長得像,于是被迫代替她去往敵國和親揭绑。 傳聞我的和親對象是個殘疾皇子弓候,可洞房花燭夜當晚...
    茶點故事閱讀 45,500評論 2 359

推薦閱讀更多精彩內容