CoordinatorLayout + AppBarLayout + CollapsingToolbarLayout

本文介绍了CoordinatorLayout、AppBarLayout和CollapsingToolbarLayout在Android中的使用,详细讲解了它们如何协同工作以实现折叠和收缩视图的效果。CoordinatorLayout作为一个强大的顶级布局,用于协调子布局的交互。AppBarLayout是垂直LinearLayout,支持Material Design的滚动手势,而CollapsingToolbarLayout则提供了折叠效果,允许子View在滚动时有不同的表现。文章还详细阐述了各种属性和模式,如layout_behavior、scrollFlags、collapseMode等,以及它们如何影响布局的行为。

CoordinatorLayout

CoordinatorLayout相当于一个功能更加强大的FrameLayout,主要有两个用途:

  1. 作为顶层布局;
  2. 作为协调与子布局之间交互的容器。

通过为子布局指定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会覆盖CoordinatorLayoutonMeasureChild行为。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进行交互,必须是实现了NestedScrollingChild2NestedScrollingChild的View,且必须调用dispatchNestedScroll,否则无法触发behavior的行为。例如滚动视图为ScrollViewListView,由于没有实现相应接口,所以也无法触发behavior,改为相应的NestedScrollViewRecyclerView则没问题。

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:contentScrimsetContentScrim
    设置CollapsingToolbarLayout滚动打到某个阈值时显示的背景。

  • app:statusBarScrimsetStatusBarScrim
    设置CollapsingToolbarLayout滚动打到某个阈值时Status bar显示的背景。

  • collapseMode
    CollapsingToolbarLayout的子View可以有以下两种折叠模式可以选择,可通过app:layout_collapseModesetCollapseMode进行设置:

    parallax模式COLLAPSE_MODE_PARALLAX
    滚动时,设置该模式的子View会跟着Scroll View一起滚动,通常和视差因子app:layout_collapseParallaxMultipliersetParallaxMultiplier配合使用,视差因子范围为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:collapsedTitleGravitysetCollapsedTitleGravity
    设置折叠时title文本的位置。

  • app:collapsedTitleTextAppearancesetCollapsedTitleTextAppearance
    设置折叠时title文本的样式。

  • app:expandedTitleTextAppearance等expanded属性为设置展开时title文本的样式。

  • app:scrimAnimationDurationsetScrimAnimationDuration
    设置折叠时所显示的背景(scrims)时长。

  • app:scrimVisibleHeightTriggersetScrimVisibleHeightTrigger
    设置特定高度用于控制折叠背景的可见性,当CollapsingToolbarLayout的可见高度小于该值时,折叠背景(scrims)可见,否则不可见。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值