【譯】完全理解 Android 中的陰影

若是咱們想創造更好的 Android App,我相信咱們須要遵循 Material Design 的設計規範。通常而言,Material Design 是一個包含光線,材質和投影的三維環境。若是咱們想要在 App 的開發過程當中,跟隨 Material Design 的設計原則,那麼理解 光 與 陰影 就顯得尤其重要了。android

我將嘗試解釋本文中的如下主題。git

  • Android 中的 3D
  • 深度(Depth)
  • Z 軸,Elevation 和 Translation Z。
  • 光源
  • 按鈕狀態(按下和靜止)
  • Outline
  • 自定義的 ViewOutlineProvider

在深刻到陰影和光線以前,我想告訴你咱們的真實環境什麼?github

什麼是 3D?

真實的物質環境,是一個三維空間,這意味着全部的物體對象都有 X、Y 和 Z 維度。Z軸與顯示器的平面垂直對齊,正 Z 軸朝向觀察者延伸。在 Material Design 的世界裏,每一個物體都有 1dp 的厚度。c#

Android 的 Depth

Material Design 不一樣於其餘的設計指南,由於它增長了 Depth(深度)的概念。而 Depth 在真實的視覺中,有重要的意義。併發

咱們能夠認爲咱們桌子上有一層紙,若是咱們再貼一張紙,咱們的眼睛會以爲它有一個深度。ide

讓咱們經過 Material Design 的應用截圖來想象它。工具

讓咱們來看看屏幕上的各個元素。佈局

  • 屏幕(表層 - 深度爲 0 )
  • CardViews
  • App UI 佈局
  • 浮動動做按鈕(Floating Action Button)

這裏面,每一個元素都在另外一個元素之上。CardView 能夠滾動,因此咱們能夠說第一層是可滾動的內容,第二層是 AppBar 佈局,第三層(頂層)是浮動動做按鈕。post

那麼,咱們如何定義層級?咱們如何讓用戶感覺到深度?答案是: Z 軸。學習

Android 中的 Z 值是什麼?

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

  • Elevation:高度,一個靜態值。
  • Translation Z:Z軸變更值,用於動畫的動態值。

我老是在想 Elevation 和 Translation Z 有什麼區別。

Elevation 是靜態的,因此你最好不要動態的去改變他。若是你想在 Z 軸上作動畫的效果(如按下態或者靜止態),你須要使用 Translation Z 屬性。

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

Z Vaue = Elevation + TranslationZ

若是咱們改變兩個具備 Z 值的 View,讓它們相交。Android如何處理屏幕上的層級?讓我用一個我設計的圖表向你展現。

另一個問題,咱們如何看到物體的影子?咱們是須要一個陰影嗎?不是的,咱們是須要一個光源。

Android 中的光源是什麼?

其實問題不在因而什麼?而是在哪裏。

在現實中,若是咱們手持一個手電筒照桌子上的物體(從它的頂部),陰影的長度會縮短,當你下降它的時候,陰影的長度會增長。

那麼在 Android 的 Material Design 中,光源在哪裏?在頂部?仍是有角度的?通過一番研究,我發現這個現象。

Android 中存在兩個光源,頂部那個是關鍵的光源,而另外一個是環境光源,咱們看到的陰影其實是這兩個光源的組合。

讓咱們看看顯示的效果。

在 Android 中,咱們有不少小部件。按鈕、CardView、對話框,抽屜等全部這些都是視圖。若是有一個光源,咱們就有陰影。那麼咱們如何在Android中決定 Z 值呢?Material Design 是如何規定這些的?

有一個示意圖來反映這種狀況。

靜止或者按下

正如我以前提到的,在 Android Framework 中,一些動畫是爲小部件而實現的。若是你在佈局中放置浮動操做按鈕,默認狀況下它將具備 6dp 的 Elevation。可是你會注意到當你按下按鈕時,FAB 的 Elevation 將會提升到12 dp

讓我告訴你,在這個過程當中發生了什麼。

其實FAB有 6dp 的 Elevation。當您按下按鈕時,translationZ 值開始增長。ViewPropertyAnimator 經過將 translationZ 值從 0dp 更改成 6dp 來 讓視圖動起來。若是你釋放按鈕,ViewPropertyAnimator 播放動畫,將 translationZ 從 6dp 變到 0dp。你能夠爲你的視圖建立自定義狀態列表動畫,並將其添加到你的視圖上。

咱們來看一下這個過程的流程圖。

陰影的祕密:Outline

Outline 是一個屬於 android.graphic 下的類,看看它的文檔都說了什麼

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

能夠爲 View 計算,也能夠由 Drawable 計算,以驅動由視圖投射的陰影的形狀,或剪裁視圖的內容。

每一個 View 都有默認的輪廓以顯示其陰影。若是咱們建立一個可繪製的自定義形狀,其輪廓將根據其形狀在內部進行計算。因此,若是咱們畫圓,輪廓將會是圓的。若是咱們繪製矩形,輪廓將是矩形。

總而言之,有一個 Outlin 可讓你以不可見的方式看到這個效果。可是,若是我想建立一個自定義的視圖,並動態地改變它的邊界呢?Android 會爲個人自定義視圖提供了 Outline 嗎?

Android 固然爲咱們提供了自定義 Outline 的辦法,那就是 : ViewOutlineProvider

什麼是 ViewOutlineProvider

在我最近的開源的 ScalingLayout 庫中,我沒有對自定義視圖實現陰影效果。我覺得這是很是漂亮,沒有影子。但要記住 Material Design 的基礎知識,3D,Depth,Z-Value。

在這個動畫中,咱們可能沒法肯定那些地方是能夠被點擊的,並且縮放佈局中並無陰影。

接下來咱們爲自定義的視圖提供動態的輪廓。

public class ScalingLayoutOutlineProvider extends ViewOutlineProvider {

    @Override
    public void getOutline(View view, Outline outline) {
        outline.setRoundRect(0, 0, width, height, radius);
    }
}
複製代碼
public class ScalingLayout extends FrameLayout {
    
    //...
    viewOutline = new ScalingLayoutOutlineProvider(w, h, currentRadius);
    setOutlineProvider(viewOutline);
    //..
    
}
複製代碼

這樣,咱們就爲自定義的 View 增長了高度的支持。

更多有關於 ViewOutlineProvider 的使用中,被簡化的一些基礎知識,你能夠在 ScalingLayout 中找到細節。

https://github.com/iammert/ScalingLayout

做者 | Mert Şimşek

翻譯 | 承香墨影

受權 承香墨影 翻譯併發布

原文地址

今天在公衆號後臺回覆成長『成長』,將會獲得我整理的一些學習資料,也能回覆『加羣』,一塊兒學習進步。

推薦閱讀:

相關文章
相關標籤/搜索