iT邦幫忙

2022 iThome 鐵人賽

DAY 14
0

大綱

  • Using
  • Type
  • Regular FABs
  • Mini FABs
  • Extended FABs
  • Speed dial
  • Custom Style

Usgin

在進入實作之前,官方有提到一些建議:如果 FAB 放在 CoordinatorLayout 佈局中,可以免費獲得某些行為設置。它會自動移動,以便任何顯示的 Snackbars 都不會覆蓋它,並且在被 AppBarLayout 或 BottomSheetBehavior 覆蓋時會自動隱藏。如果有上述情況的話,建議先將佈局改變,在下面實作上我就不會特別去應用了

Visibility

FAB 有 hide()、show() 方法,會透過動畫呈現 FAB的出現與消失,show () 動畫使元件增長並將其淡入,hide() 動畫則縮小元件並將其淡出。所以在設計畫面與 FAB 互動時,只要直接使用這兩個方法即可

// To show:
fab.show()
// To hide:
fab.hide()

Extending and Shrinking

使用 extend、shrink 方法,可在 extended FAB 上,收縮 Text Label。讓 extended FAB 變為 Regular FAB。設計上,如果只設置 text label 而沒有 icon,此功能不會觸發

// To extend:
extendedFab.extend()
// To shrink:
extendedFab.shrink()

Sizing FABs

  • normal : 56dp
  • mini : 40dp
  • auto (default) : 在 auto之下,大小會隨著 window 改變,如果螢幕大小寬度為 470 以上會自動調整為 normal ( 56dp ),以下的話則會調整為 mini ( 40dp )

以上這些都可以透過 app:fabSize 來快速設定

<com.google.android.material.floatingactionbutton.FloatingActionButton
        ...
        app:fabSize="normal"
        ...
 />
  • CustomSize : 如果上述無法滿足情境,可以使用 app:fabCustomSize來客製化 size ,而已經設置app:fabSize,將會被覆蓋掉。如果客製化的 size 被 clearCustomSize清除掉,復原回 setSize() 或是app:fabSize 設置的大小

Type

實作上就分為這三個種類

  1. Regular FABs
  2. Mini FABs
  3. Extended FABs

Regular FABs

image alt

預設上就是 Regular

in layout

<com.google.android.material.floatingactionbutton.FloatingActionButton
        android:id="@+id/regular_fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="10dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:srcCompat="@drawable/ic_baseline_add_24"
        />

Anatomy

image alt

  1. Container
  2. Icon

Mini FABs

image alt

只要透過 app:fabSize 就能更改 size

in layout

<com.google.android.material.floatingactionbutton.FloatingActionButton
      ...
      app:fabSize="mini"/>

Anatomy

image alt

  1. Container
  2. Icon

設置與組成上都與 Regular 相同,所以下方屬性也一並講解

Key properties

Container

若想透過 app:backgroundTint 改變 FAB button 的顏色時,要注意,改變過程中會發現,沒有填滿整個 FAB ,這是因為有 Stroke 的存在,如果不想要的話可以透過 app:borderWidth 設為 0dp

Icon

Style

Extended FABs

image alt

Extended FABs 是 MaterialButton 的子類,與 Regular 不同,所以在一些屬性的設置上,有所不同。例如,設置 icon 在 Regular 是用 app:srcCompat 而在 Extended 是用 app:icon,其他屬性在下面會陸續介紹到

in layout

<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
    android:id="@+id/extended_fab"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_margin="16dp"
    android:text="@string/extended_fab_label"
    app:icon="@drawable/ic_plus_24px"/>

Anatomy

image alt

  1. Container
  2. Icon (Optional)
  3. Text label

Key attributes

Container

與 Regular 相比,多了 Stroke 可以設置

app:strokeWidth
app:strokeColor	

Icon

icon 方面還能自行設置大小與間距

Text label

Speed dial

image alt

這部分在實作方面官方並沒有提到,所以我自己做了一個希望能幫到大家,整體的實作是參考這個網址,我會分成幾個部分陸續帶大家完成

In layout

