CoordinatorLayout
CoordinatorLayout相当于一个功能更加强大的FrameLayout,主要有两个用途:
- 作为顶层布局;
- 作为协调与子布局之间交互的容器。
通过为子布局指定Behaviors,可以为子布局之间提供不同的交互。CoordinatorLayout的子布局可以通过实现CoordinatorLayout.AttachedBehavior接口为自身添加默认行为(behavior)。简单来说,CoordinatorLayout相当于一个第三方,通过Behaviors属性对子布局之间进行行为交互控制。具体如何控制如下:
##CoordinatorLayout
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
...
for (int i = 0; i < childCount; i++) {
final View child = mDependencySortedChildren.get(i);
if (child.getVisibility() == GONE) {
// If the child is GONE, skip...
continue;
}
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
...
final Behavior b = lp.getBehavior();
if (b == null || !b.onMeasureChild(this, child, childWidthMeasureSpec, keylineWidthUsed,
childHeightMeasureSpec, 0)) {
onMeasureChild(child, childWidthMeasureSpec, keylineWidthUsed,childHeightMeasureSpec, 0);
}
...
}
...
}
##CoordinatorLayout.Behavior<V extends android.view.View>
/**
* @return true if the Behavior measured the child view, false if the CoordinatorLayout should perform its default measurement
*/
public boolean onMeasureChild(CoordinatorLayout parent, V child, int parentWidthMeasureSpec, int widthUsed,
int parentHeightMeasureSpec, int heightUsed) {
return false;
}
可见CoordinatorLayout是通过判断子布局是否存在Behavior或存在的Behavior是否measure了子布局来判读CoordinatorLayout自身是否需要执行自己的measure,即Behavior会覆盖CoordinatorLayout的onMeasureChild行为。layout时同理:
##CoordinatorLayout
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
final int layoutDirection = ViewCompat.getLayoutDirection(this);
final int childCount = mDependencySortedChildren.size();
for (int i = 0; i < childCount; i++) {
final View child = mDependencySortedChildren.get(i);
if (child.getVisibility() == GONE) {
// If the child is GONE, skip...
continue;
}
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
final Behavior behavior = lp.getBehavior();
if (behavior == null || !behavior.onLayoutChild(this, child, layoutDirection)) {
onLayoutChild(child, layoutDirection);
}
}
}
##CoordinatorLayout.Behavior<V extends android.view.View>
/*
* @return true if the Behavior performed layout of the child view, false to request default layout behavior
* /
public boolean onLayoutChild(CoordinatorLayout parent, V child, int layoutDirection) {
return false;
}
当然上面只是通过Behavior去控制子布局的行为,但是在执行重绘之前,CoordinatorLayout和子布局又是如何协调滚动或滑动等效果的呢?
##CoordinatorLayout.Behavior<V extends android.view.View>
/*
* @return true if this Behavior would like to intercept and take over the event stream.
* The default always returns false.
*/
public boolean onInterceptTouchEvent(CoordinatorLayout parent, V child, MotionEvent ev) {
return false;
}
/*
* @return true if this Behavior handled this touch event and would like to continue
* receiving events in this stream. The default always returns false.
*/
public boolean onTouchEvent(CoordinatorLayout parent, V child, MotionEvent ev) {
return false;
}
首先是Behavior可以选择是否拦截消费触摸事件。
public class CoordinatorLayout extends ViewGroup implements NestedScrollingParent2 {
...
}
首先是CoordinatorLayout实现了NestedScrollingParent2接口,通过该接口获取子View滚动的相关参数,再通过behavior传到对各个子view中,那些包含了behavior的子View便会消费掉相关参数,处理滚动事件,达到交互目的。所以这就要求CoordinatorLayout的子布局要想实现通过behavior进行交互,必须是实现了NestedScrollingChild2或NestedScrollingChild的View,且必须调用dispatchNestedScroll,否则无法触发behavior的行为。例如滚动视图为ScrollView或ListView,由于没有实现相应接口,所以也无法触发behavior,改为相应的NestedScrollView或RecyclerView则没问题。
AppBarLayout
AppBarLayout是一个实现了很多Material Design风格的app bar设计概论(即滚动手势)的vertical LinearLayout,其子布局应该通过setScrollFlags(int) 或app:layout_scrollFlags设置其behavior,包括以下五种flag:
-
SCROLL_FLAG_SCROLL:该View随着滚动事件滚动。以下其他属性若要生效必须得先设置该属性,即该属性必须优先设置。 -
SCROLL_FLAG_EXIT_UNTIL_COLLAPSED:向上滚动时,该View先滚动退出到其最小高度值(setMinimumHeight),然后Scroll View开始滚动,即该View优先滚动但不会完全退出屏幕。 -
SCROLL_FLAG_ENTER_ALWAYS: 向下滚动时,优先滚动该View,再滚动Scroll View。 通常也称这种模式为’quick return’。 -
SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED:SCROLL_FLAG_ENTER_ALWAYS的扩展属性,向下滚动时,该View先滚动显示到其最小高度(setMinimumHeight),然后Scroll View开始滚动,直到Scroll View滚到边界时,该View再继续滚动,直到完全显示。 -
SCROLL_FLAG_SNAP:该View的一个吸附效果,即当滚动时,该View会跟着滚动,然后松开手指时,若该View显示区域大于隐藏区域,则整个View完全显示,反之则完全退出屏幕。也就是说,设置了该属性的View,要么完全显示,要么完全滚出屏幕。
AppBarLayout一般用于作为CoordinatorLayout的直接子View,当作为其他ViewGroup的子View时,会导致其大部分功能无法使用。同时,AppBarLayout还需要一个可滚动的兄弟View,即跟其处于同一层并列的CoordinatorLayout直接子View。同时,还需要为该滚动视图的behavior设置AppBarLayout.ScrollingViewBehavior的实例。如:app:layout_behavior="@string/appbar_scrolling_view_behavior"
CollapsingToolbarLayout
CollapsingToolbarLayout是一个实现了折叠效果的FrameLayout,主要作为AppBarLayout的直接子View,用于控制自身子View的折叠。包含以下属性:
-
app:contentScrim或setContentScrim
设置CollapsingToolbarLayout滚动打到某个阈值时显示的背景。 -
app:statusBarScrim或setStatusBarScrim
设置CollapsingToolbarLayout滚动打到某个阈值时Status bar显示的背景。 -
collapseMode
CollapsingToolbarLayout的子View可以有以下两种折叠模式可以选择,可通过app:layout_collapseMode或setCollapseMode进行设置:parallax模式:
COLLAPSE_MODE_PARALLAX
滚动时,设置该模式的子View会跟着Scroll View一起滚动,通常和视差因子app:layout_collapseParallaxMultiplier或setParallaxMultiplier配合使用,视差因子范围为0 ~ 1。Pinned模式:
COLLAPSE_MODE_PIN
一般设置给Toolbar,如果是Toolbar,则无论滚动时还是滚动结束,整个Toolbar都会完整地留在屏幕上,但若不是Toolbar,则滚动到该子View的高度之前,该View不会有变化,到达该View后,该View便会逐渐缩小至CollapsingToolbarLayout的最小高度(若最小高度小于该View的高度),同时,若是设置了收缩的背景(scrims),则该背景还会覆盖该View。所以最好还是将需要设置该属性的放到Toolbar里,然后将该属性设置给Toolbar。普通模式:
COLLAPSE_MODE_OFF
若是两种模式都不想用,可以不设置collapseMode或者将其设置为COLLAPSE_MODE_OFF,该模式的子View将表现为没有设置折叠行为的View。 -
app:collapsedTitleGravity或setCollapsedTitleGravity
设置折叠时title文本的位置。 -
app:collapsedTitleTextAppearance或setCollapsedTitleTextAppearance
设置折叠时title文本的样式。 -
app:expandedTitleTextAppearance等expanded属性为设置展开时title文本的样式。 -
app:scrimAnimationDuration或setScrimAnimationDuration
设置折叠时所显示的背景(scrims)时长。 -
app:scrimVisibleHeightTrigger或setScrimVisibleHeightTrigger
设置特定高度用于控制折叠背景的可见性,当CollapsingToolbarLayout的可见高度小于该值时,折叠背景(scrims)可见,否则不可见。
本文介绍了CoordinatorLayout、AppBarLayout和CollapsingToolbarLayout在Android中的使用,详细讲解了它们如何协同工作以实现折叠和收缩视图的效果。CoordinatorLayout作为一个强大的顶级布局,用于协调子布局的交互。AppBarLayout是垂直LinearLayout,支持Material Design的滚动手势,而CollapsingToolbarLayout则提供了折叠效果,允许子View在滚动时有不同的表现。文章还详细阐述了各种属性和模式,如layout_behavior、scrollFlags、collapseMode等,以及它们如何影响布局的行为。
6994

被折叠的 条评论
为什么被折叠?



