1. 程式人生 > >Android 巢狀佈局簡析

Android 巢狀佈局簡析

前言

巢狀佈局是事件分發的衍生內容,理解了巢狀佈局的實現方式,Md的很多效果都是毛毛雨了

這裡寫圖片描述

Uml 時序圖形

這裡寫圖片描述

 巢狀佈局實現方式,以及 5.0 中與巢狀佈局有關聯的元件

提起巢狀佈局,MD中的 CoordinatorLayout,SwipeRefreshLayout,RecyclerView 當然還有V4包下的NestedScrollView,都有直接關係,因為他們是 NestedScrollingParent(NP) 或者 NestedScrollingChild(NC) 直接或者間接子類。

那麼如何才能進行巢狀佈局自定義?
通過成為NP,NC子類進行巢狀佈局開發妥妥的。
下面是NP,NC 具體方法,呼叫時序檢視blog頭部的時序圖結合原始碼進行理解
NestedScrollingChildHelper // Child輔助類
NestedScrollingParentHelper // Parent輔助類

核心類 NestedScrollingParent

package android.support.v4.view;

import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;

public interface NestedScrollingParent {

    public boolean onStartNestedScroll(View child, View target, int
nestedScrollAxes); public void onNestedScrollAccepted(View child, View target, int nestedScrollAxes); public void onStopNestedScroll(View target); public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed); public void
onNestedPreScroll(View target, int dx, int dy, int[] consumed); public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed); public boolean onNestedPreFling(View target, float velocityX, float velocityY); public int getNestedScrollAxes(); }

核心類 NestedScrollingChild

package android.support.v4.view;

import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewParent;


public interface NestedScrollingChild {

    public void setNestedScrollingEnabled(boolean enabled);


    public boolean isNestedScrollingEnabled();


    public boolean startNestedScroll(int axes);


    public void stopNestedScroll();


    public boolean hasNestedScrollingParent();


    public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed,
            int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow);


    public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow);


    public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed);


    public boolean dispatchNestedPreFling(float velocityX, float velocityY);
}

簡單一下回調方法的介含義,以及需要在那裡處理事件:

onStartNestedScroll該方法,一定要按照自己的需求返回true,該方法決定了當前控制元件是否能接收到其內部View(非並非是直接子View)滑動時的引數;假設你只涉及到縱向滑動,這裡可以根據nestedScrollAxes這個引數,進行縱向判斷。

onNestedPreScroll該方法的會傳入內部View移動的dx,dy,如果你需要消耗一定的dx,dy,就通過最後一個引數consumed進行指定,例如我要消耗一半的dy,就可以寫consumed[1]=dy/2

onNestedFling你可以捕獲對內部View的fling事件,如果return true則表示攔截掉內部View的事件。

主要關注的就是這三個方法~

這裡內部View表示不一定非要是直接子View,只要是內部View即可。

下面看一下我們具體的實現:

public class StickyNavLayout extends LinearLayout implements NestedScrollingParent
{
    @Override
    public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes)
    {
        return (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0;
    }
    @Override
    public void onNestedPreScroll(View target, int dx, int dy, int[] consumed)
    {
        boolean hiddenTop = dy > 0 && getScrollY() < mTopViewHeight;
        boolean showTop = dy < 0 && getScrollY() > 0 && !ViewCompat.canScrollVertically(target, -1);

        if (hiddenTop || showTop)
        {
            scrollBy(0, dy);
            consumed[1] = dy;
        }
    }
    @Override
    public boolean onNestedPreFling(View target, float velocityX, float velocityY)
    {
        if (getScrollY() >= mTopViewHeight) return false;
        fling((int) velocityY);
        return true;
    }
}

onStartNestedScroll中,我們判斷了如果是縱向返回true,這個一般是需要內部的View去傳入的,你要是不確定,或者擔心內部View編寫的不規範,你可以直接return true;

onNestedPreScroll中,我們判斷,如果是上滑且頂部控制元件未完全隱藏,則消耗掉dy,即consumed[1]=dy;如果是下滑且內部View已經無法繼續下拉,則消耗掉dy,即consumed[1]=dy,消耗掉的意思,就是自己去執行scrollBy,實際上就是我們的StickNavLayout滑動。

此外,這裡還處理了fling,通過onNestedPreFling方法,這個可以根據自己需求定了,當頂部控制元件顯示時,fling可以讓頂部控制元件隱藏或者顯示。

Eg:當然有人會說用手勢 + 事件分發攔截同樣可以實現這個效果,的確可以實現,只是效果會打折扣

事件分發是這樣的:子View首先得到事件處理權,處理過程中,父View可以對其攔截,但是攔截了以後就無法再還給子View(本次手勢內)。
NestedScrolling機制是這樣的:內部View在滾動的時候,首先將dx,dy交給NestedScrollingParent,NestedScrollingParent可對其進行部分消耗,剩餘的部分還給內部View。

這裡針對實現了NetstedScrollingParent 的類繼續log輸出,直觀顯示滾動事件呼叫鏈。

不進行滑動

vlayout E/StickyNavLayout: onStartNestedScroll
05-22 14 E/StickyNavLayout: onNestedScrollAccepted
05-22 14 E/StickyNavLayout: onStopNestedScroll

進行滑動

05-22 14:46:11.655 27721-27721/: onStartNestedScroll
05-22 14:46:11.655 27721-27721/: onNestedScrollAccepted
05-22 14:46:11.875 27721-27721/: onNestedPreScroll
05-22 14:46:11.895 27721-27721/: onNestedPreScroll
05-22 14:46:11.915 27721-27721/: onNestedPreScroll
05-22 14:46:11.925 27721-27721/: onNestedPreScroll
05-22 14:46:11.945 27721-27721/: onNestedPreScroll
05-22 14:46:11.965 27721-27721/: onNestedPreScroll
05-22 14:46:11.975 27721-27721/: onNestedPreScroll
05-22 14:46:11.995 27721-27721/: onNestedPreScroll
05-22 14:46:12.015 27721-27721/: onNestedPreScroll
05-22 14:46:12.025 27721-27721/: onNestedPreScroll
05-22 14:46:12.045 27721-27721/: onStopNestedScroll

快速滑動

05-22 14:48:18.245 27721-27721/: onStartNestedScroll
05-22 14:48:18.245 27721-27721/: onNestedScrollAccepted
05-22 14:48:18.315 27721-27721/: onNestedPreScroll
05-22 14:48:18.315 27721-27721/: onNestedScroll

05-22 14:48:18.315 27721-27721/: onNestedPreFling
05-22 14:48:18.315 27721-27721/: onNestedFling
05-22 14:48:18.315 27721-27721/: onStopNestedScroll

拋,快速 滑動觸發Fling

05-22 14:49:51.875 27721-27721/: onNestedPreFling
05-22 14:49:51.875 27721-27721/: onNestedFling

1.巢狀佈局模式。依附於onTouchEvent() 進行處理。在down,move,up 中進行回撥。Helper 類中實現

  1. 固定大小的 int [2],集合。 作為形參,進行傳遞,在函式中可以通過形參進行賦值

3.scroller 和 後面 api 19 新新增對於over 邊界處理的 overscroller ,兩者80% api重複,後者多了幾個處理Over bound的 api

總結:

巢狀佈局滑動和事件分發之間的區別,進行自定義擴充套件巢狀佈局需要實現那些方法,實現那幾個類。巢狀佈局的延伸相容性等有個大體的瞭解。細節部分還請自行體驗