進階之路 | 奇妙的Drawable之旅

前言

本文已經收錄到個人Github我的博客,歡迎大佬們光臨寒舍:php

個人GIthub博客java

學習清單:

  • Drawable簡介
  • Drawable分類
  • 自定義Drawable

一.爲何要學習Drawable?

Drawable種類繁多,它們都表示一種圖像的概念,可是它們不全是圖片。在實際開發中,Drawable常常被用來做爲View的背景使用。android

Drawable能夠方便咱們作出一些特殊的UI效果,這一點在UI相關的開發工做中極爲重要。面對UI設計師設計出來的各式各樣的按鈕點擊效果,動態效果,漸變效果,好看是好看,咱們程序員每每會咆哮:"你舒服了,咱們呢!!"別慌,學好Drawable,你會對各類效果信手拈來,瞭然於胸,成竹在胸!!git

並且,Drawable在開發中也有本身的優勢:程序員

  • 使用簡單,成本低於自定義Viewgithub

  • 非圖片類型的Drawable佔用空間較小,對於減小APK大小有所裨益canvas

綜上,掌握好Drawable,走遍天下也不怕!(jia de)swift

二.核心知識點概括

2.1 Drawable簡介

Q1:Drawable類是抽象類,是全部Drawable的基類。繼承關係以下:ruby

Drawable繼承關係

Q2:Drawable使用方式:bash

  • 建立所需Drawable的根節點的xml,再經過**@drawable/xxx**引入佈局中。(經常使用)
  • 普通控件(非ImageView)是設置background
  • ImageView是設置src
  • Java代碼:new一個所需Drawable並set相關屬性,最後加載到佈局中。

Q3:內部寬高了解多少

  • 獲取方式:getIntrinsicWidth()getIntrinsicHeight()

注意

  • 並非全部Drawable都有內部寬/高
  • 圖片所造成的Drawable的內部寬/高就是圖片的寬/高。
  • 顏色所造成的Drawable默認狀況下沒有內部寬/高的概念(除非指定size)。
  • 內部寬高不等於大小,Drawable沒有大小概念
  • Drawable被用做background的時候,自動被拉伸到View同等大小;Drawable被用做src的時候,存放原圖大小比例,不會被拉伸

2.2 Drawable種類

2.2.1 BitmapDrawable

  • 表示一張圖片
  • 經常使用屬性:
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]" 複製代碼
  • src:圖片的資源id
  • antialias:是否開啓圖片抗鋸齒。開啓後會讓圖片會更加平滑,同時清晰度下降不多,應該開啓。
  • dither:是否開啓抖動效果。開啓後讓高質量的圖片的在低質量的屏幕上顯示不失真,應該開啓。
  • filter:是否開啓過濾效果。當圖片尺寸被拉伸或壓縮時,開啓後可保持較好的顯示效果,應該開啓
  • tileMode:平鋪模式。開啓後gravity會失效;可選值的具體含義:
可選項 含義
disable 默認值,關閉平鋪模式
mirror 在水平和垂直方向的鏡面投影效果
repeat 在水平和垂直方向的平鋪效果
clamp 圖片四周像素會擴散到其餘區域

具體效果:

tileMode效果

  • gravity:若位圖比容器小,能夠設置位圖在容器中的相對位置。可選值的具體含義:

gravity屬性

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

a.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" />
複製代碼

b.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);
複製代碼

2.2.2 NinePatchDrawable

  • 表示一張.9格式的圖片
  • 做用:可自動地根據所需的寬/高進行相應的縮放並保證不失真。
  • 製做方法及原理:能夠參考博客:9patch / NinePatch 詳解及使用
  • 經常使用屬性:和本文2.2.1 BitmapDrawable同樣
  • 使用方法: 不建議用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" />
複製代碼

2.2.3 ShapeDrawable

  • 可表示純色、有漸變效果的基礎幾何圖形(矩形,圓形,線條等)
  • 根節點shape,子節點cornersgradientpaddingsizesolidstroke
<?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" />
複製代碼

接下來分別解釋各個節點下屬性含義:

Q1 shape:圖形的形狀,可選值有:

  • rectangle(矩形):爲默認值。
  • oval(橢圓)
  • line(橫線):

注意:必須經過stroke標籤來指定線的寬度和顏色等信息。

  • ring(圓環):
  • 注意:必須經過stroke標籤來指定圓環線的寬度和顏色等信息。
  • 圓環還有額外幾個屬性,以下圖所示:

ring額外屬性

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

  • radius:爲四個角同時設定相同的角度。優先級比如下4個屬性要低。
  • topLeftRadius:左上角的角度
  • topRightRadius:右上角的角度
  • bottomLeftRadius:左下角的角度
  • bottomRightRadius:右下角的角度

Q3:gradient:漸變效果,與solid純色填充是互斥的。

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

漸變的樣式

  • padding:與四周空白的距離。
  • size:圖形的固有大小,非最終大小

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

  • solid:純色填充。

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

  • stroke:描邊。屬性含義:
stroke的屬性 做用
width 描邊的寬度
color 描邊的顏色
dashWidth 虛線的寬度
dashGap 虛線的空隙的間隔

2.2.4 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的原則。

