Android 這些 Drawable 你都會用嗎?Part1

1. 概述

在實際開發的過程中,除了廣為人知的利用 StateListDrawable 設置按鈕點擊特效,我們有時可能會接到一些這樣的需求,比如要求我們的頭像顯示成圓形或者圓角矩形,甚至要加上可變顏色的邊框,或者要求你做一套啟動、暫停、快進和快退的視頻控制按鈕并且可以改變按鈕圖標顏色。可能某些時候第一反應就是用自定義 View 來實現,但是如果熟悉了 Drawable 的用法之后,這些效果同樣可以利用它來完成,而選擇哪種 Drawable 來實現也大有講究。

Google 官方文檔中列出了各種各種各樣的 Drawable,那么它們都是如何使用的呢?

google-docs-drawable

本系列文章將分為兩個部分,介紹其中大部分 Drawable 的用法。

2. BitmapDrawable

BitmapDrawable 可以看作是對 Bitmap 的一種包裝,它可以設定 Bitmap 的顯示方式、位置等屬性。

2.1 語法

BitmapDrawable 對應 <bitmap> 標簽定義,xml 語法如下:

<?xml version="1.0" encoding="utf-8"?>
<bitmap
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:src="@[package:]drawable/drawable_resource"
    android:antialias=["true" | "false"]
    android:dither=["true" | "false"]
    android:filter=["true" | "false"]
    android:gravity=["top" | "bottom" | "left" | "right" | "center_vertical" |
                      "fill_vertical" | "center_horizontal" | "fill_horizontal" |
                      "center" | "fill" | "clip_vertical" | "clip_horizontal"]
    android:mipMap=["true" | "false"]
    android:tileMode=["disabled" | "clamp" | "repeat" | "mirror"]  />

其中各個屬性的含義分別是:

屬性 含義
src Bitmap 對象的引用路徑
antialias 抗鋸齒效果,建議開啟
dither 是否防抖動。當位圖像素與屏幕像素不匹配(如 ARGB_8888 的位圖顯示在 RGB_565 的屏幕上)時,防止圖片失真,建議開啟
filter 是否啟用位圖過濾。開啟后,當圖片進行拉伸或者壓縮時,能夠進行平滑過渡
gravity 當位圖尺寸小于容器尺寸時在容器中的擺放位置
mipMap 啟用或停用 mipmap 提示,默認為 false
tileMode 平鋪模式。默認:disable;clamp:復制邊沿的顏色;repeat:水平和垂直方向重復繪制圖片;mirror:水平和垂直方向交替鏡像進行重復繪制

2.2 用法示例

下面以定義一個使用圖片作為背景的 Drawable 為例,展示 BitmapDrawable 的簡單實用方法。

定義

<?xml version="1.0" encoding="utf-8"?>
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
    android:src="@mipmap/kakarotto"
    android:antialias="true"
    android:dither="true"
    android:filter="true"
    android:gravity="center"
    android:tileMode="repeat"/>

使用

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/bitmap_drawable"
    tools:context=".MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

效果圖

bitmap-drawable

3. ShapeDrawable 和 GradientDrawable

官方文檔中對 ShapeDrawable 的定義是這樣的:

A Drawable object that draws primitive shapes. A ShapeDrawable takes a Shape object and manages its presence on the screen. If no Shape is given, then the ShapeDrawable will default to a RectShape.

This object can be defined in an XML file with the <shape> element.

即它是一個用來繪制原始形狀的 Drawable 對象。

而對 GradientDrawable 的定義是:

A Drawable with a color gradient for buttons, backgrounds, etc.

It can be defined in an XML file with the <shape> element. For more information, see the guide to Drawable Resources.

根據描述可知,它是一個具有色彩梯度(color gradient)的 Drawable。

3.1 語法

GradientDrawable 和 ShapeDrawable 都采用 shape 標簽來定義,和 ShapeDrawable 最大的不同的就是它擁有 gradient 屬性,下面以 GradientDrawable 為例,講解 shape 標簽的用法,它的語法如下:

