官網(wǎng)介紹:
CoordinatorLayout 有兩個(gè)主要用途:
1.作為頂級(jí)應(yīng)用程序的裝飾或布局
2.作為與一個(gè)或多個(gè)子視圖進(jìn)行特定交互的容器
首先看構(gòu)造方法,之后會(huì)解析xml中的屬性。
<declare-styleable name="CoordinatorLayout">
<attr format="reference" name="keylines"/>
<attr format="reference" name="statusBarBackground"/>
</declare-styleable>
CoordinatorLayoute 分析
CoordinatorLayoute它自己實(shí)現(xiàn)了NestedScrollingParent,NestedScrollingParent則是一個(gè)接口:
// 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 本身不可以滑動(dòng),那這些Scroll方法自然不是自己調(diào)用的,
是由內(nèi)部的類(lèi)調(diào)用的。這里要NestedScrollingChild要出場(chǎng)了。
// 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滑動(dòng)相關(guān)都有一個(gè)dispatch前綴,這也驗(yàn)證我們之前的說(shuō)法,NestedScrollingParent相關(guān)的方法是由NestedScrollingChild調(diào)用的。就像“兒子”做了什么事情,都要像“父親”匯報(bào)一樣?!案赣H”知道了以后再根據(jù)知道的內(nèi)容做自己的事情 。
NestedScrollingChild,NestedScrollingParent,聽(tīng)名字就知道貌似是一種“父子”關(guān)系。前面說(shuō)到CoordinatorLayout 不可以滑動(dòng),那NestedScrollingChild的接口必然是由實(shí)現(xiàn)了NestedScrollingChild的可以滾動(dòng)的類(lèi)去調(diào)用的。那么目前兼容包有

這三個(gè)類(lèi)實(shí)現(xiàn)了,我們就挑一個(gè)NestedScrollView 分析一下吧:
public class NestedScrollView extends FrameLayout implements NestedScrollingParent,
NestedScrollingChild, ScrollingView
可以看到,它既實(shí)現(xiàn)了NestedScrollingChild,也實(shí)現(xiàn)了NestedScrollingParent,我們暫只關(guān)心它實(shí)現(xiàn)了NestedScrollingChild就可以了。果然,我們?cè)?br>
NestedScrollView 的onInterceptTouchEvent
和onTouchEvent 的MotionEvent.ACTION_DOWN中調(diào)用了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中調(diào)用了了dispatchNestedScroll()方法。繼而調(diào)用了
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);
}
// 選一個(gè) 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;
}
最終調(diào)用了循環(huán)調(diào)用了子Behaviour中的StartNestedScroll。
小結(jié):
1.當(dāng)CoordinatorLayout作為一個(gè)父容器,包含了實(shí)現(xiàn)了NestedScrollingChild的可以滾動(dòng)的RecyclerView /NestedScrollView /SwipeRefreshLayout的時(shí)候,它的滑動(dòng)事件可以回調(diào)到CoordinatorLayout類(lèi)中,在CoordinatorLayout中,它又可以分發(fā)事件到各個(gè)子View所添加的Behaviour中。在這一過(guò)程中,CoordinatorLayout相當(dāng)一個(gè)溝通的橋梁。