A.drawable:所引用的位圖資源id,若是爲空須要有一個Drawable類型的子節點。

B.id:層id。

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

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

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

F.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>
複製代碼

bitmap的簡單疊加

2.2.5 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]" | 複製代碼

A.selector:

  • constantSize固有大小是否不變。
  • 默認爲false,表示固有大小隨着狀態的改變而改變。

  • 設爲true,則表示固有大小是固定值,是內部全部Drawable的固有大小中的最大值

  • dither:是否開啓抖動效果。開啓後讓高質量的圖片的比較低質量的屏幕上不失真。默認開啓。
  • variblePadding:其padding是否隨狀態的改變而改變。
  • 默認爲false,表示padding是固定值,是其內部全部Drawable的padding中的最大值
  • 爲true,則表示padding隨着狀態的改變而改變。

B.item:

  • drawable:所引用的位圖資源id。
  • 表示各類狀態的屬性:
狀態 含義
state_pressed(經常使用) 按下狀態
state_focused 已經獲取了焦點
state_selected 選擇了View
state_checked 適用於checkBox
state_enabled 表示可用狀態
  • 實例:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" android:constantSize="false" android:dither="true" android:variablePadding="false">
    <item android:drawable="@drawable/red_bg" android:state_pressed="false" />
    <item android:drawable="@color/black_bg" android:state_pressed="true" />
</selector>
複製代碼

2.2.6 LevelListDrawable

  • 表示一個Drawable集合,集合中的每一個Drawable都有一個等級的概念。經過設置不一樣的等級來切換具體的Drawable
  • 根節點level-list,經常使用屬性:
level-list
    |- item
    |    |- drawable="@drawable/drawable_id"
    |    |- maxLevel="integer"
    |    |- minlevel="integer"
複製代碼
  • drawable:引用的位圖資源id。
  • maxLevel:對應的最大值,取值範圍爲0~10000,默認爲0。(經常使用)
  • 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。

2.2.7 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的src
 <ImageView android:id="@+id/image" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@drawable/bg_tran" android:src="@drawable/bg_tran"/>

//在MainActivity調用startTransition()
        ImageView imageView = (ImageView) findViewById(R.id.image);
        TransitionDrawable td = (TransitionDrawable) imageView.getDrawable();
	    TransitionDrawable td2 = (TransitionDrawable) imageView.getBackground();
        td.startTransition(3000);
        td2.startTransition(3000);
複製代碼

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

2.2.8 InsetDrawable

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

與Drawable的padding屬性不一樣:padding表示的是Drawable的內容與Drawable自己的邊距;而InsetDrawable表示的是Drawable與容器之間的邊距。

  • 根節點inset,經常使用屬性:
inset
    |- drawable="@drawable/drawable_id"
    |- visible="[true | false]"
    |- insetTop="dimension"
    |- insetLeft="dimension"
    |- insetRight="dimension"
    |- insetBottom="dimension"
    |
複製代碼
  • drawable:所引用的位圖資源id。
  • visible:是否留有邊距。(經測試,發現設置true/false效果同樣....)
  • 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>
複製代碼

效果圖:

InsetDrawable效果

2.2.9 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"
    |
複製代碼
  • drawable:所引用的位圖資源id。
  • scaleGravity:等同於BitmapDrawable的android:gravity
  • 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);
複製代碼

2.2.10 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]"
    |
複製代碼
  • drawable:所引用的位圖資源id。
  • gravity:表示對齊方式,須要和clipOrientation一塊兒發揮做用。可選值含義:

ClipDrawable的gravity

  • clipOrientation:表示裁剪方向,可選值有水平和豎直。
  • 使用方法:不管是用xml仍是代碼實現,若做爲View背景,都須要在Java代碼中調用setLevel()方法控制可見區大小。
  • level取值範圍爲0~10000。
  • 0:表示徹底裁剪,即不可見;10000:表示不裁剪。
  • level越大可見區越大。

2.3 自定義Drawable

  • 工做原理的核心是draw():系統調用Drawable的draw()來繪製View的背景或ImageView的圖像。
  • 一般沒有必要去自定義Drawable,由於沒法在XML中使用自定義Drawable,這就下降了其使用範圍。
  • 建立自定義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;
    }
}
複製代碼
  • 當自定義的Drawable有固有大小時(Drawable是圖片),最好重寫getIntrinsicWidth()getIntrinsicHeight(),由於它會影響到View的wrap_content佈局。

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

三.知識拓展

恭喜你!已經看到這裏了,相信你已經對Drawable有必定的看法了!本文只是介紹了Drawable中經常使用的類型,並無徹底列出全部Drawable的類型,並且只是介紹了XML的建立。

可是,筆者也給好奇心強的讀者準備了一些乾貨(一篇博客),裏面詳細介紹了Drawable的各類類型,各類建立方法,總的來講仍是寫得比較不錯的。指路:Drawable子類用法總結.

下面展現下本文還沒來得及贅述的Drawable:

AnimationDrawable

RippleDrawable

RoundedBitmapDrawable

DrawerArrowDrawable


若是文章對您有一點幫助的話,但願您能點一下贊,您的點贊,是我前進的動力

本文參考連接:

相關文章
相關標籤/搜索