一文完全搞清楚 Material Design

一文完全搞清楚 Material Design

聲明
首先聲明如下介紹的關於 Material Design 的介紹, 都是基於在 Android 環境下,其實 Material Design 是一種爲了讓 UI 頁面更加美觀的設計規範,也能夠按照這種規範應用到 iOSWeb 上。

Material Design 是 Google 在 2014 年 I/O 大會上發佈的一種新的設計規範。這種設計風格給 Android UI 設計帶來了不少的變化。讓頁面變得美感十足。html

Material Design 是一種綜合了傳統優秀的設計和科技創新的設計語言。java

Material Design 的設計靈感來自現實世界中真正的物質材料。Material Design 設計語言強調根據用戶行爲凸顯核心功能,進而爲用戶提供操做指引,經過鮮明、形象的顏色差。添加合適的動做來引導用戶。android

Material Design 強調真實性,有立體感。Material Design 的三維體如今光、繪製面和投射陰影。 全部的材料對象都包含 x,y,z 三個維度。z 軸表明了海拔高度,而不是材料的厚度,這一點不少資料都是錯誤的。材料的厚度永遠是 1 dp 不能改變。x ,y 就是對應了材料的長寬,能夠改變。segmentfault

這裏的材料在Android 世界中就是一個個的控件,咱們能夠把控件想象成現實世界中的物體,規定每一個物體的厚度都是固定不變的,永遠是 1dpx,y就對應了控件的長和寬。api

爲了體現出真實物體的感受,引入了光,陰影等一些概念,這些概念咱們下面會一一說明。bash

爲了配合這種設計規範,Android 又推出了許多相關的控件。這些控件你既能夠單獨引用,也能夠直接經過android.design 包來引入。爲了配合 material desig, android 提供了新的主題、新的配合主題的組件、和自定義陰影和新動畫 apiapp

來看看 Android 爲了配合 Material Design 都增長了哪些新的控件:ide

一些基本概念

3D

在真實的物質世界裏面,是一個三維的環境。全部的物體都有 x,y,z三個維度。在 Material Design 中,每一個物體(也就是你的控件)都有 1 dp 的厚度。動畫

而後這些控件還有海拔的概念,還有影子的概念,這些就體現出了 3 D的感受。ui

Z 的概念

由於強調現實世界的真實性,引入Z 表明了控件的海拔高度。好比說:在一個桌面上,你放了一本書 A,而後在 A 上又放了一本書 B 。這個時候確定會有層次感,B 相對於桌面的海拔高度和 A 相對於桌面的海拔高度確定是不同的。在 Android 中就用 Z 來表明控件的海拔高度。

爲了知足 Material Design 的層次要求,android 5.0 後增長了 Z 軸,用來表示控件的海拔,海拔的效果具體體如今陰影上。

Z = elevation + translationZ

View 中的 Z 的值有兩部分組成:

注意對 translationZ 的設置,若是單純的設置控件高度的話,應該是設置 elevation 。而不是 translationZ

  • elevation :海拔高度,用來指定控件靜止海拔高度 elevation 屬性 也能夠在代碼中經過 setElevation 來設置。

    在 Android 中 elevation 這個屬性表明了海拔高度,這個值是永遠有效的,只是若是沒有陰影的話,可能體現不出來,只能經過下面的海拔演示來體現出來。

  • TranslationZ: 動態海拔高度偏移高度,是一個偏移的距離,是用來做動畫效果,不然不要使用。

    Translation Z 是動態的,當建立一個項目,增長一個按鈕,當按下按鈕會陰影變大了。實際上 Elevation 並無變化,而是 Translation Z 屬性在變化。這是 Android 使用默認的狀態列表動畫,更改 Z 屬性。

    按鈕的動做效果,默認 FAB 有 6dp 的Elevation,當按下按鈕時 translation Z 值開始增長。ViewPropertyAnimator 經過將 translation Z 的值從 0 dp改成 6 dp 來讓視圖動起來。若是釋放按鈕,ViewPropertyAnimator 播放動畫,將 translationZ 從 6 dp變到 0 dp。咱們能夠給咱們的視圖建立自定義狀態列表動畫,添加到視圖上。

Z 屬性會擴大 View 的顯示區域(主要是控件自己大小+陰影),若是它的大小大於或者等於父視圖的大小,那麼它的陰影效果就沒法顯示了,view 並不會由於 z 的屬性而縮小自身去顯示陰影。

Z屬性不只影響着view的陰影效果,還影響着view的繪製順序,在同一個父view內部,Z屬性越小,繪製的時機就越早。也就是優先被繪製,而z屬性越大,則繪製時間越晚,後繪製的將會遮蓋住先繪製的,只有Z屬性相同,才按照添加的順序繪製。

海拔

其實上面介紹Z 的時候就介紹海拔了,海拔就是爲了表現層次感所引入的,現實世界中都有海拔的概念,Z 的值就是表明了海拔。

海拔高度指的是從一個表面到另外一個表面之間的距離,元素的海拔高度指明瞭元素表面之間的距離以及陰影的深度。

