接上文,自從 Android 5.0 發佈開始,能夠看出 Google 愈來愈重視 Android 系統的 UI 設計風格了,最爲明顯的就是提出了 Material Design 設計語言。其中包含了不少 UI 設計的新特性,能夠說爲 Android 系統注入了新鮮的血液。如下文章介紹的都是 Android 5.0 之後引入的 Drawable,一塊兒來看看都有什麼吧!php
記得谷歌剛發佈 Android 5.0 系統時,用 API 21 的鏡像啓動模擬器後看到一個很明顯的變化就是,不少按鈕上都加了點擊波紋效果,而這些波紋效果就是用 RippleDrawable 來實現的。html
<ripple xmlns:android="http://schemas.android.com/apk/res/android" android:color="color" android:radius="dimension">
<item android:id="@[package:]id/resource_name" android:drawable="@[package:]drawable/drawable_resource" 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"] />
</ripple>
複製代碼
RippleDrawable 頂層標籤爲 <ripple>,它的兩個屬性的含義分別是:android
android:colorgit
ripple 效果的顏色github
android:radiusapp
ripple 徹底擴散開始的半徑。默認會根據容器大小來計算。eclipse
除此以外,它能夠包含多個 <item> 標籤,每一個 item 表示一個 Drawable,item 的屬性含義分別是:iphone
屬性 | 含義 |
---|---|
android:drawable | drawable 資源,可引用現有的的 Drawable |
android:id | 若是 item 的 id 設置成 @android:id/mask,在初始化時這個 item 不會被繪製,只會在點擊的時候以蒙層的形式限制波紋的範圍在這個 item 以內 |
android:top、android:right、android:bottom、android:left | Drawable 相對於 View 在各個方向的偏移量 |
android:gravity | 尺寸小於容器尺寸時在容器中的擺放位置 |
定義ide
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android" android:color="@color/colorAccent" android:radius="90dp">
<item android:id="@android:id/mask" android:drawable="@android:color/white" />
<item android:drawable="@color/colorPrimary" />
</ripple>
複製代碼
使用工具
<?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="I' m a Button" android:layout_width="200dp" android:layout_height="50dp" android:background="@drawable/drawable_ripple" 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>
複製代碼
效果圖
從 API 21(Android 5.0) 開始,Google 開始支持使用 Vector,VectorDrawable 應運而生。相比於普通的 Drawable,它具備如下優勢:
定義一個 VectorDrawable 的語法以下:
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:name="string" android:width="dimension" android:height="dimension" android:viewportHeight="float" android:viewportWidth="float" android:tint="color" android:tintMode=["add" | "multiply" | "src_top" | "src_in" | "src_over" | "screen"] android:autoMirrored=["true" | "false"] android:alpha="integer" />
<group android:name="string" android:pivotX="float" android:pivotY="float" android:rotation="integer" android:translationX="float" android:translationY="float" android:scaleX="float" android:scaleY="float">
<path android:name="string" android:pathData="path" android:fillColor="color" android:fillAlpha="integer" android:strokeColor="color" android:strokeWidth="integer" android:strokeAlpha="integer" android:trimPathStart="float" android:trimPathEnd="float" android:trimPathOffset="float" android:strokeLineCap=["butt" | "round" | "square"] android:strokeLineJoin=["round" | "bevel" | "miter"] android:strokeMiterLimit="integer" android:fillType=["nonZero" | "evenOdd"] />
<clip-path android:name="string" android:pathData="path" />
</group>
</vector>
複製代碼
VectorDrawable 的根標籤爲 <vector>,老規矩,先看看它的子元素屬性和含義:
屬性 | 含義 |
---|---|
android:name | drawable 的名字 |
android:width、android:height | 內部(intrinsic)寬度和高度。通常使用 dp |
android:viewportWidth、android:viewportHeight | 矢量圖視圖的寬度和高度。視圖就是矢量圖 path 路徑數據所繪製的虛擬畫布 |
android:tint | 給矢量圖着色 |
android:tintMode | 着色模式。共支持六種模式,默認爲「src_in",詳情請參考 PorterDuff.Mode |
android:autoMirrored | 自動翻轉 |
android:alpha | 圖片透明度。取值範圍爲 [0, 255]VectorDrawble 支持 |
一張矢量圖能夠由多個 path 組成,<group> 標籤能夠對多個 path 進行分組,標籤內的屬性值對組內全部 path 都生效,<group> 標籤的各個屬性及其含義分別爲以下:
屬性 | 含義 |
---|---|
android:name | 分組的名字 |
android:pivotX、android:pivotY | 縮放和旋轉時候的 X 和 Y 的基準點。該值是相對於 vector 的 viewport 值來指定的 |
android:translationX、android:translationY | X 軸和 Y 軸方向的平移位移。該值一樣是相對於 viewport 值來指定的 |
android:rotation | 旋轉角度 |
android:scaleX、android:scaleY | 分別在 X 軸和 Y 軸方向的縮放比例 |
接下來就是 <path> 標籤了,<path> 標籤訂了的矢量圖的繪製方法,包括繪製路徑、顏色、邊框樣式等屬性,它的全部屬性及其含義以下:
屬性 | 含義 |
---|---|
android:pathData | path 指令。指令格式參考:路徑 |
android:fillColor | path 填充顏色。通常爲純色,API 24 開始支持 Gradient 漸變色,詳情請參考:vectordrawable-gradients-part1 和 vectordrawable-gradients-part1-2/ |
android:fillAlpha | X 軸和 Y 軸方向的平移位移。該值一樣是相對於 viewport 值來指定的 |
android:fillType | path 的填充模式。默認是"noneZero",詳情參考:非零環繞數規則和奇-偶規則 和 Android 關於Path的FillType |
android:strokeWidth | path 邊框寬度 |
android:strokeColor | path 邊框顏色 |
android:strokeAlpha | path 邊框透明度 |
android:strokeLineCap | path 線頭的形狀。buff 平頭、round 圓頭和 square 方頭。默認爲 buff |
android:strokeLineJoin | path 拐角的形狀。miter 尖角、 bevel 平角和 round 圓角。默認爲 miter |
android:strokeMiterLimit | 設置拐角的形狀爲 miter 時,拐角的延長線的最大值。當小到必定程度時,miter 效果將會失效從而變成 bevel 效果 |
android:trimPathStart | 從 path 起始位置截斷路徑的比率。取值範圍爲[0, 1] |
android:trimPathEnd | 從 path 結束位置截斷路徑的比率。取值範圍爲[0, 1] |
android:trimPathOffset | path 截取起點的偏移量。取值範圍爲[0, 1],因爲 path 的起點和終點能夠看做的首尾相連的,所以起點和終點是一塊兒發生偏移的 |
定義
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="200dp" android:height="200dp" android:viewportHeight="600" android:viewportWidth="600">
<path android:strokeWidth="15" android:strokeColor="#000000" android:strokeLineCap="butt" android:pathData="M5 10 l200 0"/>
<!--路徑起點/終點日後偏移 10%,重新起點開始日後截斷至 50% 的路徑-->
<path android:strokeWidth="15" android:strokeColor="#000000" android:strokeLineCap="butt" android:trimPathStart="0.5" android:trimPathOffset="0.1" android:pathData="M5 80 l200 0"/>
<!--路徑起點/終點日後偏移 50%,重新終點往前截斷至 70% 部分-->
<!--也能夠理解爲重新起點日後截取至 70% 部分-->
<path android:strokeWidth="15" android:strokeColor="#000000" android:strokeLineCap="butt" android:trimPathEnd="0.7" android:trimPathOffset="0.5" android:pathData="M5 150 l200 0"/>
<path android:strokeWidth="15" android:strokeColor="#000000" android:strokeLineCap="square" android:strokeLineJoin="round" android:pathData="M5 230 l200 0 l-100 30"/>
<path android:strokeWidth="15" android:strokeColor="#000000" android:strokeLineCap="butt" android:strokeLineJoin="miter" android:pathData="M5 290 l200 0 l-100 30"/>
<path android:strokeWidth="15" android:strokeColor="#000000" android:strokeLineCap="round" android:strokeLineJoin="miter" android:strokeMiterLimit="7" android:pathData="M5 350 l200 0 l-100 30"/>
<group android:name="name" android:translateX="10" android:translateY="10" android:rotation="90" android:pivotX="300" android:pivotY="300">
<path android:name="noneZero" android:strokeWidth="2" android:strokeColor="#ffffff" android:fillColor="#3C8FC1" android:pathData="M20 120 a100 100 0 1 1 200 0 a100 100 0 1 1 -200 0 M40 120 a80 80 0 1 1 160 0 a80 80 0 1 1 -160 0"/>
<path android:name="evenOdd" android:strokeWidth="5" android:strokeColor="#ffffff" android:strokeAlpha="128" android:fillColor="#3C8FC1" android:fillType="evenOdd" android:pathData="M260 120 a100 100 0 1 1 200 0 a100 100 0 1 1 -200 0 M280 120 a80 80 0 1 1 160 0 a80 80 0 1 1 -160 0"/>
</group>
</vector>
複製代碼
使用
<?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">
<androidx.appcompat.widget.AppCompatImageView android:layout_width="match_parent" android:layout_height="match_parent" app:srcCompat="@drawable/drawable_vector" app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
複製代碼
效果圖
因爲 VectorDrawable 是從 Android 5.0 以後引入的,若是想要在舊設備上運行,就必需要進行兼容性設置,因爲篇幅有限,能夠參考Android Vector曲折的兼容之路,這裏再也不贅述。
當你覺得 VectorDrawable 除了替代傳統圖標別無它用那你就實在 too young 了,與 VetorDrawable 一塊兒誕生的還有 AnimatedVectorDrawable。還記得 VectorDrawable 中的 group
和 path
有個 name 屬性嗎?這時候它們就派上用場了,AnimatedVectorDrawable 能夠經過 name 屬性爲 group 和 path 綁定一個屬性動畫,讓這些 path 能夠動起來,作出比較炫酷的動畫效果。在 API 25 以前,由於渲染是在 UI 線程進行的的,所以性能不是很好,加上兼容性問題,目前使用得並很少。自從 API 25 以後,Google 將 AnimatedVectorDrawable 的渲染放在了 RenderThered 中執行,這顯然減輕了很多 UI 線程的壓力,Google 官方描述是:
This means animations in AnimatedVectorDrawable can remain smooth even when there is heavy workload on the UI thread.
所以,若是運行在新設備上,你們大可沒必要操心性能問題了。
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" android:drawable="@[package:]drawable/drawable_resource" >
<target android:name="string" android:animation="@[package:]animator/animator_resource" />
</animated-vector>
複製代碼
AnimatedVectorDrawable 的根標籤爲 <animated-vector>,android:drawable
屬性用來指定 VectorDrawable 資源,<target> 標籤將它的子元素 name
屬性指定的 VectorDrawable 中須要添加動畫效果的 path 或者 group 與 animation
屬性中的 animator 資源綁定起來。animation 資源一樣能夠經過標籤訂義或者指向現有的 animator 文件。
下面之前面文章中提到的 Demo 中的一個效果爲例,展現一個 AnimatedVectorDrawable 的基本用法。
定義
須要添加動畫效果的 VectorDrawable,一共有兩個 path:
<?xml version="1.0" encoding="utf-8"?>
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt" android:drawable="@drawable/ic_arrow">
<target android:name="left">
<aapt:attr name="android:animation">
<objectAnimator android:duration="1000" android:interpolator="@android:interpolator/anticipate_overshoot" android:propertyName="translateX" android:repeatCount="infinite" android:repeatMode="reverse" android:valueFrom="0" android:valueTo="-10" android:valueType="floatType"/>
</aapt:attr>
</target>
<target android:name="right">
<aapt:attr name="android:animation">
<objectAnimator android:duration="1000" android:interpolator="@android:interpolator/anticipate_overshoot" android:propertyName="translateX" android:repeatCount="infinite" android:repeatMode="reverse" android:valueFrom="0" android:valueTo="10" android:valueType="floatType"/>
</aapt:attr>
</target>
</animated-vector>
複製代碼
使用
在代碼中監聽和控制動畫:
class AnimatedVectorDrawableActivity : AppCompatActivity() {
private lateinit var animatable2Compat: Animatable2Compat
@RequiresApi(Build.VERSION_CODES.M)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_animated_vector_drawable)
var image = findViewById<ImageView>(R.id.image)
var animatedVectorDrawableCompat = AnimatedVectorDrawableCompat.create(this, R.drawable.drawable_animated_vector)
image.setImageDrawable(animatedVectorDrawableCompat)
animatable2Compat = image.drawable as Animatable2Compat
animatable2Compat.registerAnimationCallback(object : Animatable2Compat.AnimationCallback() {
override fun onAnimationStart(drawable: Drawable?) {
Log.e("gpj", "onAnimationStart")
}
override fun onAnimationEnd(drawable: Drawable?) {
Log.e("gpj", "onAnimationEnd")
}
})
animatable2Compat.start()
}
override fun onDestroy() {
super.onDestroy()
animatable2Compat.stop()
}
}
複製代碼
效果圖
前面提到的 StateListDrawable 只能使用靜態的資源在不一樣的狀態之間進行切換,一樣的,在 Android 5.0 以後,狀態列表裏可使用動態資源了,它就是 AnimatedStateListDrawable。
<?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:id="@[+][package:]id/resource_name" 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"] />
<transition android:drawable="@[package:]drawable/drawable_resource" android:fromId="@[package:]id/item_name" android:toId="@[package:]id/item_name" />
</selector>
複製代碼
能夠發現,相對於 StateListDrawable,這裏只多出一個 <transition> 標籤,它的各個屬性含義分別是:
android:drawable
定義或者指向一個 AnimatedVectorDrawable 資源。不難理解,這裏須要指定狀態變化的動畫。
android:fromId 和 android:toId
分別指定狀態變化的起始和結束 item 的 id。詳情請看示例。
定義
<?xml version="1.0" encoding="utf-8"?>
<animated-selector xmlns:android="http://schemas.android.com/apk/res/android" android:visible="true" android:dither="true">
<!--勾選狀態-->
<item android:id="@+id/checked" android:drawable="@drawable/ic_checked" android:state_checked="true" />
<!--未勾選狀態-->
<item android:id="@+id/unchecked" android:drawable="@drawable/ic_unchecked" />
<!--未勾選狀態過分到勾選狀態-->
<transition android:drawable="@drawable/toggle_unchecked_checked" android:fromId="@id/unchecked" android:toId="@id/checked" />
<!--勾選狀態過分到未勾選狀態-->
<transition android:drawable="@drawable/toggle_checked_unchecked" android:fromId="@id/checked" android:toId="@id/unchecked" />
</animated-selector>
複製代碼
正常狀態到勾選狀態過分動畫:
<?xml version="1.0" encoding="utf-8"?>
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt" android:drawable="@drawable/ic_checked">
<!--打勾 path 動畫-->
<target android:name="tick">
<aapt:attr name="android:animation">
<objectAnimator android:duration="200" android:interpolator="@android:interpolator/accelerate_cubic" android:propertyName="trimPathEnd" android:valueFrom="0" android:valueTo="1" android:valueType="floatType" />
</aapt:attr>
</target>
<!--圓圈 path 動畫-->
<target android:name="circle">
<aapt:attr name="android:animation">
<objectAnimator android:duration="500" android:interpolator="@android:interpolator/accelerate_decelerate" android:propertyName="strokeColor" android:valueFrom="#A0A0A0" android:valueTo="#1E9618" android:valueType="intType" />
</aapt:attr>
</target>
</animated-vector>
複製代碼
勾選狀態到未勾選狀態過分動畫:
<?xml version="1.0" encoding="utf-8"?>
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt" android:drawable="@drawable/ic_checked">
<!--打勾 path 動畫-->
<target android:name="tick">
<aapt:attr name="android:animation">
<objectAnimator android:duration="100" android:interpolator="@android:interpolator/decelerate_cubic" android:propertyName="trimPathEnd" android:valueFrom="1" android:valueTo="0" android:valueType="floatType" />
</aapt:attr>
</target>
<!--圓圈 path 動畫-->
<target android:name="circle">
<aapt:attr name="android:animation">
<objectAnimator android:duration="500" android:interpolator="@android:interpolator/accelerate_decelerate" android:propertyName="strokeColor" android:valueFrom="#1E9618" android:valueTo="#A0A0A0" android:valueType="intType" />
</aapt:attr>
</target>
</animated-vector>
複製代碼
使用
<?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">
<androidx.appcompat.widget.AppCompatCheckBox android:layout_width="wrap_content" android:layout_height="50dp" android:button="@drawable/drawable_animated_state_list" android:paddingEnd="8dp" android:paddingLeft="8dp" android:paddingRight="8dp" android:paddingStart="8dp" android:text="I'm a CheckBox" android:textColor="#ff00ff" app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
複製代碼
效果圖
你們都知道,在 Android 8.0 以前,若是咱們要在設備上顯示 GIF 圖,通常都要藉助一些第三方的庫(如 Glide)來實現。記得 Android 8.0 剛發佈的時候,提到一個 ImageDecoder 類,它除了能夠解析 PNG、JEPG 類型的文件以外,還能夠解析 WebP 和 GIF,而 GIF 文件解析出來的正是 AnimatedImageDrawable。因爲目前資源有限,Google 也沒有提供使用 XML 來定義 AnimatedImageDrawable 的例子,這裏就在 Kotlin 代碼裏面介紹 AnimatedImageDrawable 和 ImageDecoder 的簡單使用。
使用
@RequiresApi(Build.VERSION_CODES.P)
class AnimatedImageDrawableActivity : AppCompatActivity() {
var mAnimatedImageDrawable: AnimatedImageDrawable? = null
private val cacheAsset: CacheAsset by lazy {
CacheAsset(this)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_animated_image_drawable)
button.setOnClickListener {
ImageDecoder.createSource(cacheAsset.file("gif_example.gif")).also { source ->
ImageDecoder.decodeDrawable(source).also { drawable ->
image.setImageDrawable(drawable)
if(drawable is AnimatedImageDrawable) {
mAnimatedImageDrawable = drawable
drawable.start()
}
}
}
}
}
override fun onDestroy() {
super.onDestroy()
mAnimatedImageDrawable?.run { stop() }
}
}
複製代碼
效果圖
到此,常見的 Drawable 的用法已經所有講完了,若是要加深理解,建議把 Demo 跑一遍。
附:文章中的 Demo 地址:github.com/guanpj/Draw…