<?xml version="1.0" encoding="utf-8"?>
<shape
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape=["rectangle" | "oval" | "line" | "ring"] >
    <corners
        android:radius="integer"
        android:topLeftRadius="integer"
        android:topRightRadius="integer"
        android:bottomLeftRadius="integer"
        android:bottomRightRadius="integer" />
    <gradient
        android:angle="integer"
        android:centerX="integer"
        android:centerY="integer"
        android:centerColor="integer"
        android:endColor="color"
        android:gradientRadius="integer"
        android:startColor="color"
        android:type=["linear" | "radial" | "sweep"]
        android:usesLevel=["true" | "false"] />
    <padding
        android:left="integer"
        android:top="integer"
        android:right="integer"
        android:bottom="integer" />
    <size
        android:width="integer"
        android:height="integer" />
    <solid
        android:color="color" />
    <stroke
        android:width="integer"
        android:color="color"
        android:dashWidth="integer"
        android:dashGap="integer" />
</shape>

其中各個屬性標簽的含義分別是:

android:shape

表示形狀,它的值可以是 rectangle(矩形)、oval(橢圓)、line(橫線)和 ring(圓環),默認為 rectangle。 此外,當形狀值是 ring 的時候,還有一下幾個屬性可配置:

屬性 含義
android:innerRadius 圓環的內半徑。與 innerRadiusRatio 同時設置時,以 innerRadiusRatio 為準
android:innerRadiusRatio 圓環的內半徑占環寬度的比率
android:thickness 圓環厚度。與 thicknessRatio同時設置時,以 thicknessRatio 為準
android:thicknessRatio 圓環的厚度占環寬度的比率
android:useLevel 一般為 false,否則可能達不到預期顯示效果,除非把它當作 LevelListDrawable 來使用

<corners>

<corners
    android:radius="integer"
    android:topLeftRadius="integer"
    android:topRightRadius="integer"
    android:bottomLeftRadius="integer"
    android:bottomRightRadius="integer" />

指圖形的圓角半徑,僅當 shape 屬性為 rectangle 即形狀是矩形時生效,數值越小越接近直角,android:radius 同時設置四個角的半徑,其他四個屬性則可單獨設置某個角的半徑。

<gradient>

<gradient
    android:angle="integer"
    android:centerX="integer"
    android:centerY="integer"
    android:centerColor="integer"
    android:endColor="color"
    android:gradientRadius="integer"
    android:startColor="color"
    android:type=["linear" | "radial" | "sweep"]
    android:usesLevel=["true" | "false"] />

表示顏色漸變,它的各個屬性值的含義分別是:

屬性 含義
android:angle 漸變的角度。必須是 45 的倍數,默認值為 0。0 為從左到右,90 為從上到下
android:centerX 漸變中心的相對 X 軸位置 (0 - 1.0)
android:centerY 漸變中心的相對 Y 軸位置 (0 - 1.0)
android:startColor 漸變的起始顏色
android:centerColor 漸變的中間顏色
android:endColor 漸變的結束顏色
android:gradientRadius 漸變的半徑,僅在 android:type="radial" 時適用
android:useLevel 一般為 false,否則可能達不到預期顯示效果,除非把它當作 LevelListDrawable 來使用
android:type 漸變類別。它的值可以為:linear(線性),默認值、radial(徑內漸變)和sweep(掃描漸變)

<padding>

距離內容或者子元素的內邊距,每個方向可以單獨設置。

<size>

設置 shape 大小,width 表示寬度,height 表示高度。需要注意的是,這個一般并不是 shape 的最終大小,如果用作 View 的背景,它的大小是由 View 的大小來決定的。

<solid>

表示純色填充,color 屬性為填充的顏色。

<stroke>

邊框描述,它的各個屬性值的含義分別是:

屬性 含義
android:width 邊框寬度
android:color 邊框顏色
android:dashWidth 虛線的線段寬度
android:dashGap 虛線之間的空白間隔

需要注意的是,如果需要設置邊框虛線效果,則需要同時設置 dashWidth 和 dashGap 的值不為 0,否則無法顯示虛線效果。

3.2 用法示例

下面以定義一個圓角并帶有其他效果的 Drawable 為例,展示 GradientDrawable 的簡單用法。