海拔高度是兩個表面在 Z 軸上的距離,單位也是使用的 dp,一個子元素的海拔是相對於父元素而言的。

海拔高度分爲:靜止狀態海拔高度和動態海拔高度偏移。

靜態狀態海拔高度:全部的元素都有一個靜止的海拔高度(elevation)。

動態海拔高度偏移:指的是從靜止狀態向目標海拔移動的距離(translationZ)

組件的海拔高度:

  • 同一組件在不一樣的應用中,海拔高度是相同的,好比:不一樣應用中的浮動操做按鈕的海拔是相同的
  • 同一組件在不一樣的平臺和設備中,可能會有不一樣的海拔高度,這主要和環境深度有關。好比:電視具備比桌面更大的深度,由於屏幕更大,用戶觀看的距離更遠。一樣電視和桌面的深度比移動設備更深。

某些類型的組件具備響應式的海拔高度,會根據用戶的輸入(例如 正常狀態、獲取焦點、按下)和系統事件來改變自身的海拔。這些海拔高度的改變一般是經過動態海拔高度偏移來實現的。

動態海拔高度偏移是組件從靜止海拔高度向目標海拔高度所移動的距離。全部組件在被按下時,默認所增長的海拔高度是同樣的。一旦輸入事件完成或取消,組件會回到原來靜止的海拔高度。

這張圖中,控件的海拔高度就不一樣,表現出層次感。

好比這張圖,手機屏幕能夠當作是水平面,海拔高度爲0,上面有不少控件,它們的海拔高度是不同的,就表現出層次感了。

海拔的演示

好比 CardView 和 TextView

<RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="200dp">

        <androidx.cardview.widget.CardView
            android:layout_width="match_parent"
            android:layout_height="match_parent">

        </androidx.cardview.widget.CardView>
        <TextView
            
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/app_name"/>
    </RelativeLayout>
這樣的話 TextView 是不會顯示出來的,由於 TextView 的默認海拔是0 ,就被 Cardview 給擋住了,由於 CardView 的默認海拔是 2dp,若是你將 TextView 的海拔設置爲 3dp 這個時候 TextView 就能夠顯示了。
複製代碼

通常控件的標準海拔

  • 應用欄:4dp
  • 按鈕:靜止狀態 2dp 按下狀態:8dp
  • 浮動操做按鈕(FAB)靜止:6dp 按下:12dp
  • 卡片 靜止:2dp 浮動狀態:8dp
  • 菜單和子菜單:菜單:8dp 子菜單:9dp(每一個子菜單+1)
  • 對話框 24dp
  • 抽屜式導航 16dp
  • 刷新指示器 3dp
  • 快速入口/搜索欄 靜止2dp 滾動3dp
  • snackbar 6dp
  • 開關 1dp

物體的層級結構

全部的物體都是根據父-子關係描述的,子元素會繼承父元素的變化屬性,好比位置、旋轉、海拔高度。同級的物體在層次結構中屬於同一層。

好比說:咱們桌子上有一層紙,若是咱們再貼一張紙,咱們的眼睛就會以爲有一個深度。

一樣的效果,左邊就有深度的感受,有層次感。

深度(Depth)

深度(depth)的意思就是材質環境中全部的元素都是沿着 Z 軸水平、垂直和以不一樣的深度移動,在 Z 軸的正方向而且在但是範圍內的點的高度。其實就海拔。

輪廓

默認狀況下,全部的view都是矩形的,雖然能夠給view設置背景圓形的圖片,便可以在界面顯示出圓形的內容,可是view的大小實際上依然是矩形,而且設置的圖片實際上也是矩形的,只是圓形之外的區域是透明色。

若是根據view大小來生成對應的陰影,就會出現很奇怪的效果,(一個看起來圓形的view展現出的確實一個矩形的陰影)爲了解決這個問題,view增長了一個新的描述來指明內容顯示的形狀,這就是輪廓。

輪廓(Outlines) 表明圖形對象的外形狀,並肯定了對於觸摸反饋的波紋區域。

每一個 view 都有默認的輪廓(其實有的 View 也沒有默認的輪廓,好比 TextView)。若是咱們自定義一個 View,其輪廓應該由咱們本身來實現其輪廓。

輪廓的實現

①經過shape設置的背景,view會自動根據shape的形狀進行輪廓斷定, ②經過color設置的背景,view默認其輪廓和view的大小同樣。 ③可是經過圖片進行背景設置,view則沒法獲知輪廓的形狀,這個時候就須要手動進行指定了

手動指定輪廓

當默認輪廓很差使,或者是咱們本身定義的View 的話,就須要咱們本身經過代碼來指定輪廓了。

在代碼中,能夠經過setOutlineProvider來指定一個view的輪廓。

與輪廓有關的類 Outline

Outline是在 android.graphic 下的類,文檔說明:

定義一個簡單的形狀,用於做爲圖形的邊界區域

能夠做爲一個 View 計算,能夠由 Drawable 計算,用來驅動投射出的陰影形狀,或者裁剪 View 的內容

