1. 程式人生 > >Material Design 之 Touch Feedback

Material Design 之 Touch Feedback

目錄

概要

在 Materil Design(一下簡稱 MD ) 中,當用戶與 UI 互動的時候,觸控反饋(Touch Feedback)可以在互動點提供一種及時的視覺確認效果。

設定觸控反饋

Button 在 MD 中預設就有一個反饋動畫,使用的是 RippleDrawable 類,這個類很有意思,它根據手指與 UI 互動的不同狀態(例如,長按,短按,擡起)轉化為不同的波紋效果(Ripple Effect)。 這個效果就不演示了,相信大家已經都看爛了。

然而,並不是所有的控制元件都有預設的觸控反饋的波紋效果,例如 TextView,那麼,怎麼給這些控制元件新增觸控反饋的波紋效果呢?

可以使用系統提供的兩個屬性來在 XML 中給 View 設定背景,也就是 android:background

  • ?android:attr/selectableItemBackground 提供了有邊界的波紋效果
  • ?android:attr/selectableItemBackgroundBorderless 提供了無邊界的波紋效果

selectableItemBackgroundBorderless 在 API 21 才可用。

在 RelativeLayout 中新增一個 TextView 並居中顯示

    <TextView
        android:layout_width="200dp"
android:layout_height="200dp" android:layout_centerInParent="true" android:clickable="true" android:gravity="center" android:text="設定觸控反饋效果"/>

TextView 的屬性,我特意設定了一個可點選的屬性 android:clickable,這樣才能響應點選事件,後面才能看到波紋效果。

在 Android Studio 中效果如下

這裡寫圖片描述

TextView 預設是沒有邊界的,當我們點選 TextView 後,發現並沒有任何觸控反饋的效果(即使設定了可點選啊屬性)。

那麼,按照前面說的,給 TextView 設定一個觸控反饋的背景屬性,例如,先設定了一個有邊界的波紋屬性

    <TextView
        android:background="?android:attr/selectableItemBackground"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:layout_centerInParent="true"
        android:clickable="true"
        android:gravity="center"
        android:text="設定觸控反饋效果"/>

當點選 TextView 後,效果如下

這裡寫圖片描述

可以看到兩個效果
1.會以觸控點為中心,形成一個波紋效果
2.TextView 顯示出了邊界

有些時候,我們需要 TextView 響應點選事件,但是並不需要 TextView 顯示邊界。例如,在微博中,點選一個使用者的名字,會跳轉到使用者相關的頁面,如果在點選名字的時候,出現了邊界,是不是有點醜陋? 反正我覺得有點醜的。

那麼,如果我們既想有點選波紋效果,又不想出現邊界,就可以用系統提供的無邊界波紋效果

    <TextView
        android:background="?android:attr/selectableItemBackgroundBorderless"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:layout_centerInParent="true"
        android:clickable="true"
        android:gravity="center"
        android:text="設定觸控反饋效果"/>

當點選 TextView 的時候,你只會看到一個波紋效果,而並沒有出現邊界,如下圖所示。

這裡寫圖片描述

系統波紋的顏色是一個淡灰色,我們可以換換

    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <item name="colorControlHighlight">@color/colorAccent</item>
    </style>

這裡寫圖片描述

RippleDrawable

系統提供的波紋效果,是一個灰色的波紋效果,那麼如果我們想自定義的波紋的顏色呢?

前面提到過,Button 預設的觸控反饋效果,是用 RippleDrawable 類實現的,可以在 XML 中定義一個 Ripple Drawable。

res/drawable/ripple.xml

<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
        android:color="@color/colorPrimary">
</ripple>

我給這個 RippleDrawable 定義了一個顏色 android:color="@color/colorPrimary",這就是波紋的顏色。

現在把這個 Ripple Drawale 設定為 TextView 背景

    <TextView
        android:background="@drawable/ripple"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:layout_centerInParent="true"
        android:clickable="true"
        android:gravity="center"
        android:text="設定觸控反饋效果"/>

點選 TextView 效果如下

這裡寫圖片描述

由於用的是模擬器,因此這個波紋不是很明顯,如果在真機上,可以明顯看到一個波紋效果,而這個波紋顏色,就是剛才設定。

那麼,現在長按下,可以看到如下效果

這裡寫圖片描述

ok,這個長按效果不糾結了,知道這麼回事就行了。

那麼現在有一個問題,怎樣才能像 Button 的波紋效果一樣,大小限制在整個 Button 內呢?

RippleDrawable 是繼承自 LayerDrawable,所以可以加一層 layer,來限制波紋的大小。

<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
        android:color="@color/colorPrimary">
    <item android:drawable="@android:color/white"/>
</ripple>

再點選 TextView 後,會有如下效果

這裡寫圖片描述

看懂這個效果後,可能有人會問了,剛才你不是說到給 TextView 設定有帶邊框的波紋很醜嗎? 是的,確實醜。

然而在開發中,我們還可能遇到一種情況,例如 CardView 本身也是沒有觸控反饋效果的,但是它有邊界,如果在 CardView 上新增這個有邊界的波紋,這樣就不醜了吧?具體效果,就留給大家自己去試試了。

其實 RippleDrawable 還有一個很有意思的事情,它又一個遮罩層,id 為 android.R.id.mask,既然叫做遮罩層,那就是限制波紋的範圍了。
剛才看到有界的波紋的範圍其實就是 TextView 的大小範圍,那麼可以用遮罩層來改變這個範圍的形狀,如果大家清楚影象混合的幾種模式,這就很好理解了。

首先為 RippleDrawable 加一個 layer,用來定義遮罩層

<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
        android:color="@color/colorPrimary">

    <item android:drawable="@android:color/white"/>

    <item
        android:id="@android:id/mask"
        android:drawable="@drawable/ripple_mask"/>
</ripple>

注意,遮罩層的 id 一定要定義為 @android:id/mask

再看看 res/ripple_mask.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
       android:shape="oval">
    <solid android:color="@android:color/holo_red_light"/>
</shape>

形狀定義為了 oval,系統會自動根據 TextView 的大小來決定是圓形還是橢圓。

如果按照影象混合原理來看,其實這裡的顏色的透明度只要是不透明的,就能形成遮罩效果,但是從我測試的來看,這裡的顏色可以隨意設定,也不需要關心透明度。

當我們點選 TextView 的時候,就與無邊界的波紋效果一樣,只是這個波紋的圓小一點,如下圖

這裡寫圖片描述

剛才說了,到底是橢圓還是圓是根據 TextView 的大小而定的,如果把 TextView 的高度改下

    <TextView
        android:background="@drawable/ripple"
        android:layout_width="200dp"
        android:layout_height="100dp"
        android:layout_centerInParent="true"
        android:clickable="true"
        android:gravity="center"
        android:text="@string/button"/>

現在 TextView 是長方形了,那麼再點選的時候,效果如下

這裡寫圖片描述

現在就是一個橢圓的波紋效果了,是不是很有點意思。

再回到了 res/ripple_mask.xml,剛才我們說到,顏色其實是可以隨意定義的,那麼我們還要設定這個幹嘛? 當然是為了形成 GradientDrawable 物件了。

這裡用的是填充的方式來形成 GradientDrawable 物件,當然,我們也可以用描邊的方式

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
       android:shape="oval">
    <stroke
        android:width="10dp"
        android:color="@android:color/black"/>
</shape>

那麼點選 TextView ,效果如下

這裡寫圖片描述

這就有點意思了!

參考連結