Android 詳細分析AppBarLayout的七種ScrollFlags

1. 前言

查看Android文檔下,com.google.android.material.appbar 包下的 AppBarLayout.LayoutParams 類有一個 AppBarLayout_Layout_layout_scrollFlags 的屬性,這個屬性可以有七種取值:

這些取值在 xml 裏也有對應的值:

下面分別對這些屬性的在xml裏的使用及效果,進行介紹說明,再簡單介紹一下在代碼裏如何設置這些屬性:

2. 正文

2.1 準備工作

在介紹之前,需要有一個小例子,效果是這樣的:

最上邊的 WanAndroid字體 部分是一個 Toolbar,Tab 區域是 TabLayout,這兩者在 AppBarLayout 佈局裏面,因爲 AppBarLayout 本身是一個 VERTICAL 佈局的 LinearLayout,所以 ToolbarTabLayout 是豎向排列的。然後,下邊是一個 ViewPager 佈局,切換三個 Fragment,每個 Fragment 又是一個 RecyclerView 列表佈局。我們打算把 layout_scrollFlags 添加到 Toolbar 節點下,演示 layout_scrollFlags 各種取值的效果。
對應的 xml 佈局如下:

<?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=".AppBarLayoutActivity">

    <com.google.android.material.appbar.AppBarLayout
        android:id="@+id/appbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay">

        <androidx.appcompat.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:popupTheme="@style/AppTheme.PopupOverlay"
            app:title="WanAndroid"/>

        <com.google.android.material.tabs.TabLayout
            android:id="@+id/tablayout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>
    </com.google.android.material.appbar.AppBarLayout>

    <androidx.viewpager.widget.ViewPager
        android:id="@+id/viewpager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

2.2 noScroll

Disable scrolling on the view. This flag should not be combined with any of the other scroll flags.
關閉在這個View上的滾動。這個 flag 不應該和其他滾動 flag 組合使用。

在沒有添加任何 layout_scrollFlags 時,AppBarLayout 的子 View 是沒有滾動效果的。這等價於添加了 app:layout_scrollFlags="noScroll",如下:

<androidx.appcompat.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:layout_scrollFlags="noScroll"
            app:popupTheme="@style/AppTheme.PopupOverlay"
            app:title="WanAndroid"/>

運行效果如下:

2.3 scroll

The view will be scroll in direct relation to scroll events. This flag needs to be set for any of the other flags to take effect. If any sibling views before this one do not have this flag, then this value has no effect.
設置了此 flag 的 View 會直接關聯滾動事件而滾動。其他的 flag 要生效,這個 flag 是需要設置的。如果在這個View前面的任何兄弟 View 沒有設置這個 flag,那麼這個值就沒有效果。

這個文檔解釋的內容比較多。我們一一來演示。首先在 Toolbar 下添加這個 flag

        <androidx.appcompat.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:layout_scrollFlags="scroll"
            app:popupTheme="@style/AppTheme.PopupOverlay"
            app:title="WanAndroid"/>

運行一下,看效果,可以看到 Toolbar 可以隨着滾動事件而滾動:

現在我們把 app:layout_scrollFlags="scroll" 挪到 TabLayout 上,而這時 Toolbar上是沒有 scroll 設置的:

        <androidx.appcompat.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:popupTheme="@style/AppTheme.PopupOverlay"
            app:title="WanAndroid"/>

        <com.google.android.material.tabs.TabLayout
            android:id="@+id/tablayout"
            app:layout_scrollFlags="scroll"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>

運行一下,效果是這樣的:

可以看到和沒有設置任何 layout_scrollFlags 的效果是一模一樣的。這是怎麼回事呢?看一下上面的文檔說明:如果在這個View前面的任何兄弟 View 沒有設置這個 flag,那麼這個值就沒有效果。因爲我們在 TabLayout 上面設置了 app:layout_scrollFlags=“scroll”,而位於 TabLayout 之前的 Toolbar 沒有設置 app:layout_scrollFlags=“scroll”,所以 TabLayout設置的 app:layout_scrollFlags="scroll" 沒有效果。
那麼怎麼纔能有效果呢?在 Toolbar上也設置 app:layout_scrollFlags="scroll"。這時的效果如下:

可以看到 ToolbarTabLayout 一起都隨着滾動事件而滾動了。

2.4 enterAlways

When entering (scrolling on screen) the view will scroll on any downwards scroll event, regardless of whether the scrolling view is also scrolling. This is commonly referred to as the ‘quick return’ pattern.
當進入(即在屏幕上的滾動手指由上向下滑動)時,設置了這個 flag 的 View一遇到向下的滾動事件就會滾動,不管滾動的 View是否也在滾動中。這通常被稱爲“快速返回”模式。
更新 Toolbar 的 layout_scrollFlags 如下:

        <androidx.appcompat.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:layout_scrollFlags="scroll|enterAlways"
            app:popupTheme="@style/AppTheme.PopupOverlay"
            app:title="WanAndroid"/>

