天天看點

Android 嵌套布局簡析

前言

嵌套布局是事件分發的衍生内容,了解了嵌套布局的實作方式,Md的很多效果都是毛毛雨了

​​原文位址進入​​

Android 嵌套布局簡析

Uml 時序圖形

Android 嵌套布局簡析

 嵌套布局實作方式,以及 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

總結:

繼續閱讀