就先從元件設置開始,方位的話大都是放在右下方,而每個子 FAB 要相連再一起,最後與主 FAB 串連起來。然後把所有子 FAB 都先隱藏起來,在點擊主 FAB 後才彈出顯示

  <!-- Expandable FABs  -->
    <com.google.android.material.floatingactionbutton.FloatingActionButton
        android:id="@+id/expandable_fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginEnd="24dp"
        android:layout_marginBottom="224dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:srcCompat="@drawable/ic_baseline_add_24" />

    <com.google.android.material.floatingactionbutton.FloatingActionButton
        android:id="@+id/share_expand_fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="16dp"
        android:visibility="gone"
        app:layout_constraintBottom_toTopOf="@+id/expandable_fab"
        app:layout_constraintEnd_toEndOf="@+id/expandable_fab"
        app:srcCompat="@drawable/ic_baseline_share_24" />

    <com.google.android.material.floatingactionbutton.FloatingActionButton
        android:id="@+id/search_expand_fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="16dp"
        android:visibility="invisible"       
        app:layout_constraintBottom_toTopOf="@+id/share_expand_fab"
        app:layout_constraintEnd_toEndOf="@+id/share_expand_fab"
        app:srcCompat="@drawable/ic_baseline_search_24" />

Animation

再來就是動畫,如果對設計動畫不熟的朋友,可以先看一下文檔。我們會需要四個動畫檔,分別是:主FAB的轉入與轉出、子FAB的跳出與收回

roate_open

由於我們想要主FAB 有旋轉傾斜的效果,將 Degrees 從 0 到 45

<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:fillAfter="true">
<!-- 讓元件旋轉的動畫效果 -->
    <rotate
        android:fromDegrees="0"
        android:pivotX="50%"
        android:pivotY="50%"
        android:toDegrees="45"
        android:duration="300"/>
</set>

roate_close

那關閉就是 open 的相反,將 Degrees 從 45 到 0 旋轉回來

<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:fillAfter="true">
    <rotate
        android:fromDegrees="45"
        android:pivotX="50%"
        android:pivotY="50%"
        android:toDegrees="0"
        android:duration="300"/>

</set>

from_bottom

要讓子FAB 彈出,由於在 layout 預設狀態下,子 FAB 是位在主 FAB 上方的,為了在動畫上看起來是從底部彈出,所以在 設置 fromYDelta、toYDelta,讓元件 from 它本身相對位置的下方,to 回到原本的位置,再透過 縮小 子FAB 讓用戶能迅速辨識

<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:fillAfter="true">
<!-- 移動動畫效果 -->
    <translate
        android:duration="500"
        android:fromYDelta="300%"
        android:toYDelta="0%" />
<!-- 縮放的動畫效果 -->
    <scale
        android:pivotY="50%"
        android:pivotX="50%"
        android:toXScale="0.9"
        android:toYScale="0.9"/>
<!-- 透明度變化的動畫效果(淡入淡出) -->
    <alpha
        android:fromAlpha="0"
        android:toAlpha="1"
        android:duration="800"/>

</set>

to_bottom

與 from 做出相反的設置,將子 FAB 收起,而這邊在 淡出的速度要快一點,否則會讓用戶看到 translate 回到主 FAB 下方的樣子,避免讓用戶誤會

<set xmlns:android="http://schemas.android.com/apk/res/android"

    android:fillAfter="true">
    <translate
        android:duration="300"
        android:fromYDelta="0%"
        android:toYDelta="300%" />

    <scale
        android:pivotY="50%"
        android:pivotX="50%"
        android:fromYScale="0.9"
        android:fromXScale="0.9"
        android:toXScale="0.9"
        android:toYScale="0.9"/>

    <alpha
        android:fromAlpha="1"
        android:toAlpha="0"
        android:duration="300"/>

</set>

In code

那上方的 layout 與 animation 都設置完後,就開始實際運用了。首先先盡力三個方法,分別為 setAnimation、setVisibility、setButtonClickable,然後透過一個作為 FAB 開關依據的布林值,來控制動畫執行的過程

在設置這些方法之前,我們先將動畫資源檔初始化

private val fromBottom :Animation by lazy { AnimationUtils.loadAnimation(requireContext(), R.anim.from_bottom_animation) }
private val toBottom :Animation by lazy { AnimationUtils.loadAnimation(requireContext(), R.anim.to_bottom_animation) }
private val rotateClose :Animation by lazy { AnimationUtils.loadAnimation(requireContext(), R.anim.rotate_close_animation) }
private val rotateOpen :Animation by lazy { AnimationUtils.loadAnimation(requireContext(), R.anim.rotate_open_animation) }

  • setAnimation (啟動動畫的方法)