定義

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
       android:shape="rectangle">

    <!--圓角半徑-->
    <corners
            android:topLeftRadius="15dp"
            android:topRightRadius="15dp"
            android:bottomLeftRadius="15dp"
            android:bottomRightRadius="15dp"/>

    <!--內邊距-->
    <padding
            android:left="10dp"
            android:top="10dp"
            android:right="10dp"
            android:bottom="10dp" />

    <!--漸變效果-->
    <gradient android:angle="45"
              android:type="linear"
              android:startColor="#ff0000"
              android:centerColor="#00ff00"
              android:endColor="#0000ff" />

    <!--預設大小-->
    <size
        android:width="200dp"
        android:height="100dp" />

    <!--邊框樣式-->
    <stroke
            android:width="2dp"
            android:color="#000000"
            android:dashWidth="7dp"
            android:dashGap="3dp" />

</shape>

使用

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".GradientDrawableActivity">

    <Button
            android:text="Button"
            android:layout_width="200dp"
            android:layout_height="100dp"
            android:background="@drawable/gradient_drawable"
            android:id="@+id/textView"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"/>

</androidx.constraintlayout.widget.ConstraintLayout>

效果圖

gradient-drawable

4. StateListDrawable

StateListDrawable 可以根據對象的狀態并使用不同的 item(Drawable) 對象來表示同一個圖形。如可以根據 Button 的狀態(按下、獲取焦點等)來顯示不同的 Drawable 從而實現點擊的效果。

4.1 語法

定義 StateListDrawable 的語法格式如下:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android"
    android:constantSize=["true" | "false"]
    android:dither=["true" | "false"]
    android:variablePadding=["true" | "false"] 
    android:autoMirrored=["true" | "false"] 
    android:enterFadeDuration="integer"
    android:exitFadeDuration="integer">
    <item
        android:drawable="@[package:]drawable/drawable_resource"
        android:state_pressed=["true" | "false"]
        android:state_focused=["true" | "false"]
        android:state_hovered=["true" | "false"]
        android:state_selected=["true" | "false"]
        android:state_checkable=["true" | "false"]
        android:state_checked=["true" | "false"]
        android:state_enabled=["true" | "false"]
        android:state_activated=["true" | "false"]
        android:state_window_focused=["true" | "false"] />
</selector>

StateListDrawable 的根標簽為 <selector>,各個屬性標簽的含義分別是:

android:constantSize

由于 StateListDrawable 會根據不同的狀態來顯示不同的 Drawable,而每個 Drawable 的大小不一定相同,因此當 constantSize 屬性的值為 true 時表示固定大小(值為所有 Drawable 固有大小的最大值),值為 false 時則大小為當前狀態下對應的 Drawable 的大小。默認值為 false。

android:variablePadding

表示 StateListDrawable 的 padding 值是否隨狀態的改變而改變,默認為 false。

android:dither

是否開啟抖動效果,默認為 true,建議開啟。

android:autoMirrored

某些西亞國家文字是從右至左的,設置此值表示當系統為 RTL (right-to-left) 布局的時候,是否對圖片進行鏡像翻轉。

android:enterFadeDurationandroid:exitFadeDuration

狀態改變時的淡入淡出效果的持續時間

<item>

每個 item 表示一個 Drawable,item 的屬性含義分別是:

屬性 含義
android:drawable drawable 資源,可引用現有的的 Drawable
android:state_pressed 是否處于被按下狀態
android:state_focused 是否已得到焦點狀態
android:state_hovered 光標是否停留在View的自身大小范圍內的狀態
android:state_selected 是否處于被選中狀態
android:state_checkable 是否處于可勾選狀態
android:state_checked 是否處于已勾選狀態
android:state_enabled 是否處于可用狀態
android:state_active 是否處于激活狀態
android:state_window_focused 是否窗口已得到焦點狀態

4.2 用法示例

下面以定制一個具有點擊效果 Button 的背景為例,展示 StateListDrawable 的用法。

定義