總之,這個類就是用來給View指定輪廓的。View 的輪廓還能夠經過 outlineProvider 屬性來設置,默認是 background 還有其餘值bounds none paddingBounds

none:即便你設置了 evaluation 也會顯示陰影
background:按背景來顯示輪廓,若是 background 是顏色值,則輪廓就是 view 的大小,若是是 shape 則按shape指定的形狀來做爲輪廓,顯示陰影  若是 background 是圖片或者透明shape的話只能用代碼 `setOutlineProvider()` 來指定輪廓了
bounds:View 的矩形大小做爲輪廓
paddingBounds:View 的矩形大小減去 padding 的值後的大小作輪廓  paddedBounds 和bounds相似,不過陰影會稍微向右偏移一點
複製代碼

若是咱們想建立一個自定義視圖,並動態地去改變它的輪廓,這個時候須要使用 ViewOutlineProvider

經過ViewOutlineProvider這個類咱們能夠本身給 View 添加輪廓

public class MyViewOutlineProvider extends ViewOutlineProvider {

    @Override
    public void getOutline(View view, Outline outline) {
        outline.setRoundRect(0, 0, width, height, radius);
    }
}
// 這樣這個 View 就有輪廓了,而後經過 setElevation 來修改海拔就能夠出現陰影了
//這個方法是提供輪廓,具體的陰影經過 Z 來設置,在輪廓大小固定的狀況下,修改 Z 的大小,會佔用輪廓的空間,看上去輪廓在變小。
view.setOutProvider(new MyViewOutlienProvider);
// 若是不想讓視圖有投射陰影,能夠設置輪廓提供者爲 null
複製代碼
裁剪

View 的裁剪是指將 View 按照輪廓裁剪,能改變 View 的形狀,如圓形頭像:

  • 先設置輪廓

  • 在設置根據輪廓裁剪 View,目前只支持對矩形、圓形、圓角矩形的裁剪

    tvClip.setClipToOutline(true)// 設置對 View 進行裁剪

    經過 outlin.canClip() 方法來檢查是否支持擦肩。

陰影

上面介紹了 3D、海拔、輪廓這些基本的概念,其實這些概念最終有體現效果就是靠陰影。

陰影是一個重要的視覺提示,表示了物體的海拔和運動方向。也是指示兩個面之間距離的惟一視覺元素。物體的海拔高度決定了陰影的外觀。

陰影還能夠用來表示物體的運動方向、表面之間的距離是增長仍是減小。

陰影提供了關於海拔、運動方向和繪製邊緣的提示。不一樣的海拔高度,陰影的表現效果是不一樣的。

通常來講海拔越高,陰影越大,越低陰影越小,可是海拔太大會出現陰影消失的現象(通常是超過20dp)。當物質材料表面比例改變的時候,其陰影不該該發生改變,海拔髮生了變化的時候,其陰影要發生改變。

物質材料內部能夠展現任何形狀和顏色,但其內容不會增長材料的厚度。

陰影的產生是不一樣海拔高度的材料相互疊加產生的,在 Material Design 中,虛擬的光線照射使個人物質材料出現陰影,這裏的光有兩種光,一種是關鍵燈,一種是環境燈。關鍵燈會建立更加銳利的方向性陰影,稱爲關鍵陰影。環境光從各個角度出現,建立擴散的柔和陰影,稱爲環境陰影。

關鍵陰影

環境陰影

關鍵陰影和環境陰影

黑暗下

材質環境中的陰影由關鍵燈光和環境燈光投射共同產生。 在Android和iOS開發中,當光源在沿z軸的各個位置處被「材質」表面阻擋時,會出現陰影。 在Web上,僅經過操縱y軸便可描繪陰影。 如下示例顯示了海拔爲6dp的卡片。

陰影的條件

陰影由輪廓和海拔共同決定。

海拔決定了陰影的大小,輪廓決定了陰影的形狀。

陰影必定須要有輪廓而後海拔增高後才能被投射出來,二者缺一不可。陰影的底層是 native 實現的而不是普通的 2D 漸變效果模擬陰影。

在 Android L 中設置陰影只需兩點

  1. 設置海拔高度(經過 elevation)
  2. 設置輪廓

Button 單純的施加 elevation 是沒有陰影效果的,由於 Button 的陰影效果由 stateListAnimatior 來決定了,若是想要本身給 Button 添加的 elevation 有效果的話,必須將 stateListAnimator = "@null" 才能夠。可是若是stateListAnimator設置爲 null 後,點擊的海拔高度動畫就沒有了,爲此你能夠在 Button 外套一層 LinearLayoutLinearLayout 設置 elevation,記住LinearLayout必定要有背景 。可是設置最好不須要這樣,用 Button 自身的陰影效果就能夠了,它的陰影會根據 Button 在頁面中的位置的不一樣陰影還不一樣。 詳見 Button

參考Materila Design中文 Materila Design官網

完全理解Android中的陰影

各類陰影

中文官網Material動畫效果

相關文章
相關標籤/搜索