滑動回彈與內(nèi)層listview的滑動沖突


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


  • 需求:

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

2、內(nèi)部的listview可以正常的上下滑動

3、listview滑到頂部的時(shí)候,繼續(xù)下拉,則是拉動整個(gè)外部view,并且松開回彈

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


需求一個(gè)一個(gè)的實(shí)現(xiàn),首先第一個(gè)下拉回彈

因?yàn)槔锩孢€要套一個(gè)listview,所以我們自定義一個(gè)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中的縱坐標(biāo)
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                startY = y;
                break;
            case MotionEvent.ACTION_MOVE:
                moveY = y;
                Log.d("myview", "moveY="+moveY+"|startY="+startY);
                //當(dāng)往下滑動的時(shí)候
                if ((moveY - startY) > 0) {
                    //獲取到位移距離,并改變布局參數(shù)讓view動起來
                    layout(getLeft(), getTop() + (moveY - startY), getRight(), getBottom() + (moveY - startY));
                    //diffY是用來記錄總共的位移數(shù)據(jù)的,用于在ACTION_UP中還原
                   //每次走進(jìn)move都位移了一點(diǎn)點(diǎn),就重新布局一次,把每次位移的這一點(diǎn)點(diǎn)累加起來
                    diffY += (moveY - startY);
                }
                break;
            case MotionEvent.ACTION_UP:
                //  在ACTION_UP中就不能用moveY-startY了
                //  因?yàn)槊看巫叩紸CTION_MOVE的時(shí)候moveY獲取的是觸摸點(diǎn)離view上邊界的距離,在ACTION_MOVE里重新布局后moveY離上邊界肯定是固定的,startY在不放手的情況下也是固定的
                //  所以如果用moveY-startY的話會是0,就無法回彈了
                layout(getLeft(), getTop() - diffY, getRight(), getBottom() - diffY);
                diffY = 0;
                break;
        }
        return true;
    }
}


OK,實(shí)現(xiàn)第二條需求,讓內(nèi)部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;
                //當(dāng)往下滑動的時(shí)候才攔截,
                if ((moveY - startY) > 0) {
                    isIntercept = true;
                }else {
                    isIntercept = false;
                }
                break;
            case MotionEvent.ACTION_UP:
                break;
        }
        return isIntercept;
    }

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

但這是listview的事情,應(yīng)該由listview來做判斷,什么時(shí)候攔截什么時(shí)候不攔截。

自定義一個(gè)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) {
                    //只有當(dāng)listview還原了,才將事件交給外層,由外層攔截事件
                    getParent().requestDisallowInterceptTouchEvent(false); 
                }else {
                    getParent().requestDisallowInterceptTouchEvent(true);
                }
                break;
        }
        return super.onTouchEvent(ev);
    }

這么寫會發(fā)現(xiàn)無效,因?yàn)閘istview不想scrollview,它沒有重寫getScrollY()方法,直接調(diào)用的是父類view的方法,返回值永遠(yuǎn)是0. 沒寫也沒關(guān)系,我們自己寫

//listview沒有重寫getScrollY方法,我們只能自己寫,可是這方法是final的。。所以不要吐槽名字
    public int getScrollY1() {
        View v = getChildAt(0);
        if (v == null) {
            return 0;
        }
        int firstVisiblePosition = getFirstVisiblePosition();
      //top的值肯定是<=0的,因?yàn)榈谝粋€(gè)view完全展示的時(shí)候top為0,滑上去了就是負(fù)數(shù)
        int top = v.getTop();
        return -top + firstVisiblePosition * v.getHeight() ;
    }

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

OK,listview可以正?;瑒恿?,第二條需求完成


大功告成?太年輕了。。

你會發(fā)現(xiàn)可以下拉回彈,listview可以上下滑動并且下拉回彈,但是!

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

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

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

因?yàn)槲覀冊谕獠縱iew的onInterceptTouchEvent()里獲取到了startY,所以當(dāng)listview復(fù)原的時(shí)候的moveY和這個(gè)startY是相等的,外部view就可以正常的下拉回彈。

而先滑動一次listview后,再次點(diǎn)擊滑動,獲取到的是一個(gè)新的startY,而此時(shí)你要把listview復(fù)原的moveY是大于startY的,所以listview滑到頂?shù)臅r(shí)候再下拉,布局會突然下降一截。

知道原因就好解決了,在listview處理滑動事件的時(shí)候,復(fù)原的時(shí)候,將moveY的值給外部view的startY賦值就行了唄!

怎么傳值,請看RxBus工具類,這是用rxjava寫的一個(gè)取代EventBus的工具。


在MyView中初始化的時(shí)候注冊一下

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) {
                  //這里加一句,將此時(shí)的坐標(biāo)發(fā)給MyView
                    RxBus.getInstance().send((int)ev.getY(),"startY");  
                    getParent().requestDisallowInterceptTouchEvent(false); 
                }else {
                    getParent().requestDisallowInterceptTouchEvent(true);
                }
                break;
        }

OK,全部搞定,收工~

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

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