1. 程式人生 > >Android 自定義陰影Shadow顏色,大小等樣式

Android 自定義陰影Shadow顏色,大小等樣式

最近在專案碰到一個比較頭疼的專案,設計師需要給ui圖中的一些按鈕之類的東西新增陰影。乍一看設計圖,這沒啥嘛,咱們Android中不是有這個屬性嘛,於是擼起袖子開搞:

	<TextView
    android:id="@+id/btn_test_performance"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:elevation="5dp"
    android:text="@string/hello"
    android:background="@drawable/shape_round_white"
    android:padding="20dp"
    android:layout_marginTop="10dp"
    android:layout_gravity="center"/>

android:elevation=""屬性就是給View新增陰影,引數越大則陰影的大小越大,體現出當前view的懸浮高度越高。

程式碼敲完跑起來看一下效果圖,圖1 恩,效果還可以。於是把所有需要陰影的控制元件全部加上android:elevation屬性,美滋滋,有api就是好。事情發展得很順利,直到我們設計師找到我,“你是Android設計師吧,你這個顏色有點不對啊,我們標記的顏色比較淡,你這個顏色有點深”。。。瞬間凌亂,wtf,這個顏色是系統自帶的啊,我怎麼控制。於是上網google一下,怎樣修改elevation的顏色屬性。一頓搜尋後,無果。禍不單行,我們測試工程師也找了過來,“你這個頁面不對啊,我們設計圖上這裡有陰影,你這裡啥也沒有”。???什麼鬼,不可能啊,我這裡都有的,你這個包有問題吧。於是把測試的測試機拿過來自己裝上一個一跑。。。尼瑪,什麼鬼,真沒有。回頭再看一遍自己的程式碼發現在android:elevation上有一個黃色的警告,之前沒注意,滑鼠放上去一看,圖2

。。原來是版本適配問題,看來是api是不能用了,只能通過一些其他的手段去實現了。經過漫長的思考人生後,想出下面幾個思路:

  1. 自定義shape.xml drawable檔案去給控制元件設定背景,使用Gradient漸變色屬性實現陰影樣式。
  2. 找ui設計師,讓他們切一個帶陰影的圖給我們(找ui能解決的都不是事~)
  3. 自己程式碼實現一個帶背景的自定義view

經過不停地自我推翻以及方案被否定後還是留下了第三個方案,主要原因如下:

  • 第一個方案的漸變色不支援四周向中間的漸變,只能支援線性或者環裝形式漸變,這兩種都不滿足需要,因為陰影本身是一個四周一層很淡的顏色包圍,在一個矩形框的層面上顏色大概一致,所以這個思路無法實現這個需求,拋棄。
  • 第二個方案詢問了一下我們ui。。他們給出的結果是如果使用切圖的話那標註的話很難標,身為一個優秀的設計師大多對畫素點都和敏感,介面上的畫素點有一點不協調那都是無法容忍的。。
  • 那麼就只剩下第三個方案了

經過資料查詢,發現有兩個方式用來寫陰影很合適。在我們Android中自定義view的時候有以下兩個方法,我們可能用的都不是很多:

  1. paint.setShadowLayer(float radius, float dx, float dy, int shadowColor); 這個方法可以達到這樣一個效果,在使用canvas畫圖時給檢視順帶上一層陰影效果。 簡單介紹一下這幾個引數: radius: 陰影半徑,主要可以控制陰影的模糊效果以及陰影擴散出去的大小。 dx:陰影在X軸方向上的偏移量 dy: 陰影在Y軸方向上的偏移量 shadowColor: 陰影顏色。 終於找到了設定顏色的,通過設定shadowColor來控制檢視的陰影顏色。 我們試一下使用paint.setShadowLayer(10, 0, 0, Color.RED)的效果: 圖3 這個是在4.1的裝置上的效果圖,上面的hello world使用elevation,可以看到絲毫沒有效果。下面是使用自定義view的方式畫出來的一個圖,可以看到效果是很nice的,這個也實現了顏色的可調整性,完美解決。

這裡也順帶說下第二種思路,這邊我試了一下效果沒這邊好,有可能是我沒調整好,個人覺得應該也是能實現這種效果的, paint還有一個方法是paint.setMaskFilter();這裡通過設定BlurMaskFilter()應該也能達到這種效果。

對於第一種方案,經過思考決定封裝成一個Viewgroup比較合適,將需要設定陰影的檢視放到Viewgroup裡面。其中涉及到幾個屬性,陰影的寬度,view到Viewgroup的距離,如果檢視和父佈局一樣大的話,那陰影就不好顯示,如果要能夠顯示出來就必須設定clipChildren=false。還有就是檢視自帶的圓角,大部分背景都是有圓角的,比如上圖中的圓角,需要達到高度還原陰影的效果就是的陰影的圓角和背景保持一致。 這裡把這幾個屬性抽成自定義屬性:

<declare-styleable name="ShadowContainer">
    <attr name="containerShadowColor" format="color"/>
    <attr name="containerShadowRadius" format="dimension"/>
    <attr name="containerDeltaLength" format="dimension"/>
    <attr name="containerCornerRadius" format="dimension"/>
    <attr name="deltaX" format="dimension"/>
    <attr name="deltaY" format="dimension"/>
</declare-styleable>
  • containerShadowColor:設定陰影顏色
  • containerShadowRadius:設定陰影的擴充套件距離
  • containerDeltaLength:設定子View到ShadowContainer的距離
  • containerCornerRadius:設定子View背景的圓角大小
  • deltaX: 設定陰影向下移動距離
  • deltaY: 設定陰影在Y軸移動距離

圖4 這裡使用畫圖的方式解釋一下這幾個引數: 圖中的紅色區域是需要設定陰影的View,外部黑色邊框包裹的區域是ShadowContainer,中間的白色部分是陰影展示的區域,這塊的大小根據設定的containerDeltaLength控制。這裡打個比方,如果目標view需要距離左邊20dp的話,而中間的containerdeltaLength為10dp,則只需要將ShadowContainer的marginLeft設定為10dp就可以了,這樣可以完美控制子view的位置。

此外,containerShadowRadius影響的是陰影在空白區域的佔比情況,如果containerShadowRadius小一點的話陰影倒是不大, 但是如果太大的話那麼空白區域可能裝不下陰影面積。這裡需要看情況調整。

使用方式:

<example.chenj.com.apptest.ShadowContainer
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginTop="50dp"
    android:layout_gravity="center"
    app:containerCornerRadius="5dp"
    app:containerDeltaLength="5dp"
    app:containerShadowColor="#f00"
    app:containerShadowRadius="5dp"
    >

    <View
        android:layout_width="80dp"
        android:layout_height="80dp"
        android:background="@drawable/shape_round_white"/>

</example.chenj.com.apptest.ShadowContainer>