<?xml version="1.0" encoding="utf-8"?>
<selector
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:visible="true"
        android:dither="true"
        android:autoMirrored="true"
        android:enterFadeDuration="200"
        android:exitFadeDuration="200" >
    <!--獲取焦點狀態-->
    <item
            android:state_focused="true"
            android:drawable="@drawable/shape_dark" />

    <!--按下狀態-->
    <item
            android:state_pressed="true"
            android:drawable="@drawable/shape_dark"/>

    <!--默認狀態-->
    <item
            android:drawable="@drawable/shape_light"/>
</selector>

使用

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/holo_orange_dark"
    tools:context=".LayerDrawableActivity">

    <Button
            android:text="Button"
            android:background="@drawable/drawable_state_list"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

效果圖

state-list-drawable

5. LayerDrawable

LayerDrawable 是管理 Drawable 列表的 Drawable。列表中的每個 item 按照列表的順序繪制,列表中的最后 item 繪于頂部。

5.1 語法

定義 LayerDrawable 的語法格式如下:

<?xml version="1.0" encoding="utf-8"?>
<layer-list
    xmlns:android="http://schemas.android.com/apk/res/android" >
    <item
        android:drawable="@[package:]drawable/drawable_resource"
        android:id="@[+][package:]id/resource_name"
        android:top="dimension"
        android:right="dimension"
        android:bottom="dimension"
        android:left="dimension" 
        android:gravity=["top" | "bottom" | "left" | "right" | "center_vertical" |
                          "fill_vertical" | "center_horizontal" | "fill_horizontal" |
                          "center" | "fill" | "clip_vertical" | "clip_horizontal"] />
</layer-list>

LayerDrawable 頂層標簽為 <layer-list>,它可以包含多個 <item> 標簽,每個 item 表示一個 Drawable,item 的屬性含義分別是:

屬性 含義
android:drawable drawable 資源,可引用現有的的 Drawable
android:id item 的 id,使用"@+id/name"的形式表示。可通過 View.findViewById() 或者 Activity.findViewById() 方法查找到這個 Drawable
android:top、android:right、android:bottom、android:left Drawable 相對于 View 在各個方向的偏移量
android:gravity 尺寸小于容器尺寸時在容器中的擺放位置

5.2 用法示例

下面以定義一個圓角并帶陰影效果的 Drawable 為例,展示 LayerDrawable 的簡單使用。

定義

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <!--內部定義一個 Drawable-->
    <item
            android:left="2dp"
            android:top="4dp">
        <shape>
            <solid android:color="@android:color/darker_gray" />
            <corners android:radius="10dp" />
        </shape>
    </item>

    <!--指定現有的 Drawable-->
    <item
            android:bottom="4dp"
            android:right="2dp"
            android:drawable="@drawable/shape_light">
    </item>
</layer-list>

使用

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/holo_orange_dark"
    tools:context=".LayerDrawableActivity">

    <LinearLayout
            android:orientation="vertical"
            android:background="@drawable/layer_drawable"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:padding="10dp"
            android:layout_margin="20dp"
            tools:layout_editor_absoluteY="331dp"
            tools:layout_editor_absoluteX="190dp"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent">

        <TextView android:layout_width="match_parent"
                  android:layout_height="wrap_content"
                  android:textColor="#000000"
                  android:text="I'm a title......."
                  android:textSize="20sp" />

        <TextView android:layout_width="match_parent"
                  android:layout_height="wrap_content"
                  android:textColor="@android:color/darker_gray"
                  android:text="content content content content content content content content..."
                  android:textSize="16sp" />

    </LinearLayout>

</androidx.constraintlayout.widget.ConstraintLayout>

效果圖

gradient-drawable

6. LevelListDrawable

LevelListDrawable 同樣表示一個 Drawable 列表,列表中的每個 item 都有一個 level 值, LevelListDrawable 會根據不同的 level 在不同的 item 之間進行切換。

6.1 語法

定義 LevelListDrawable 的語法格式如下:

<?xml version="1.0" encoding="utf-8"?>
<level-list
    xmlns:android="http://schemas.android.com/apk/res/android" >
    <item
        android:drawable="@drawable/drawable_resource"
        android:maxLevel="integer"
        android:minLevel="integer" />
</level-list>

