joy keeps flowin'

material风格吸顶效果

xx
目次

前言 #

Material Design是Google在2014年的I/O大会上发布的一种设计规范,包括布局、颜色、形状、声音、动效等。面向的是Android、iOS、Web和Flutter。对自己的Android,Google还推出了一系列组件、主题帮助开发者快速、方便的实现Material Design的效果。下面是几个能用在大部分实际项目中的控件:

AppBarLayout #

最近我们的项目首页UI改版,目前的布局大致如下图。打开首页时是显示tab的,向上滑动RecyclerView时超过了一定距离(比如TabLayout高度的50%)就隐藏TabLayout,下滑时超过了这距离时再显示TabLayout

简图

搜索时,发现有人用CoordinatorLayout+AppBarLayout实现的把ToolBar隐藏的效果。于是把我们项目中的TabLayout用AppBarLayout给包起来。达到了与期望的效果差不多的。

下面是简化了的布局。为了写起来方便用了RecyclerView,实际项目中用的则是Viewpager,除了页面左右滑动外其余都是相同的的。

布局代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".appbar.activity.OtherActivity">

    <com.google.android.material.appbar.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <com.google.android.material.tabs.TabLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="@color/teal_200"
            app:layout_scrollFlags="scroll|enterAlways|snap|snapMargins">

            <com.google.android.material.tabs.TabItem
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Monday"/>

            <com.google.android.material.tabs.TabItem
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Tuesday"/>

            <com.google.android.material.tabs.TabItem
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Wednesday"/>
        </com.google.android.material.tabs.TabLayout>
    </com.google.android.material.appbar.AppBarLayout>

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/content_rv"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"/>

</androidx.coordinatorlayout.widget.CoordinatorLayout>

布局文件中最外层需要使用CoordinatorLayout,其他的就是TabLayout和一个RecyclerView。其中重要的是layout_scrollFlagslayout_behavior

layout_behavior的作用是在有layout_behaviorCoordinatorLayout子View(NestedScrollViewRecyclerView)滚动时可以根据layout_scrollFlags改变AppBarLayout的行为。

layout_behavior是官方定义的,用appbar_scrolling_view_behaviorlayout_scrollFlags可以根据我们需要的效果选择。

layout_scrollFlags #

在官方文档中,layout_scrollFlags的取值可以有以下七种:

noScroll #

不受RecyclerView或*NestedScrollView的滑动影响。*

scroll #

AppBarLayout在RecyclerView的第一个item或*NestedScrollView上方随着其滑动。类似插入到*RecyclerView第0个位置上。

exitUntilCollapsed #

需要与scroll一起使用,且可以设置minHeight。当向上滑动时顶部会有minHeight高度的TabLayout一直在顶部;向下滑动不会有变化,直到第0个位置的item完全显示。这时再向下滑动会逐渐恢复TabLayout原来的高度。

minHeight可以设置0dp,这时与只设置scroll无异。

enterAlways #

需要与scroll一起使用。在任何位置,向下滑动会隐藏TabLayout,向上滑动会显示TabLayout。且可以停留在TabLayout任意高度。

enterAlwaysCollapsed #

需要与scrollenterAlways一起使用,且可以设置minHeight。向下滑动时与enterAlways相同。当向下滑动时会有minHeight高度的TabLayout一直在顶部,直到第0个位置的item完全显示。这时再向下滑动会逐渐恢复TabLayout原来的高度。

snap #

需要与scroll一起使用。与只是用scroll不同的只在TabLayout的显示和隐藏上,无论显示还隐藏只有TabLayout高度的一半时,才会变化,否则会回到当前的状态。

snapMargins #

需要与scrollsnap一起使用。当使用snap时,在TabLayout中设置marginBottom,设置的marginBottom不会随着TabLayout一起,会有marginBottom高度的一块留在顶部,需要再次滑动才能收起来。这就是snapMargins的使用场景,设置了marginBottom会随着TabLayout一起收起。

上面几种的效果其实都不是我们想要的。enterAlways没有吸顶的效果,意味着TabLayout可以显示一部分,这时用户点击Tab到其他内容页时,TabLayout的显示就会异常。而使用snap时,当TabLayout已经隐藏了,如果用户滑动到一半了就需要先回到第0个item的位置,再滑动才能显示TabLayout,体验很差。

既然有snap有吸顶效果,enterAlways又可以在任何位置位置滑出/隐藏TabLayout。有没有可能实现结合的效果呢?把两个flag都写在TabLayout上,确实实现了。但是没有文档说可以这么用,很迷惑。去看了下AppBarLayout的源码,CoordinatorLayoutAppBarLayout联动原理不再赘述。

snap的效果与enterAlways实际上是分开的。设置了SCROLL_FLAG_ENTER_ALWAYS后,从任意位置都能滑出TabLayout。又因为有snap的效果,手指松开结束滑动之后又会有吸顶的效果。

标签:
Categories: