官網介紹:
CoordinatorLayout 有兩個主要用途:
1.作為頂級應用程序的裝飾或布局
2.作為與一個或多個子視圖進行特定交互的容器
首先看構造方法,之后會解析xml中的屬性。
<declare-styleable name="CoordinatorLayout">
<attr format="reference" name="keylines"/>
<attr format="reference" name="statusBarBackground"/>
</declare-styleable>
CoordinatorLayoute 分析
CoordinatorLayoute它自己實現了NestedScrollingParent,NestedScrollingParent則是一個接口:
// 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();
由于CoordinatorLayoute 本身不可以滑動,那這些Scroll方法自然不是自己調用的,
是由內部的類調用的。這里要NestedScrollingChild要出場了。
// 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);//注意,NestedScrollingChild滑動相關都有一個dispatch前綴,這也驗證我們之前的說法,NestedScrollingParent相關的方法是由NestedScrollingChild調用的。就像“兒子”做了什么事情,都要像“父親”匯報一樣。“父親”知道了以后再根據知道的內容做自己的事情 。
NestedScrollingChild,NestedScrollingParent,聽名字就知道貌似是一種“父子”關系。前面說到CoordinatorLayout 不可以滑動,那NestedScrollingChild的接口必然是由實現了NestedScrollingChild的可以滾動的類去調用的。那么目前兼容包有
這三個類實現了,我們就挑一個NestedScrollView 分析一下吧:
public class NestedScrollView extends FrameLayout implements NestedScrollingParent,
NestedScrollingChild, ScrollingView
可以看到,它既實現了NestedScrollingChild,也實現了NestedScrollingParent,我們暫只關心它實現了NestedScrollingChild就可以了。果然,我們在
NestedScrollView 的onInterceptTouchEvent
和onTouchEvent 的MotionEvent.ACTION_DOWN中調用了startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL);
public boolean startNestedScroll(int axes) {
if (hasNestedScrollingParent()) {
// Already in progress
return true;
}
if (isNestedScrollingEnabled()) {
ViewParent p = mView.getParent();// mView即NestedScrollView ,那它的Parent自然為CoordinatorLayoute 了
View child = mView;
while (p != null) {
if (ViewParentCompat.onStartNestedScroll(p, child, mView, axes)) {
mNestedScrollingParent = p;
ViewParentCompat.onNestedScrollAccepted(p, child, mView, axes);
return true;
}
if (p instanceof View) {
child = (View) p;
}
p = p.getParent();
}
}
return false;
}
在onTouchEvent的MotionEvent.ACTION_MOVE中調用了了dispatchNestedScroll()方法。繼而調用了
public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed,
int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow) {
if (isNestedScrollingEnabled() && mNestedScrollingParent != null) {
if (dxConsumed != 0 || dyConsumed != 0 || dxUnconsumed != 0 || dyUnconsumed != 0) {
int startX = 0;
int startY = 0;
if (offsetInWindow != null) {
mView.getLocationInWindow(offsetInWindow);
startX = offsetInWindow[0];
startY = offsetInWindow[1];
}
ViewParentCompat.onNestedScroll(mNestedScrollingParent, mView, dxConsumed,
dyConsumed, dxUnconsumed, dyUnconsumed);
if (offsetInWindow != null) {
mView.getLocationInWindow(offsetInWindow);
offsetInWindow[0] -= startX;
offsetInWindow[1] -= startY;
}
return true;
} else if (offsetInWindow != null) {
// No motion, no dispatch. Keep offsetInWindow up to date.
offsetInWindow[0] = 0;
offsetInWindow[1] = 0;
}
}
return false;
}
之后
ViewParentCompat.onNestedScroll(mNestedScrollingParent, mView, dxConsumed,
dyConsumed, dxUnconsumed, dyUnconsumed);
// ViewParentCompat.onNestedScroll()
public static void onNestedScroll(ViewParent parent, View target, int dxConsumed,
int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
IMPL.onNestedScroll(parent, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed);
}
// 選一個 IMPL = new ViewParentCompatStubImpl();
@Override
public boolean onStartNestedScroll(ViewParent parent, View child, View target,
int nestedScrollAxes) {
if (parent instanceof NestedScrollingParent) { //指CoordinatorLayoute
return ((NestedScrollingParent) parent).onStartNestedScroll(child, target,
nestedScrollAxes);
}
return false;
}
回到CoordinatorLayoute 中的
@Override
public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
boolean handled = false;
final int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
final View view = getChildAt(i);
if (view.getVisibility() == View.GONE) {
// If it's GONE, don't dispatch
continue;
}
final LayoutParams lp = (LayoutParams) view.getLayoutParams();
final Behavior viewBehavior = lp.getBehavior();
if (viewBehavior != null) {
final boolean accepted = viewBehavior.onStartNestedScroll(this, view, child, target,
nestedScrollAxes);
handled |= accepted;
lp.acceptNestedScroll(accepted);
} else {
lp.acceptNestedScroll(false);
}
}
return handled;
}
最終調用了循環調用了子Behaviour中的StartNestedScroll。
小結:
1.當CoordinatorLayout作為一個父容器,包含了實現了NestedScrollingChild的可以滾動的RecyclerView /NestedScrollView /SwipeRefreshLayout的時候,它的滑動事件可以回調到CoordinatorLayout類中,在CoordinatorLayout中,它又可以分發事件到各個子View所添加的Behaviour中。在這一過程中,CoordinatorLayout相當一個溝通的橋梁。