要點提煉|開發藝術之Drawable

釐米姑娘發表於2017-12-21

提到Drawable,第一反應肯定是用於存放圖片,實際上,Drawable還可以存放其他資源。那麼本篇就來深入瞭解Drawable,要點如下:

  • Drawable簡介
  • Drawable種類
    • BitmapDrawable
    • NinePatchDrawable
    • ShapeDrawable
    • LayerDrawable
    • StateListDrawable
    • LevelListDrawable
    • TransitionDrawable
    • InsetDrawable
    • ScaleDrawable
    • ClipDrawable
  • 自定義Drawable

1.Drawable簡介

a.Drawable是一種可在Canvas上進行繪製的物件,即可繪製物

b.Drawable類是抽象類,是所有Drawable的基類。繼承關係如下:

要點提煉|開發藝術之Drawable

c.Drawable使用方式:

  • 建立所需Drawable的根節點的xml,再通過**@drawable/xxx**引入佈局中。(常用)
  • Java程式碼:new一個所需Drawable並set相關屬性,最後載入到佈局中。

d.Drawable可通過getIntrinsicWidth()getIntrinsicHeight()獲取其內部寬/高

注意:並不是所有Drawable都有內部寬/高。

  • 圖片所形成的Drawable的內部寬/高就是圖片的寬/高。
  • 顏色所形成的Drawable沒有內部寬/高的概念。

e.Drawable 沒有大小概念

f.Drawable 使用範圍單一

  • 作為ImageView中的影像顯示
  • 作為View的背景

推薦閱讀你真的會用Drawable嗎


2.Drawable種類

下面分別學習各種Drawable:

a.BitmapDrawable

  • 表示一張圖片。
  • 根節點<bitmap>,常用屬性:
bitmap
    |- src="@drawable/res_id"
    |- antialias="[true | false]"
    |- dither="[true | false]"
    |- filter="[true | false]"
    |- tileMode="[disabled | clamp | repeat | mirror]"
    |- gravity="[top | bottom | left | right | center_vertical |
    |            fill_vertical | center_horizontal | fill_horizontal |
    |            center | fill | clip_vertical | clip_horizontal]"
複製程式碼

android:src:圖片的資源id。

android:antialias:是否開啟圖片抗鋸齒。開啟後會讓圖片會更加平滑,同時清楚度降低。

android:dither:是否開啟抖動效果。開啟後讓高質量的圖片的比較低質量的螢幕上不失真。

android:filter:是否開啟過濾效果。當圖片尺寸被拉伸或壓縮時,開啟過濾效果可保持較好的顯示效果。

android:tileMode:平鋪模式。可選值的具體含義:

要點提煉|開發藝術之Drawable

具體效果:

要點提煉|開發藝術之Drawable

android:gravity:若點陣圖比容器小,可以設定點陣圖在容器中的相對位置。可選值的具體含義:

要點提煉|開發藝術之Drawable

  • 使用方法: 以下兩種方法效果相同,圖見之前截圖中所示的mirror情況。

①XML定義:

//在Drawable資料夾中建立bg_tilemode_mirror.xml
<?xml version="1.0" encoding="utf-8"?>
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
    android:dither="true"
    android:src="@mipmap/ic_launcher"
    android:tileMode="mirror"
    >
</bitmap>

//在activity_main.xml中設定為View背景
<View
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/bg_tilemode_mirror"
    />
複製程式碼

②Java程式碼:

//在MainActivity建立BitmapDrawable
Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher);
        BitmapDrawable bitDrawable = new BitmapDrawable(bitmap);
        bitDrawable.setDither(true);
        bitDrawable.setTileModeXY(Shader.TileMode.MIRROR, Shader.TileMode.MIRROR);

//載入到mylayout佈局
        LinearLayout myLayout = (LinearLayout) findViewById(R.id.mylayout);
        myLayout.setBackgroundDrawable(bitDrawable);
複製程式碼

補充例項BitmapDrawable (可控制對齊平鋪的影像)