LayerDrawable 根標簽為 <layer-list>,它可以包含多個 <item> 標簽,每個 item 表示一個 Drawable,item 的屬性含義分別是:

屬性 含義
android:drawable drawable 資源,可引用現有的的 Drawable
android:maxLevel 該 item 允許的最大級別,取值范圍為[0, 10000]
android:minLevel 該 item 允許的最小級別,取值范圍為[0, 10000]

6.2 用法示例

定義

<?xml version="1.0" encoding="utf-8"?>
<level-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item
            android:drawable="@drawable/kakarotto1"
            android:maxLevel="0" />

    <item
            android:drawable="@drawable/kakarotto2"
            android:maxLevel="1" />

    <item
            android:drawable="@drawable/kakarotto3"
            android:maxLevel="2" />

    <item
            android:drawable="@drawable/kakarotto4"
            android:maxLevel="3" />

    <item
            android:drawable="@drawable/kakarotto5"
            android:maxLevel="4" />
</level-list>

使用

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".GradientDrawableActivity">

    <ImageView
            android:text="Button"
            android:layout_width="230dp"
            android:layout_height="150dp"
            android:src="@drawable/drawable_level_list"
            android:id="@+id/img"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"/>

</androidx.constraintlayout.widget.ConstraintLayout>

然后控制 ImageView 的 level 即可顯示出效果:

class LevelListDrawableActivity : AppCompatActivity() {
    lateinit var mImageView: ImageView

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_level_list_drawable)

        mImageView = findViewById(R.id.img)
        for (i in 0..15) {
            mHandler.sendEmptyMessageDelayed(i, (1000 * i).toLong())
        }
    }

    var mHandler: Handler = object: Handler() {
        override fun handleMessage(msg: Message?) {
            msg?.what?.let { mImageView.setImageLevel(it%5) }
        }
    }
}

效果圖

level-list-drawable

7. InsetDrawable

在有些場景下,我們可能需要設置一個全屏的背景圖片,但又想讓背景圖片跟邊框留出一些間隙,這時使用 InsetDrawable 就能很好地解決問題了。

7.1 語法

<?xml version="1.0" encoding="utf-8"?>
<inset
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/drawable_resource"
    android:insetTop="dimension"
    android:insetRight="dimension"
    android:insetBottom="dimension"
    android:insetLeft="dimension" />

根標簽為 <inset>,它的各個屬性含義分別是:

屬性 含義
android:drawable drawable 資源,可引用現有的的 Drawable
android:insetTop、android:insetRight、android:insetBottom、android:insetLeft 內容距離各個邊框的距離

7.2 用法示例

定義

<?xml version="1.0" encoding="utf-8"?>
<inset xmlns:android="http://schemas.android.com/apk/res/android"
       android:drawable="@drawable/shape_dark"
       android:insetBottom="10dp"
       android:insetTop="10dp"
       android:insetLeft="10dp"
       android:insetRight="10dp" />

使用

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@drawable/drawable_inset">

    <TextView
            android:textSize="20sp"
            android:text="TextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextText"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintTop_toTopOf="parent"/>

</androidx.constraintlayout.widget.ConstraintLayout>

效果圖

inset-drawable

8. ScaleDrawable

ScaleDrawable 可以根據 level 值動態地將 Drawable 進行一定比例的縮放。當 level 的取值范圍為 [0, 10000],當 level 為 0 時表示隱藏;當 level 值為 1 時,Drawable 的大小為初始化時的縮放比例,當 level 值為 10000 時,Drawable 大小為 100% 縮放比例。

8.1 語法

定義 ScaleDrawable 的語法如下:

<?xml version="1.0" encoding="utf-8"?>
<scale
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/drawable_resource"
    android:scaleGravity=["top" | "bottom" | "left" | "right" | "center_vertical" |
                          "fill_vertical" | "center_horizontal" | "fill_horizontal" |
                          "center" | "fill" | "clip_vertical" | "clip_horizontal"]
    android:scaleHeight="percentage"
    android:scaleWidth="percentage" />

它的根標簽為 <scale>,它的各個屬性的含義分別是:

android:drawable

drawable 資源,可引用現有的的 Drawable

