ViewPager,ScrollView 嵌套ViewPager滑動衝突解決

這篇博客主要講解一下幾個問題 
- 粗略地介紹一下View的事件分發機制 
- 解決事件滑動衝突的思路及方法 
- ScrollView 裏面嵌套ViewPager導致的滑動衝突 
- ViewPager裏面嵌套ViewPager 導致的滑動衝突 
- 輪播圖的幾種實現方式

先看一下效果圖

ScrollView裏面嵌套ViewPager

ViewPager裏面嵌套ViewPager


View的 事件分發機制

這篇博客大打算詳細講解View的事件分發機制,因爲網上已經出現了一系列的好 文章,我自己的水平也有限,目前肯定寫得不咋的。

先囉嗦一下,View 的事件分發機制主要涉及到一下三個 方法

  • dispatchTouchEvent ,這個方法主要是用來分發事件的
  • onInterceptTouchEvent,這個方法主要是用來攔截事件的(需要注意的是ViewGroup纔有這個方法,View沒有onInterceptTouchEvent這個方法
  • onTouchEvent這個方法主要是用來處理事件的
  • requestDisallowInterceptTouchEvent(true),這個方法能夠影響父View是否攔截事件,true表示 不攔截事件,false表示攔截事件

下面引用圖解 Android 事件分發機制這一篇博客的內容

  • 仔細看的話,圖分爲3層,從上往下依次是Activity、ViewGroup、View
  • 事件從左上角那個白色箭頭開始,由Activity的dispatchTouchEvent做分發
  • 箭頭的上面字代表方法返回值,(return true、return false、return super.xxxxx(),super 的意思是調用父類實現。
  • dispatchTouchEvent和 onTouchEvent的框裏有個【true—->消費】的字,表示的意思是如果方法返回true,那麼代表事件就此消費,不會繼續往別的地方傳了,事件終止。
  • 目前所有的圖的事件是針對ACTION_DOWN的,對於ACTION_MOVE和ACTION_UP我們最後做分析。
  • 之前圖中的Activity 的dispatchTouchEvent 有誤(圖已修復),只有return super.dispatchTouchEvent(ev) 纔是往下走,返回true 或者 false 事件就被消費了(終止傳遞)。

總結

當TouchEvent發生時,首先Activity將TouchEvent傳遞給最頂層的View,TouchEvent最先到達最頂層 view 的 dispatchTouchEvent ,然後由 dispatchTouchEvent 方法進行分發,

  • 如果dispatchTouchEvent返回true 消費事件,事件終結。
  • 如果dispatchTouchEvent返回 false ,則回傳給父View的onTouchEvent事件處理;

    onTouchEvent事件返回true,事件終結,返回false,交給父View的OnTouchEvent方法處理

  • 如果dispatchTouchEvent返回super的話,默認會調用自己的onInterceptTouchEvent方法

    默認的情況下interceptTouchEvent回調用super方法,super方法默認返回false,所以會交給子View的onDispatchTouchEvent方法處理

    如果 interceptTouchEvent 返回 true ,也就是攔截掉了,則交給它的 onTouchEvent 來處理,

    如果 interceptTouchEvent 返回 false ,那麼就傳遞給子 view ,由子 view 的 dispatchTouchEvent 再來開始這個事件的分發。

關於更多詳細分析,請查看原博客圖解 Android 事件分發機制,真心推薦,寫得很好。


解決事件滑動衝突的思路及方法

常見的三種情況

第一種情況,滑動方向不同

第二種情況,滑動方向相同

第三種情況,上述兩種情況的嵌套

解決思路

看了上面三種情況,我們知道他們的共同特點是父View 和子View都想爭着響應我們的觸摸事件,但遺憾的是我們的觸摸事件 同一時刻只能被某一個View或者ViewGroup攔截消費,所以就產生了滑動衝突?那既然同一時刻只能由某一個View或者ViewGroup消費攔截,那我們就只需要 決定在某個時刻由這個View或者ViewGroup攔截事件,另外的 某個時刻由 另外一個View或者ViewGroup攔截事件不就OK了嗎?綜上,正如 在 Android開發藝術》 一書提出的,總共 有兩種解決方案

以下解決思路來自於 《Android開發藝術》 書籍

下面的兩種方法針對第一種情況(滑動方向不同),父View是上下滑動,子View是左右滑動的情況。

外部解決法

從父View着手,重寫onInterceptTouchEvent方法,在父View需要攔截的時候攔截,不要的時候返回false,爲代碼大概 如下

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
    final float x = ev.getX();
    final float y = ev.getY();

    final int action = ev.getAction();
    switch (action) {
        case MotionEvent.ACTION_DOWN:
            mDownPosX = x;
            mDownPosY = y;

            break;
        case MotionEvent.ACTION_MOVE:
            final float deltaX = Math.abs(x - mDownPosX);
            final float deltaY = Math.abs(y - mDownPosY);
            // 這裏是夠攔截的判斷依據是左右滑動,讀者可根據自己的邏輯進行是否攔截
            if (deltaX > deltaY) {
                return false;
            }
    }

    return super.onInterceptTouchEvent(ev);
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

內部解決法

從子View着手,父View先不要攔截任何事件,所有的 事件傳遞給 子View,如果子View需要此事件就消費掉,不需要此事件的話就交給 父View處理。

實現思路 如下,重寫子 View的dispatchTouchEvent方法,在Action_down 動作中通過方法 requestDisallowInterceptTouchEvent(true) 先請求 父 View不要攔截事件,這樣保證 子View能夠 接受到Action_move事件,再在Action_move動作中根據 自己的邏輯是否要攔截事件,不要的 話再交給 父View處理

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    int x = (int) ev.getRawX();
    int y = (int) ev.getRawY();
    int dealtX = 0;
    int dealtY = 0;

    switch (ev.getAction()) {
        case MotionEvent.ACTION_DOWN:
            dealtX = 0;
            dealtY = 0;
            // 保證子View能夠接收到Action_move事件
            getParent().requestDisallowInterceptTouchEvent(true);
            break;
        case MotionEvent.ACTION_MOVE:
            dealtX += Math.abs(x - lastX);
            dealtY += Math.abs(y - lastY);
            Log.i(TAG, "dealtX:=" + dealtX);
            Log.i(TAG, "dealtY:=" + dealtY);
            // 這裏是夠攔截的判斷依據是左右滑動,讀者可根據自己的邏輯進行是否攔截
            if (dealtX >= dealtY) {
                getParent().requestDisallowInterceptTouchEvent(true);
            } else {
                getParent().requestDisallowInterceptTouchEvent(false);
            }
            lastX = x;
            lastY = y;
            break;
        case MotionEvent.ACTION_CANCEL:
            break;
        case MotionEvent.ACTION_UP:
            break;

    }
    return super.dispatchTouchEvent(ev);
}


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

ScrollView 裏面嵌套ViewPager導致的滑動衝突

外部解決法

如上面所述,從 父View ScrollView着手,重寫 OnInterceptTouchEvent方法,在上下滑動的時候攔截事件,在左右滑動的時候不攔截事件,返回 false,這樣確保子View 的dispatchTouchEvent方法會被調用,代碼 如下

/**
 * @ explain:這個ScrlloView不攔截水平滑動事件,
 * 是用來解決 ScrollView裏面嵌套ViewPager使用的
 * @ author:xujun on 2016/10/25 15:28
 * @ email:[email protected]
 */
public class VerticalScrollView extends ScrollView {

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

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

    public VerticalScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @TargetApi(21)
    public VerticalScrollView(Context context, AttributeSet attrs, int defStyleAttr, int
            defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    private float mDownPosX = 0;
    private float mDownPosY = 0;

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        final float x = ev.getX();
        final float y = ev.getY();

        final int action = ev.getAction();
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                mDownPosX = x;
                mDownPosY = y;

                break;
            case MotionEvent.ACTION_MOVE:
                final float deltaX = Math.abs(x - mDownPosX);
                final float deltaY = Math.abs(y - mDownPosY);
                // 這裏是夠攔截的判斷依據是左右滑動,讀者可根據自己的邏輯進行是否攔截
                if (deltaX > deltaY) {
                    return false;
                }
        }

        return super.onInterceptTouchEvent(ev);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53

內部解決法

如上面上述,通過requestDisallowInterceptTouchEvent(true)方法來影響父View是否攔截事件,我們通過重寫ViewPager的 dispatchTouchEvent()方法,在左右滑動的時候請求父View ScrollView不要攔截事件,其他的時候由子View 攔截事件

/**
 * @ explain:這個 ViewPager是用來解決ScrollView裏面嵌套ViewPager的 內部解決法的
 * @ author:xujun on 2016/10/25 16:38
 * @ email:[email protected]
 */
public class MyViewPager extends ViewPager {

    private static final String TAG = "xujun";

    int lastX = -1;
    int lastY = -1;

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

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

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        int x = (int) ev.getRawX();
        int y = (int) ev.getRawY();
        int dealtX = 0;
        int dealtY = 0;

        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                dealtX = 0;
                dealtY = 0;
                // 保證子View能夠接收到Action_move事件
                getParent().requestDisallowInterceptTouchEvent(true);
                break;
            case MotionEvent.ACTION_MOVE:
                dealtX += Math.abs(x - lastX);
                dealtY += Math.abs(y - lastY);
                Log.i(TAG, "dealtX:=" + dealtX);
                Log.i(TAG, "dealtY:=" + dealtY);
                // 這裏是夠攔截的判斷依據是左右滑動,讀者可根據自己的邏輯進行是否攔截
                if (dealtX >= dealtY) {
                    getParent().requestDisallowInterceptTouchEvent(true);
                } else {
                    getParent().requestDisallowInterceptTouchEvent(false);
                }
                lastX = x;
                lastY = y;
                break;
            case MotionEvent.ACTION_CANCEL:
                break;
            case MotionEvent.ACTION_UP:
                break;

        }
        return super.dispatchTouchEvent(ev);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58

注意事項(坑)

當我們ScrollView的最上層的Layout裏面多多個孩子的時候,當下面一個孩子是RecyclerView或者ListView的時候,往往會自動滑動到ListView或者RecyclerView 的第一個item,導致進入界面的時候會導致RecyclerView 上面的 View被滑動到界面之外,看不見,這時候的用戶體驗是比較差的

即結構如下面的時候

在Activity中的相關解決方法

於是我查找了相關的資料,在Activity中完美解決,主要要一下兩種方法

第一種方法,重寫Activity的onWindowFocusChanged()方法,在裏面調用mNoHorizontalScrollView.scrollTo(0,0);方法,滑動到頂部,因爲onWindowFocusChanged是在所有View繪製完畢的時候纔會回調的,不熟悉的話建議先回去看一下Activity的生命週期的相關介紹


private void scroll() {
    mNoHorizontalScrollView.scrollTo(0,0);
}

@Override
public void onWindowFocusChanged(boolean hasFocus) {
    super.onWindowFocusChanged(hasFocus);
    if(hasFocus  && first){
        first=false;
        scroll();
    }
}


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

第二種解決方法,調用RecyclerView上面的View的一下方法,讓其獲取焦點

view.setFocusable(true);  
view.setFocusableInTouchMode(true);  
view.requestFocus();
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

這段代碼在初始化的時候就讓該界面的頂部的某一個控件獲得焦點,滾動條自然就顯示到頂部了。

在Fragment中的相關解決方法

同樣是調用第二種方法,調用RecyclerView上面的View的一下方法,讓其獲取焦點

view.setFocusable(true);  
view.setFocusableInTouchMode(true);  
view.requestFocus();
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

這段代碼在初始化的時候就讓該界面的頂部的某一個控件獲得焦點,滾動條自然就顯示到頂部了。但是該方法存在缺點,就是當我們上面的view如果滑動到一半的時候,切換到下一個Fragment,在切換回來的時候,RecyclerView的第一個item會自動滑動到頂部。目前我還沒有找到相對比較好的解決這個問題的方法,大家知道相關解決方法的話也歡迎聯繫我,可以加我 微信或者在留言區評論,謝謝

個人疑點

借鑑於解決Activity的方法,目前我還沒有找到一個方法是在Fragemnt界面完全繪製完畢以後回調的方法,如果大家知道怎樣處理的 話,歡迎大家提出來


ViewPager裏面嵌套ViewPager導致的滑動衝突

內部解決法

從子View ViewPager着手,重寫 子View的 dispatchTouchEvent方法,在子 View需要攔截的時候進行攔截,否則交給父View處理,代碼如下

public class ChildViewPager extends ViewPager {

    private static final String TAG = "xujun";
    public ChildViewPager(Context context) {
        super(context);
    }

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

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        int curPosition;

        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                getParent().requestDisallowInterceptTouchEvent(true);
                break;
            case MotionEvent.ACTION_MOVE:
                curPosition = this.getCurrentItem();
                int count = this.getAdapter().getCount();
                Log.i(TAG, "curPosition:=" +curPosition);
                // 噹噹前頁面在最後一頁和第0頁的時候,由父親攔截觸摸事件
                if (curPosition == count - 1|| curPosition==0) {
                    getParent().requestDisallowInterceptTouchEvent(false);
                } else {//其他情況,由孩子攔截觸摸事件
                    getParent().requestDisallowInterceptTouchEvent(true);
                }

        }
        return super.dispatchTouchEvent(ev);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

外部解決法

這個如果要採用外部解決法來解決的話想,相對很麻煩,我提一下自己的個人思路,我們可以先測量子View在哪個區域,然後我們在根據我們按下的點是否在區域以內,如果是的話,在根據子View時候需要攔截進行處理


討論

對於這種效果,上面是輪播圖的,下面是RecyclerView或者ListView的,一般有一下幾種實現方式 
- 使用我們上述提高的ScrollView裏面嵌套ViewPager和RecyclerView,這種實現方式需要自己解決View滑動事件的衝突,同時還有我在上述提高的在Fragment中存在的問題 
- 使用listView的addHeaderView來實現,或者是通過多種不同的item來實現 
- 使用RecyclerView添加headerView來實現,或者複用多種不同的item來實現。關於RecyclerView如何添加headerView可以參考鴻洋大神的這一篇博客Android 優雅的爲RecyclerView添加HeaderView和FooterView 
- 使用SupportLibrary中的CoordinatorLayout等控件

其佈局文件如下,Activity代碼見項目中的SixActivity

<?xml version="1.0" encoding="utf-8"?>

<android.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/background_light"
    android:fitsSystemWindows="true"
>

    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="300dp"
        android:fitsSystemWindows="true"
        android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"

    >


        <android.support.design.widget.CollapsingToolbarLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_scrollFlags="scroll|snap">


            <android.support.v4.view.ViewPager
                android:id="@+id/viewPager"
                android:layout_width="match_parent"
                android:layout_height="match_parent"

            >

            </android.support.v4.view.ViewPager>

            <TextView
                android:id="@+id/tv_page"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_gravity="bottom"
                android:gravity="right"
                android:text="1/10"
                android:textColor="#000"/>

        </android.support.design.widget.CollapsingToolbarLayout>

    </android.support.design.widget.AppBarLayout>


    <android.support.v7.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

    </android.support.v7.widget.RecyclerView>


</android.support.design.widget.CoordinatorLayout>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60

關於CoordinatorLayout的更多用法,可以參考我的這一篇博客使用CoordinatorLayout打造各種炫酷的效果


總結

  • 當我們滑動方向不同的時候,採用外部解決法和內部解決法,複雜度差不多。
  • 當我們滑動的方向相同的話,建議採用內部解決法來解決,因爲採用外部解決法複雜度比較高。而且有時候我們是採用別人的開源控件,這時候去修改別人的源碼可能會發生一些意想不到的bug。

題外話

  • 在這篇博客的最後提高的實現輪播圖+list列表的幾種實現形式,剛開始是不想寫的,後面因爲ScrollView裏面嵌套ViewPager和RecyclerView在fragment中RecyclerView搶佔焦點,在某些情況下用戶體驗不好,才寫出來的,跟這篇博客要講解的View滑動事件衝突沒有多大關係,只是給讀者提供多種思路而已
  • 至於CoordinatorLayout,是google IO 2015中提出來的,功能很強大,可以說是專門爲了解決嵌套導滑動而產生的,極大地方便了開發者,對於初學者,可以暫時不必掌握它,先把其他的基礎學好就好
  • 同時賣一下廣告,歡迎大家到我的github上面star或者fork,謝謝

參考文章:圖解 Android 事件分發機制

文章首發地址CSDN:http://blog.csdn.net/gdutxiaoxu/article/details/52939127

源碼下載地址:https://github.com/gdutxiaoxu/TouchDemo.git

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章