b.NinePatchDrawable

  • 表示一張.9格式的圖片。
  • 作用:可自動地根據所需的寬/高進行相應的縮放並保證不失真。
  • 製作方法:參考技能篇之製作Nine-Patch圖片
  • 根節點<nine-patch>,常用屬性:
nine-patch
    |- src="@drawable/9_png_resid"
    |- dither="[true | false]"
    |
複製程式碼

android:src:圖片的資源id。

android:dither:是否開啟抖動效果。開啟後讓高質量的圖片的比較低質量的螢幕上不失真。

  • 使用方法: 不建議用Java程式碼建立NinePatchDrawable,建議使用XML定義,程式碼見下。
//在Drawable資料夾中建立bg_nine_patch.xml
<?xml version="1.0" encoding="utf-8"?>
<nine-patch xmlns:android="http://schemas.android.com/apk/res/android"
    android:dither="true"
    android:src="@drawable/box"
    >
</nine-patch>

//在activity_main.xml中設定為EditText背景
<EditText
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/bg_nine_patch"
    />
複製程式碼

c.ShapeDrawable

  • 可表示純色、有漸變效果的基礎幾何圖形(矩形,圓形,線條等)。
  • 根節點<shape>,子節點<corners><gradient><padding><size><solid><stroke>
<?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:topLeftRaidus="integer"
        android:topRightRaidus="integer"
        android:bottomLeftRaidus="integer"
        android:bottomRightRaidus="integer" />
    <gradient
        android:angle="integer"
        android:centerX="integer"
        android:centerY="integer"
        android:centerColor="color"
        android:endColor="color"
        android:gradientRadius="integer"
        android:startColor="color"
        android:type="[linear | radial | sweep]"
        android:useLevel="[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(橫線):
      • 注意:必須通過<stroke>標籤來指定線的寬度和顏色等資訊。
    • ring(圓環):
      • 注意:必須通過<stroke>標籤來指定圓環線的寬度和顏色等資訊。
      • 圓環還有額外幾個屬性,如下圖所示:

要點提煉|開發藝術之Drawable

<corners>:表示shape的四個圓角的角度,只適用於矩形。

  • android:radius:為四個角同事設定相同的角度。優先順序比以下4個屬性要低。
  • android:topLeftRadius:左上角的角度。
  • android:topRightRadius:右上角的角度。
  • android:bottomLeftRadius:左下角的角度。
  • android:bottomRightRadius:右下角的角度。

<gradient>:漸變效果,與<solid>純色填充是互相排斥的。

  • android:angle:漸變的角度。
    • 預設為0。
    • 值必須為45的倍數。
    • 0表示從左到右,90表示從下到上。
  • android:centerX:漸變的中心點的X座標。
  • android:centerY:漸變的中心點的Y座標。
  • android:startColor:漸變的起始色。
  • android:centerColor:漸變的中間色。
  • android:endColor:漸變的結束色。
  • android:gradient:漸變半徑。僅當**android:type="radial"**時有效。
  • android:useLevel:一般為false,當Drawable作StateListDrawable時為true。
  • android:type:漸變的類別。可選值:
    • linear(線性漸變):預設
    • radial(徑向漸變):需要配合android:gradient屬性一起使用。
    • sweep(掃描線漸變)

要點提煉|開發藝術之Drawable

<padding>:與四周空白的距離。

<size>:圖形的固有大小,非最終大小。

  • android:widthandroid:height分別設定shape的寬/高。

<solid>:純色填充。

  • android:color:指定填充的顏色。

<stroke>:描邊。屬性含義:

要點提煉|開發藝術之Drawable

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

    <solid android:color="#a9a6a6" />

    <padding
        android:bottom="7dp"
        android:left="7dp"
        android:right="7dp"
        android:top="7dp" />
    <stroke
        android:width="3dip"
        android:color="#FFFF00" />
    <corners
        android:radius="3dp"/>

</shape>
複製程式碼

效果圖:

要點提煉|開發藝術之Drawable


d.LayerDrawable

  • 表示一種層次化的Drawable集合,通過將不同的Drawable放置在不同的層上面從而達到一種疊加後的效果。
  • 根節點<layer-list>,常用屬性:
layer-list
    |- item
    |    |- drawable="@drawable/drawable_id"
    |    |- id="@+id/xxx_id"
    |    |- top="dimension"
    |    |- left="dimension"
    |    |- right="dimension"
    |    |- bottom="dimension"
    |
複製程式碼

注意:每組 Drawable 由<item>節點進行配置,一個layer-list可包含多個item,服從下面item覆蓋上面item的原則。

android:drawable:所引用的點陣圖資源id,如果為空需要有一個Drawable型別的子節點。

android:id:層id。

android:left:層相對於容器的左邊距。

android:right:層相對於容器的右邊距。

android:top:層相對於容器的上邊距。

android:bottom:層相對於容器的下邊距。

  • 例項:bitmap的簡單疊加
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
        <bitmap
            android:gravity="center"
            android:src="@mipmap/ic_launcher_round"
             />
    </item>
    <item
        android:left="20dp"
        android:top="30dp">
        <bitmap
            android:gravity="center"
            android:src="@mipmap/ic_launcher_round"
             />
    </item>
    <item
        android:left="70dp"
        android:top="80dp">
        <bitmap
            android:gravity="center"
            android:src="@mipmap/ic_launcher_round"
             />
    </item>
</layer-list>
複製程式碼

效果圖:

要點提煉|開發藝術之Drawable

補充例項Drawable子類之——LayerDrawable (圖層疊加)


e.StateListDrawable

  • 表示一個Drawable的集合,每個Drawable對應著View的一種狀態
  • 根節點<selector>,常用屬性:
selector
    |-constantSize="[true | false]"
    |-dither="[true | false]"
    |-variablePadding="[true | false]"
    |- item
    |    |- drawable="@drawable/drawable_id"
    |    |- state_pressed="[true | false]"
    |    |- state_focused="[true | false]"
    |    |- state_selected="[true | false]"
    |    |- state_hovered="[true | false]"
    |    |- state_checked="[true | false]"
    |    |- state_checkable="[true | false]"
    |    |- state_enabled="[true | false]"
    |    |- state_activated="[true | false]"
    |    |- state_window_focused="[true | false]"
    |
複製程式碼

<selector>

  • android:constantSize固有大小是否隨其狀態的改變而改變。
    • 預設為false,表示固有大小隨著狀態的改變而改變。
    • 為true,則表示固有大小是固定值,是內部所有Drawable的固有大小中的最大值
  • android:dither:是否開啟抖動效果。開啟後讓高質量的圖片的比較低質量的螢幕上不失真。
  • android:variblePadding:其padding是否隨狀態的改變而改變。
    • 預設為false,表示padding是固定值,是其內部所有Drawable的padding中的最大值
    • 為true,則表示padding隨著狀態的改變而改變。

<item>

  • android:drawable:所引用的點陣圖資源id。
  • 表示各種狀態的屬性:

要點提煉|開發藝術之Drawable

補充例項探索Android中selector和shape的結合使用


f.LevelListDrawable

  • 表示一個Drawable集合,集合中的每個Drawable都有一個等級的概念。
  • 根節點<level-list>,常用屬性:
level-list
    |- item
    |    |- drawable="@drawable/drawable_id"
    |    |- maxLevel="integer"
    |    |- minlevel="integer"
複製程式碼

android:drawable:引用的點陣圖資源id。

android:maxLevel:對應的最大值,取值範圍為0~10000,預設為0。(常用)

android:minlevel:對應的最小值,取值範圍為0~10000,預設為0。

  • 使用方法:無論是用xml還是程式碼實現,若作為View背景,都需要在Java程式碼中呼叫setLevel() 方法;若作為ImageView前景,需要呼叫setImageLevel()
  • 載入規則:當某item的android:maxLevel 等於 setLevel所設定的數值時就會被載入。若都沒有匹配的則都不顯示。
  • 例項:
//在Drawable資料夾中建立bg_level.xml
<?xml version="1.0" encoding="utf-8"?>
<level-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:maxLevel="1" android:drawable="@drawable/image1" />
    <item android:maxLevel="2" android:drawable="@drawable/image2" />
    <item android:maxLevel="3" android:drawable="@drawable/image3" />