private fun setAnimation(isExpanded: Boolean) {
        if (isExpanded){
            binding.searchExpandFab.startAnimation(fromBottom)
            binding.shareExpandFab.startAnimation(fromBottom)
            binding.expandableFab.startAnimation(rotateOpen)
        }else{
            binding.searchExpandFab.startAnimation(toBottom)
            binding.shareExpandFab.startAnimation(toBottom)
            binding.expandableFab.startAnimation(rotateClose)
        }
    }
  • setVisibility ( 顯示子 FAB 的方法 )
private fun setVisibility(isExpanded: Boolean) {
        if (isExpanded){
            binding.searchExpandFab.visibility = VISIBLE
            binding.shareExpandFab.visibility = VISIBLE
        }else{
            binding.searchExpandFab.visibility = INVISIBLE
            binding.shareExpandFab.visibility = INVISIBLE
        }
    }
  • setButtonClickable ( 開啟子 FAB 點擊功能的方法)
private fun setButtonClickable(isExpanded: Boolean) {
        if (isExpanded){
            binding.searchExpandFab.isClickable= true
            binding.shareExpandFab.isClickable = true
        }else{
            binding.searchExpandFab.isClickable = false
            binding.shareExpandFab.isClickable = false
        }
    }

都設置完後,到主 FAB 去設置點擊邏輯就大功告成了。透過 isFabExpanded 布林值的變化,去啟動各種功能。當然這邊可以在包得更好,例如包成類似 hide or show 等方式,在可讀性在會更好,但礙於時間關係,小弟弟我呢就留給大家發揮了

private var isFabExpanded = false
    
binding.expandableFab.setOnClickListener {
            isFabExpanded = !isFabExpanded
            setVisibility(isFabExpanded)
            setAnimation(isFabExpanded)
            setButtonClickable(isFabExpanded)
        }

Style

風格檔上,依據在使用 exapnded FABs 時,是否有添加 icon,若有記得去改成適用 Icon 的 style,讓 Material Design 幫助我們設置適當的 icon padding

Custom Style

又來到愉快的自定義 style 環節 (自虐環節),這邊一樣給大家看我魔改的成品
FAB 的顏色都是隨著主題的相對色,也就是 colorSecondary、colorOnSecondary,就會是我下手的目標

<style name="Widget.App.ExtendedFloatingActionButton"parent="Widget.MaterialComponents.ExtendedFloatingActionButton.Icon">
    <item name="materialThemeOverlay">@style/ThemeOverlay.App.FloatingActionButton</item>
    <item name="shapeAppearanceOverlay">@style/ShapeAppearance.App.SmallComponent</item>
</style>

<style name="Widget.App.FloatingActionButton" parent="Widget.MaterialComponents.FloatingActionButton">
    <item name="materialThemeOverlay">@style/ThemeOverlay.App.FloatingActionButton</item>
    <item name="shapeAppearanceOverlay">@style/ShapeAppearance.App.SmallComponent</item>
</style>

<style name="ThemeOverlay.App.FloatingActionButton" parent="">
    <item name="colorSecondary">@color/lightGrey</item>
    <item name="colorOnSecondary">@color/white</item>
</style>
    
<style name="ShapeAppearance.App.SmallComponent" parent="ShapeAppearance.MaterialComponents.SmallComponent">
    <item name="cornerFamily">cut</item>
    <item name="cornerSize">4dp</item>
</style>

成品

小結

FAB 設置上 Material Design 都幫我們設計好了,細節部分也支援很多屬性可以調整,不一定要用 style 才能改動。重點反而是在於動畫呈現那一個部分,因為這是與一般 Button 最大的差別之處,甚至要去做出在一些符合使用者情境設計的功能,例如我們剛剛實作的 Speed dial,還有配合畫面跳轉做的 transitions 動畫。如何才能實作出配合當前畫面上下文的設計實作,才是 FABs 真正困難也最有價值的地方

若對實作還是有點不懂的,這邊提供我的 Github 方便大家參考


上一篇
Day 13 - Floating action button (Design)
下一篇
Day 15 - Divider ( Design & Implementation )
系列文
從 Google Material Design Components 來了解與實作 Android 的 UI/UX 元件設計30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言