運行效果如下:
scroll|enterAlways
和上面單獨設置 scroll 的效果相比,在滾出屏幕時的效果是一樣的,不同的地方在於滾入屏幕時:scroll 設置,先滾動 Scrolling View,再滾動設置 flag 的 View(即 Toolbar);scroll|enterAlways 設置,先滾動設置 flag 的 View(即 Toolbar),再滾動 Scrolling View。

2.5 enterAlwaysCollapsed

An additional flag for ‘enterAlways’ which modifies the returning view to only initially scroll back to it’s collapsed height. Once the scrolling view has reached the end of it’s scroll range, the remainder of this view will be scrolled into view. The collapsed height is defined by the view’s minimum height.
這是 ‘enterAlways’ 的一個附加的 flag。作用是修改返回的 View的進入效果。首先,返回的View會滾動返回到它的摺疊高度。當滾動的 View一達到它的滾動範圍的末尾時,返回的View還在視圖外的部分就會滾動進入視圖。摺疊高度是通過返回的View的最小高度來定義的。

可以看到額外添加這個 flag,是把進入過程分開走,首先Toolbar會滾動到摺疊的高度,等列表滾動到末尾時,Toolbar的剩餘部分纔會滾動進入視圖。
修改 xml 如下,記得添加 minHeight 屬性:

        <androidx.appcompat.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            android:minHeight="20dp"
            app:layout_scrollFlags="scroll|enterAlways|enterAlwaysCollapsed"
            app:popupTheme="@style/AppTheme.PopupOverlay"
            app:title="WanAndroid"/>

運行一下,看效果:

2.6 exitUntilCollapsed

When exiting (scrolling off screen) the view will be scrolled until it is ‘collapsed’. The collapsed height is defined by the view’s minimum height.
當退出(即手指在屏幕上由下向上滑動)時,設置這個 flag 的 View 會滾動直至它的摺疊高度。摺疊高度是由這個View的最小高度來定義的。

這個 flag 關注的是退出過程。
修改 xml 代碼如下:

        <androidx.appcompat.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            android:minHeight="20dp"
            app:layout_scrollFlags="scroll|exitUntilCollapsed"
            app:popupTheme="@style/AppTheme.PopupOverlay"
            app:title="WanAndroid"/>

運行一下,看效果:

2.7 snap

Upon a scroll ending, if the view is only partially visible then it will be snapped and scrolled to its closest edge. For example, if the view only has its bottom 25% displayed, it will be scrolled off screen completely. Conversely, if its bottom 75% is visible then it will be scrolled fully into view.
滾動一結束,如果這個 View 僅僅部分可見那麼它就會被 snapped 並滾動到它最近的邊緣。例如,如果這個 View 僅有底部的 25% 部分可見,那麼它就會完全地滾出屏幕。相反,如果它有底部的 75% 部分可見,那麼它就會全部滾入屏幕。

關於 snap 這個單詞的理解,有人翻譯成吸附效果。我查了一下詞典,覺得這個意思比較貼切:

V-ERG (使)發出吧嗒一聲(合上或打開)
If you snap something into a particular position, or if it snaps into that position, it moves quickly into that position, with a sharp sound.
He snapped the notebook shut... 

他啪的一聲合上了筆記本。

He snapped the cap on his ballpoint... 

他吧嗒一聲把筆帽扣在了圓珠筆上。

修改 xml,如下:

        <androidx.appcompat.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:layout_scrollFlags="scroll|snap"
            app:popupTheme="@style/AppTheme.PopupOverlay"
            app:title="WanAndroid"/>

運行一下,看效果:

2.8 snapMargins

An additional flag to be used with ‘snap’. If set, the view will be snapped to its top and bottom margins, as opposed to the edges of the view itself.
這是和 ‘snap’ 一起使用的額外的 flag。如果設置的話,這個 View 將會被 snap 到它的頂部外邊距和它的底部外邊距的位置,而不是這個 View 自身的上下邊緣。

修改xml,如下:

        <androidx.appcompat.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:layout_scrollFlags="scroll|snap|snapMargins"
            app:popupTheme="@style/AppTheme.PopupOverlay"
            android:layout_marginTop="30dp"
            android:layout_marginBottom="30dp"
            app:title="WanAndroid"/>

效果如下:

2.9 在代碼中設置 scrollFlags

        val layoutParams = toolbar.layoutParams as AppBarLayout.LayoutParams
        layoutParams.scrollFlags =AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL

3. 最後

本文的代碼可以去這裏下載到。

參考

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章