android:scaleGravity

當圖片尺寸小于 View 時,設置這個屬性值可以對圖片進行定位,可以使用 ”|“ 符號組合使用,所有值的含義分別為:

說明
top 將對象放在其容器頂部,不改變其大小。
bottom 將對象放在其容器底部,不改變其大小。
left 將對象放在其容器左邊緣,不改變其大小。這是默認值。
right 將對象放在其容器右邊緣,不改變其大小。
center_vertical 將對象放在其容器的垂直中心,不改變其大小。
fill_vertical 按需要擴展對象的垂直大小,使其完全適應其容器。
center_horizontal 將對象放在其容器的水平中心,不改變其大小。
fill_horizontal 按需要擴展對象的水平大小,使其完全適應其容器。
center 將對象放在其容器的水平和垂直軸中心,不改變其大小。
fill 按需要擴展對象的垂直大小,使其完全適應其容器。
clip_vertical 可設置為讓子元素的上邊緣和/或下邊緣裁剪至其容器邊界的附加選項。裁剪基于垂直重力:頂部重力裁剪上邊緣,底部重力裁剪下邊緣,任一重力不會同時裁剪兩邊。
clip_horizontal 可設置為讓子元素的左邊和/或右邊裁剪至其容器邊界的附加選項。裁剪基于水平重力:左邊重力裁剪右邊緣,右邊重力裁剪左邊緣,任一重力不會同時裁剪兩邊。

android:scaleHeight

Drawable 高的縮放比例,值越高最終結果越小。

android:scaleWidth

Drawable 寬的縮放比例

8.2 用法示例

這里采用定制一個大小可變的背景為例,展示 ScaleDrawable 的簡單用法。

定義

<?xml version="1.0" encoding="utf-8"?>
<scale xmlns:android="http://schemas.android.com/apk/res/android"
       android:drawable="@drawable/kakarotto"
       android:scaleHeight="80%"
       android:scaleWidth="80%"
       android:scaleGravity="center" />

使用

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
            android:text="Button"
            android:layout_width="200dp"
            android:layout_height="100dp"
            android:background="@drawable/drawable_scale"
            android:id="@+id/button"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"/>

</androidx.constraintlayout.widget.ConstraintLayout>
class ScaleDrawableActivity : AppCompatActivity() {
    lateinit var scaleDrawable: ScaleDrawable
    var curLevel = 0

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_scale_drawable)

        scaleDrawable = findViewById<Button>(R.id.button).background as ScaleDrawable
        scaleDrawable.level = 0

        Observable.interval(200, TimeUnit.MILLISECONDS)
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread()).subscribe {
                scaleDrawable.level = curLevel
                curLevel += 200
                if (curLevel >= 10000) {
                    curLevel = 0
                }
                Log.e("gpj", "level ${curLevel}")
            }
    }
}

效果圖

scale-drawable

9. ClipDrawable

與 ScaleDrawable 原理相同,ClipDrawable 則可以根據 level 值動態地將 Drawable 進行一定比例的剪裁。當 level 的取值范圍為 [0, 10000],當 level 為 0 時表示隱藏;當 level 值為 1 時,Drawable 的大小為初始化時的剪裁比例,當 level 值為 10000 時,Drawable 大小為 100% 剪裁比例。

9.1 語法

定義 ClipDrawable 的語法規則如下:

<?xml version="1.0" encoding="utf-8"?>
<clip xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/drawable_resource"
    android:clipOrientation=["horizontal" | "vertical"]
    android:gravity=["top" | "bottom" | "left" | "right" | "center_vertical" |
                     "fill_vertical" | "center_horizontal" | "fill_horizontal" |
                     "center" | "fill" | "clip_vertical" | "clip_horizontal"] />

它的根標簽為 <clip>,各個屬性的含義分別是:

android:drawable

drawable 資源,可引用現有的的 Drawable

android:clipOrientation

剪裁方向,horizontal 表示水平方向剪裁,vertical 表示豎直方向剪裁

android:gravity

gravity 屬性需要配合 clipOrientation 來使用,可以使用 ”|“ 符號組合使用,所有值的含義分別為:

