最近项目有一个需求,做出类似闲鱼鱼塘界面的效果。如下图:
所以想到用CoordinatorLayout+AppBarLayout+CollapsingToolbarLayout去搭建此界面。
CoordinatorLayout
实际上是一个更强大的FrameLayout, 可以通过Behavior 来控制其中各个child view的交互行为. 也可以指定anchor来指定floating view相对于其他某个View的位置. 比如Floating Action Button在显示Snackbar的时候自动向上移动.
为了使Toolbar响应滚动事件, 需要给它外边包一个AppBarLayout.
它是一个纵向的LinearLayout, 必须要作为CoordinateLayout的直接child使用.
然后, 我们需要定义AppBarLayout和我们scroll的内容View的关系.
这里可以是一个RecyclerView, 或者其他支持嵌套scrolling的view, 比如NestedScrollView
(实际上View类就有方法setNestedScrollingEnabled(), 但是还是需要View自己实现nested scrolling的功能, 否则这个开关也没有效果.)
support library提供了@string/appbar_scrolling_view_behavior, 它映射到AppBarLayout.ScrollingViewBehavior.
它是用来告诉AppBarLayout下面那个scroll view上的scroll事件什么时候发生.
所以这个属性必须在触发事件的view上指定, 比如:
<android.support.v4.widget.NestedScrollView android:id="@+id/nsv" android:layout_width="match_parent" android:layout_height="match_parent" android:fillViewport="true" android:scrollbars="none" app:layout_behavior="@string/appbar_scrolling_view_behavior"> <android.support.v4.view.ViewPager android:id="@+id/vp_list" android:layout_width="match_parent" android:layout_height="match_parent" app:layout_behavior="@string/appbar_scrolling_view_behavior" /></android.support.v4.widget.NestedScrollView>
当CoordinatorLayout看到自己的child(比如NestedScrollView)声明了这个属性, 就会在自己的其他child中寻找相关的view(AppBarLayout).
这样, 当NestedScrollView发生scroll事件的时候, AppBarLayout和其中的views都会被通知到.
滚动事件怎么通知到AppBarLayout的呢? 还需要一个属性: app:layout_scrollFlags
scroll:该view显示时,只有在滚动视图顶部向上滚动时,该view才会慢慢消失。 该view消失后,只有在滚动视图顶部向下滚动时,该view才会慢慢出现。
enterAlways
:表示只要列表上方内容滚动出现, View就应该出现. 适用的情形: 当把列表滚到底部时, Toolbar被隐藏了, 一旦回滚一点儿, Toolbar就应该立即出现. 如果不设置这个flag, 默认的行为是一直要把列表滚到顶部, Toolbar才会出现.enterAlwaysCollapsed
:正常情况下, 如果只有enterAlways
被指定, 在列表向下滚动的过程中Toolbar将会一直展开.如果同时指定了enterAlwaysCollapsed
和minHeight
, 那么开始滚动以后, 只滚动到minHeight为止, 直到滚动到达列表顶部的时候, view才会展开到全部高度.exitUntilCollapsed
: 正常只指定scroll的情况下, 向下滑动将会使得整个Toolbar移动到不见.如果同时指定了exitUntilCollapsed
和minHeight
, 那么将会收缩到minHeight为止, Toolbar不会一直滚动和退出屏幕.snap
:使用了这个属性, 等scroll事件结束的时候, View可见的尺寸小于它的50%, 则它会直接消失, 如果大于50%, 则它会完整地出现.
若要实现Toolbar折叠的效果,需在Toolbar外面包一层CollapsingToolbarLayout,这个类必须作为AppBarLayout的直接child使用。
<android.support.design.widget.CollapsingToolbarLayout android:id="@+id/collapsing_toolbar_layout" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@color/white" app:collapsedTitleGravity="center" app:collapsedTitleTextAppearance="@style/yuquan_collapsed_text" app:contentScrim="@color/white" app:expandedTitleGravity="top" app:expandedTitleMarginStart="60dp" app:expandedTitleMarginTop="35dp" app:expandedTitleTextAppearance="@style/yuquan_expanded_text" app:layout_scrollFlags="scroll|exitUntilCollapsed">
<ImageView android:id="@+id/logo" android:layout_width="match_parent" android:layout_height="match_parent" android:contentDescription="@null" android:fitsSystemWindows="true" android:scaleType="fitCenter" android:src="@drawable/android_logo" app:layout_collapseMode="parallax" app:layout_collapseParallaxMultiplier="0.1" />
<android.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="@dimen/toolbar_size" app:contentInsetEnd="0dp" app:contentInsetStart="0dp" app:layout_collapseMode="pin"> <ImageView android:id="@+id/iv_yuquan_back" android:layout_width="@dimen/toolbar_size" android:layout_height="@dimen/toolbar_size" android:layout_gravity="left" android:clickable="true" android:padding="12dp" android:src="@drawable/icon_yuquan_back_gray" /> <ImageView android:id="@+id/iv_yuquan_more" android:layout_width="@dimen/toolbar_size" android:layout_height="@dimen/toolbar_size" android:layout_gravity="right" android:clickable="true" android:padding="12dp" android:visibility="gone" android:src="@drawable/icon_yuquan_more_gray" /> </android.support.v7.widget.Toolbar></android.support.design.widget.CollapsingToolbarLayout>
包了这个类之后, setTitle要调用这个类的方法:
CollapsingToolbarLayout collapsingToolbar = (CollapsingToolbarLayout) findViewById(R.id.collapsing_toolbar); collapsingToolbar.setTitle("Title");
这里可以设置ImageView折叠展开的效果:
layout_collapseMode属性设置为parallax,在内容滚动时,CollapsingToolbarLayout中的View(比如ImageView)也可以同时滚动,实现视差滚动效果,通常和app:layout_collapseParallaxMultiplier属性(设置视差因子)搭配使用,其值为0~1;设置上为pin,则不会有滚动效果。
说一说遇到的坑吧:
因为项目框架搭建的列表全部使用ListView,所以最初viewpager里面放的也是ListView。因为Listview在Coordinatorlayout中无法响应滚动事件,所以自己实现NestedScrollingChild的功能,待全部写完,测试时发现在5.0以下系统竟然只能滑动一屏!
Google一圈才发现,这只对5.0+系统有效,最好的解决办法是用RecyclerView,因为内部实现了NestedScrollingChild,处理了“与父view交互”的嵌套滑动。
所以在使用Coordinatorlayout时,最好最好最好配合RecyclerView使用。