</level-list>

//在activity_main.xml中設定為ImageView背景
 <ImageView
        android:id="@+id/image"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:src="@drawable/bg_level"/>

//在MainActivity呼叫setImageLevel()
   ImageView imageView = (ImageView) findViewById(R.id.image);
        imageView.setImageLevel(2);
複製程式碼

執行結果:ImageView的背景為image2。


g.TransitionDrawable

  • LayerDrawable的子類,實現兩層 Drawable之間的淡入淡出效果。
  • 根節點<transition>,常用屬性和LayerDrawable相同,不再贅述。
transition
    |- item
    |    |- drawable="@drawable/drawable_id"
    |    |- id="@+id/xxx_id"
    |    |- top="dimension"
    |    |- left="dimension"
    |    |- right="dimension"
    |    |- bottom="dimension"
    |
複製程式碼
  • 使用方法:無論是用xml還是程式碼實現,若作為View背景,都需要在Java程式碼中呼叫startTransition() 方法才能啟動兩層間的切換動畫,也可以呼叫reverseTransition() 方法反方向切換。
  • 例項:
//在Drawable資料夾中建立bg_tran.xml
<?xml version="1.0" encoding="utf-8"?>
<transition xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/image1"/>
    <item android:drawable="@drawable/image2"/>
</transition>

//在activity_main.xml中設定為ImageView背景
 <ImageView
        android:id="@+id/image"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:src="@drawable/bg_tran"/>

//在MainActivity呼叫startTransition()
        ImageView imageView = (ImageView) findViewById(R.id.image);
        TransitionDrawable td = (TransitionDrawable) imageView.getDrawable();
        td.startTransition(3000);
複製程式碼

執行結果:ImageView的背景從image1緩緩切換到image2。


h.InsetDrawable

-表示把一個Drawable嵌入到另外一個Drawable的內部,並在四周留一些間距。

與Drawable的padding屬性不同:padding表示的是Drawable的內容與Drawable本身的邊距;而InsetDrawable表示的是兩個Drawable與容器之間的邊距。

  • 根節點<inset>,常用屬性:
layer-list
    |- drawable="@drawable/drawable_id"
    |- visible="[true | false]"
    |- insetTop="dimension"
    |- insetLeft="dimension"
    |- insetRight="dimension"
    |- insetBottom="dimension"
    |
複製程式碼

android:drawable:所引用的點陣圖資源id。

android:visible:是否留有邊距。

android:insetTop:設定距離容器的上邊距。其他同理。

  • 適用場景:當控制元件需要的背景比實際的邊框時。
  • 例項:
//在drawable資料夾下建立
<?xml version="1.0" encoding="utf-8"?>
<inset xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/image"
    android:insetBottom="40dp"
    android:insetLeft="10dp"
    android:insetRight="30dp"
    android:insetTop="20dp"
    android:visible="true">
</inset>
複製程式碼

效果圖:

要點提煉|開發藝術之Drawable


i.ScaleDrawable

  • 表示將Drawable縮放到一定比例。
  • 根節點<scale>,常用屬性:
scale
    |- drawable="@drawable/drawable_id"
    |- scaleGravity="[top | bottom | left | right |
        center_vertical | center_horizontal | center |
        fill_vertical | fill_horizontal | fill |
        clip_vertical | clip_horizontal]"
    |- scaleWidth="percentage"
    |- scaleHeight="percentage"
    |
複製程式碼

android:drawable:所引用的點陣圖資源id。

android:scaleGravity:等同於BitmapDrawable的android:gravity

android:scaleWidth/android:scaleHeight:指定Drawable寬/高的縮放比例,以百分比的形式表示。

  • 使用方法:無論是用xml還是程式碼實現,若作為View背景,都需要在Java程式碼中呼叫setLevel()
  • 方法控制Drawable等級。
    • level取值範圍為0~10000。
    • 預設值為0:表示不可見;1~10000:表示可見。
  • 例項:將一張圖片縮小為原來的30%,程式碼為:
//在drawable資料夾下建立bg_scale.xml
<?xml version="1.0" encoding="utf-8"?>
<scale xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/drawable_test"
    android:scaleGravity="center"
    android:scaleHeight="70%"
    android:scaleWidth="70%"/>

//在activity_main.xml中設定為ImageView背景
 <ImageView
        android:id="@+id/image"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:src="@drawable/bg_scale"/>

//在MainActivity呼叫setLevel()
         ImageView imageView = (ImageView) findViewById(R.id.image);
        ScaleDrawable scaleDrawable = (ScaleDrawable) imageView.getDrawable();
        scaleDrawable.setLevel(1);
複製程式碼

j.ClipDrawable

  • 表示裁剪一個Drawable。
  • 根節點<clip>,常用屬性:
scale
    |- drawable="@drawable/drawable_id"
    |- gravity="[top | bottom | left | right |
        center_vertical | center_horizontal | center |
        fill_vertical | fill_horizontal | fill |
        clip_vertical | clip_horizontal]"
    |- clipOrientation="[vertical | horizontal]"
    |
複製程式碼

android:drawable:所引用的點陣圖資源id。

android:gravity:表示對齊方式,需要和clipOrientation一起發揮作用。可選值含義:

要點提煉|開發藝術之Drawable

android:clipOrientation:表示裁剪方向,可選值有水平和豎直。

  • 使用方法:無論是用xml還是程式碼實現,若作為View背景,都需要在Java程式碼中呼叫setLevel()
  • 方法控制可見區大小。
    • level取值範圍為0~10000。
    • 0:表示完全裁剪,即不可見;10000:表示不裁剪。
    • level越大可見區越大。

補充例項結合Timer做一個簡單的裁剪動畫,當然這個思路也可以應用到之前學習的縮放ScaleDrawable、LevelListDrawable中來實現一個小動畫。

以上大多是使用xml來定義一個Drawable,那麼使用java程式碼建立方法可參考文章:Android Drawable完全解析:Drawable子類用法總結,同時這篇文章還講解不少Drawable,比如ColorDrawable(1)、AnimationDrawable(2)、RotateDrawable(9)、GradientDrawable(12)、RippleDrawable(17)、RoundedBitmapDrawable(15)、DrawerArrowDrawable(11)、AnimatedStateListDrawable(5),感興趣可以瞭解學習~


3.自定義Drawable

a.Drawable的工作原理的核心是draw():系統呼叫Drawable的draw()來繪製View的背景或ImageView的影像。

b.通常沒有必要去自定義Drawable,因為無法在XML中使用自定義Drawable,這就降低了其使用範圍。

c.建立自定義Drawable,必須重寫其draw()setAlpha()setColorFilter()getOpacity() 等方法。以下為自定義Drawable示例:

//自定義Drawable
public class CustomDrawable extends Drawable {
   
    private Paint mPaint;

    public CustomDrawable(int color) {
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setColor(color);
    }

    @Override
    public void draw(Canvas canvas) {
        final Rect rect =  getBounds();
        float cx = rect.exactCenterX();
        float cy = rect.exactCenterY();
        canvas.drawCircle(cx, cy, Math.min(cx, cy), mPaint);
    }

    @Override
    public void setAlpha(int alpha) {
        mPaint.setAlpha(alpha);
        invalidateSelf();
    }

    @Override
    public void setColorFilter(ColorFilter colorFilter) {
        mPaint.setColorFilter(colorFilter);
        invalidateSelf();
    }

    @Override
    public int getOpacity() {
        return PixelFormat.TRANSLUCENT;
    }
}
複製程式碼

d.當自定義的Drawable有固有大小時,最好重寫getIntrinsicWidth()getIntrinsicHeight(),因為它會影響到View的wrap_content佈局。

注意:Drawable的內部大小不等於Drawable的實際大小,後者可通過**getBounds()**獲得,一般它和View的尺寸相同。

推薦閱讀Drawable繪製過程原始碼分析和自定義Drawable實現動畫


希望這篇文章對你有幫助~

相關文章