說明
top 將對象放在其容器頂部,不改變其大小。當 clipOrientation"vertical" 時,在可繪制對象的底部裁剪。
bottom 將對象放在其容器底部,不改變其大小。當 clipOrientation"vertical" 時,在可繪制對象的頂部裁剪。
left 將對象放在其容器左邊緣,不改變其大小。這是默認值。當 clipOrientation"horizontal" 時,在可繪制對象的右邊裁剪。這是默認值。
right 將對象放在其容器右邊緣,不改變其大小。當 clipOrientation"horizontal"時,在可繪制對象的左邊裁剪。
center_vertical 將對象放在其容器的垂直中心,不改變其大小。裁剪行為與重力為 "center" 時相同。
fill_vertical 按需要擴展對象的垂直大小,使其完全適應其容器。當 clipOrientation"vertical" 時,不會進行裁剪,因為可繪制對象會填充垂直空間(除非可繪制對象級別為 0,此時它不可見)。
center_horizontal 將對象放在其容器的水平中心,不改變其大小。裁剪行為與重力為 "center" 時相同。
fill_horizontal 按需要擴展對象的水平大小,使其完全適應其容器。當 clipOrientation"horizontal" 時,不會進行裁剪,因為可繪制對象會填充水平空間(除非可繪制對象級別為 0,此時它不可見)。
center 將對象放在其容器的水平和垂直軸中心,不改變其大小。當 clipOrientation"horizontal" 時,在左邊和右邊裁剪。當 clipOrientation"vertical" 時,在頂部和底部裁剪。
fill 按需要擴展對象的垂直大小,使其完全適應其容器。不會進行裁剪,因為可繪制對象會填充水平和垂直空間(除非可繪制對象級別為 0,此時它不可見)。
clip_vertical 可設置為讓子元素的上邊緣和/或下邊緣裁剪至其容器邊界的附加選項。裁剪基于垂直重力:頂部重力裁剪上邊緣,底部重力裁剪下邊緣,任一重力不會同時裁剪兩邊。
clip_horizontal 可設置為讓子元素的左邊和/或右邊裁剪至其容器邊界的附加選項。裁剪基于水平重力:左邊重力裁剪右邊緣,右邊重力裁剪左邊緣,任一重力不會同時裁剪兩邊。

9.2 用法示例

定義

<?xml version="1.0" encoding="utf-8"?>
<clip xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/drawable_resource"
    android:clipOrientation=["horizontal" | "vertical"]
    android:gravity=["top" | "bottom" | "left" | "right" | "center_vertical" |
                     "fill_vertical" | "center_horizontal" | "fill_horizontal" |
                     "center" | "fill" | "clip_vertical" | "clip_horizontal"] />

使用

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
            android:text="Button"
            android:layout_width="200dp"
            android:layout_height="100dp"
            android:background="@drawable/drawable_clip"
            android:id="@+id/button"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"/>

</androidx.constraintlayout.widget.ConstraintLayout>
class ClipDrawableActivity : AppCompatActivity() {
    lateinit var clipDrawable: ClipDrawable
    var curLevel = 0

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_clip_drawable)

        clipDrawable = findViewById<Button>(R.id.button).background as ClipDrawable
        clipDrawable.level = 0

        Observable.interval(50, TimeUnit.MILLISECONDS)
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread()).subscribe {
                clipDrawable.level = curLevel
                curLevel += 200
                if (curLevel >= 10000) {
                    curLevel = 0
                }
                Log.e("gpj", "level ${curLevel}")
            }
    }
}

效果圖

clip-drawable

10. RotateDrawable

與 ScaleDrawable 和 ClipDrawable 類似,RotateDrawable 可以根據 level 值將 Drawable 進行動態旋轉。

10.1 語法

RotateDrawable 的定義方法如下:

<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/drawable_resource"
    android:visible=["true" | "false"]
    android:fromDegrees="integer" 
    android:toDegrees="integer"
    android:pivotX="percentage"
    android:pivotY="percentage" />

它的根標簽為 <clip>,各個屬性的含義分別是:

屬性 含義
android:drawable drawable 資源,可引用現有的的 Drawable
android:visible 是否可見
android:fromDegrees 旋轉起始角度
android:toDegrees 旋轉結束角度
android:pivotX 旋轉中心位于 X 軸的百分比
android:pivotY 旋轉中心位于 Y 軸的百分比

10.2 用法示例

下面以定義一個可旋轉的背景為例展示 RotateDrawable 的簡單使用

定義

<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
        android:drawable="@drawable/kakarotto"
        android:fromDegrees="0"
        android:toDegrees="180"
        android:pivotX="50%"
        android:pivotY="50%"/>

使用


<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
            android:text="Button"
            android:layout_width="200dp"
            android:layout_height="100dp"
            android:background="@drawable/drawable_rotate"
            android:id="@+id/button"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"/>

</androidx.constraintlayout.widget.ConstraintLayout>
class RotateDrawableActivity : AppCompatActivity() {
    lateinit var rotateDrawable: RotateDrawable
    var curLevel = 0

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_rotate_drawable)

        rotateDrawable = findViewById<Button>(R.id.button).background as RotateDrawable
        rotateDrawable.level = 0

        Observable.interval(50, TimeUnit.MILLISECONDS)
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread()).subscribe {
                rotateDrawable.level = curLevel
                curLevel += 200
                if (curLevel >= 10000) {
                    curLevel = 0
                }
                Log.e("gpj", "level ${curLevel}")
            }
    }
}

效果圖

rotate-drawable

11. TransitionDrawable

有時候我們可能需要在兩個圖片切換的時候增加漸變效果,除了使用動畫之外,這里還可以用 TransitionDrawable 輕松實現。

11.1 語法

<?xml version="1.0" encoding="utf-8"?>
<transition
xmlns:android="http://schemas.android.com/apk/res/android" >
    <item
        android:drawable="@[package:]drawable/drawable_resource"
        android:id="@[+][package:]id/resource_name"
        android:top="dimension"
        android:right="dimension"
        android:bottom="dimension"
        android:left="dimension" />
</transition>

它的根標簽為 <transition>,它可以包含多個 <item> 標簽,每個 item 表示一個 Drawable,item 的屬性含義分別是:

屬性 含義
android:drawable drawable 資源,可引用現有的的 Drawable
android:id iitem 的 id,使用"@+id/name"的形式表示。可通過 View.findViewById() 或者 Activity.findViewById() 方法查找到這個 Drawable
android:top、android:right、android:bottom、android:left Drawable 相對于 View 在各個方向的偏移量

11.2 用法示例

這里定義一個淡入淡出效果的圖片切換效果,展示 TransitionDrawable 的基本使用。

定義

<?xml version="1.0" encoding="utf-8"?>
<transition xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/kakarotto1" />
    <item android:drawable="@drawable/kakarotto2" />
</transition>

使用

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
            android:layout_width="230dp"
            android:layout_height="150dp"
            android:background="@drawable/drawable_transition"
            android:id="@+id/img"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"/>

</androidx.constraintlayout.widget.ConstraintLayout>
class TransitionDrawableActivity : AppCompatActivity() {
    lateinit var disposable: Disposable
    var reverse = false

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_transition_drawable)

        var transitionDrawable = findViewById<ImageView>(R.id.img).background as TransitionDrawable

        Observable.interval(3000, TimeUnit.MILLISECONDS)
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(object : Observer<Long> {
                override fun onSubscribe(d: Disposable) {
                    disposable = d
                }
                override fun onComplete() {
                }

                override fun onNext(t: Long) {
                    if (!reverse) {
                        transitionDrawable.startTransition(3000)
                        reverse = true
                    } else {
                        transitionDrawable.reverseTransition(3000)
                        reverse = false
                    }
                }

                override fun onError(e: Throwable) {
                }

            })
    }

效果圖

transition-drawable

好了,傳統的 Drawable 就介紹完了,但是隨著 Android 版本的更新,Drawable 家族也不斷得有新成員加入進來,讓開發者有了更多的選擇,下一部分文章,我將介紹從 Android 5.0(API 21) 之后加入進來的幾個成員。

附:文章中的 Demo 地址:https://github.com/guanpj/DrawableDemo

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容