Drawable
1.BitmapDrawable
位圖圖像。Android支持三種格式的位圖文件:.png(首選)、.jpg(可接受)、.gif(不建議)。我們可以直接使用文件名作為資源 ID 來(lái)引用位圖文件,也可以在 XML 文件中創(chuàng)建別名資源 ID,這就叫做 XML位圖。
<1> 使用xml位圖
<?xml version="1.0" encoding="utf-8"?>
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
android:antialias="true"
android:gravity="fill"
android:src="@drawable/ic_launcher_background"
android:tileMode="disabled"/>
關(guān)于<bitmap> 屬性:
android:src:引用可繪制對(duì)象資源,必備。
-
android:tileMode:定義平鋪模式。當(dāng)平鋪模式啟用時(shí),位圖會(huì)重復(fù),且注意:一旦平鋪模式啟用, android:gravity 屬性就將會(huì)被忽略。定義平鋪屬性的值必須是以下值之一:
- disabled:不平鋪位圖,默認(rèn)值。
- clamp:當(dāng)著色器繪制范圍超出其原邊界時(shí)復(fù)制邊緣顏色。
- repeat:水平和垂直重復(fù)著色器的圖像。
- mirror:水平和垂直重復(fù)著色器的圖像,交替鏡像圖像以使相鄰圖像始終相接。
注意:在平鋪模式啟用時(shí)android:gravity屬性將被忽略。
-
android:gravity:定義位圖的重力屬性,當(dāng)位圖小于容器時(shí),可繪制對(duì)象在其容器中放置的位置。
top:將對(duì)象放在其容器頂部,不改變其大小。
bott****om:將對(duì)象放在其容器底部,不改變其大小。
left:將對(duì)象放在其容器左邊緣,不改變其大小。
right:將對(duì)象放在其容器右邊緣,不改變其大小。
center_vertical:將對(duì)象放在其容器的垂直中心,不改變其大小。
fill_vertical:按需要擴(kuò)展對(duì)象的垂直大小,使其完全適應(yīng)其容器。
center_horizontal:將對(duì)象放在其容器的水平中心,不改變其大小。
fill_horizontal:按需要擴(kuò)展對(duì)象的水平大小,使其完全適應(yīng)其容器。
center:將對(duì)象放在其容器的水平和垂直軸中心,不改變其大小。
fill:按需要擴(kuò)展對(duì)象的垂直大小,使其完全適應(yīng)其容器。這是默認(rèn)值。
clip_vertical:可設(shè)置為讓子元素的上邊緣和/或下邊緣裁剪至其容器邊界的附加選項(xiàng)。裁剪基于垂直重力:頂部重力裁剪上邊緣,底部重力裁剪下邊緣,任一重力不會(huì)同時(shí)裁剪兩邊。
clip_horizontal:可設(shè)置為讓子元素的左邊和/或右邊裁剪至其容器邊界的附加選項(xiàng)。裁剪基于水平重力:左邊重力裁剪右邊緣,右邊重力裁剪左邊緣,任一重力不會(huì)同時(shí)裁剪兩邊。
<2>通過(guò)代碼來(lái)實(shí)現(xiàn),即BitmapDrawable
binding.bitmapDrawableInclude.apply {
tv1.setText(R.string.bitmap_drawable)
tv1.background = ContextCompat.getDrawable(requireContext(), R.drawable.bitmap_drawable)
tv2.setText(R.string.bitmap_drawable)
val bitmap = BitmapFactory.decodeResource(resources, R.drawable.nick)
val bitmapShape = BitmapDrawable(resources, bitmap)
tv2.background = bitmapShape
}
2.LayerDrawable
圖層列表(LayerDrawable):是可繪制對(duì)象列表組成的可繪制對(duì)象。列表中的每個(gè)可繪制對(duì)象均按照列表順序繪制,列表中的最后一個(gè)可繪制對(duì)象繪于頂部。
每個(gè)可繪制對(duì)象由單一<layer-list> 元素內(nèi)的<item>元素表示。
<1>XML
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<bitmap android:src="@drawable/nick" />
</item>
<item
android:bottom="150dp"
android:left="0dp"
android:right="250dp"
android:top="0dp">
<shape android:shape="oval">
<solid android:color="@color/royal_blue" />
</shape>
</item>
<item
android:bottom="75dp"
android:left="125dp"
android:right="125dp"
android:top="75dp">
<shape android:shape="oval">
<solid android:color="@color/indian_red" />
</shape>
</item>
<item
android:bottom="0dp"
android:left="250dp"
android:right="0dp"
android:top="150dp">
<shape android:shape="oval">
<solid android:color="@color/yellow" />
</shape>
</item>
</layer-list>
<layer-list>:必備的根元素。包含一個(gè)或多個(gè) <item> 元素。
<item>:是 <layer-list> 元素的子項(xiàng),其屬性支持定義在圖層中所處的位置。
-
<item> 標(biāo)簽屬性:
? android:top:整型。頂部偏移(像素)。
? android:right:整型。右邊偏移(像素)。
? android:bottom:整型。底部偏移(像素)。
? android:left:整型。左邊偏移(像素)。
<2>代碼實(shí)現(xiàn)
binding.layerDrawableInclude.apply {
tv1.setText(R.string.layer_drawable)
tv1.background = ContextCompat.getDrawable(requireContext(), R.drawable.layer_drawable)
tv2.setText(R.string.layer_drawable)
val itemLeft = GradientDrawable().apply {
setColor(ContextCompat.getColor(requireContext(), R.color.royal_blue))
setSize(50.px, 50.px)
shape = GradientDrawable.OVAL
}
val itemCenter = GradientDrawable().apply {
setColor(ContextCompat.getColor(requireContext(), R.color.indian_red))
shape = GradientDrawable.OVAL
}
val itemRight = GradientDrawable().apply {
setColor(ContextCompat.getColor(requireContext(), R.color.yellow))
shape = GradientDrawable.OVAL
}
val arr = arrayOf(
ContextCompat.getDrawable(requireContext(), R.drawable.nick)!!,
itemLeft,
itemCenter,
itemRight
)
val ld = LayerDrawable(arr).apply {
setLayerInset(1, 0.px, 0.px, 250.px, 150.px)
setLayerInset(2, 125.px, 75.px, 125.px, 75.px)
setLayerInset(3, 250.px, 150.px, 0.px, 0.px)
}
tv2.background = ld
}
3.StateListDrawable
狀態(tài)列表(StateListDrawable):會(huì)根據(jù)對(duì)象狀態(tài),使用多個(gè)不同的圖像來(lái)表示同一個(gè)圖形。
<1> XML
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" android:constantSize="false">
<item android:drawable="@drawable/nick" android:state_pressed="false" />
<item android:drawable="@drawable/basketball" android:state_pressed="true" />
</selector>
其中的屬性:
-
<selector>:必備的根元素。包含一個(gè)或多個(gè) <item> 元素。
2.<item>:定義在某些狀態(tài)期間使用的可繪制對(duì)象,必須是 <selector> 元素的子項(xiàng)。其屬性:
android:drawable:引用可繪制對(duì)象資源,必備。
android:state_pressed:布爾值。是否按下對(duì)象(例如觸摸/點(diǎn)按某按鈕)。
android:state_checked:布爾值。是否選中對(duì)象。
android:state_enabled:布爾值。是否能夠接收觸摸或點(diǎn)擊事件。
<2> 代碼
binding.stateListDrawableTv.apply {
setOnClickListener {
Log.e(TAG, "stateListDrawableTv: isPressed = $isPressed")
}
}
val sld = StateListDrawable().apply {
addState(
intArrayOf(android.R.attr.state_pressed),
ContextCompat.getDrawable(requireContext(), R.drawable.basketball)
)
addState(StateSet.WILD_CARD, ContextCompat.getDrawable(requireContext(), R.drawable.nick))
}
binding.stateListDrawableTv2.apply {
background = sld
setOnClickListener {
Log.e(TAG, "stateListDrawableTv2: isPressed = $isPressed")
}
}
4.LevelListDrawable
級(jí)別列表(LevelListDrawable):管理可繪制對(duì)象列表,每個(gè)可繪制對(duì)象都有設(shè)置Level等級(jí)限制,當(dāng)使用setLevel時(shí),會(huì)加載級(jí)別列表中android:maxLevel值大于或等于傳遞至方法的值的可繪制對(duì)象資源。
<1>xml
<?xml version="1.0" encoding="utf-8"?>
<level-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:drawable="@drawable/nick"
android:maxLevel="1" />
<item
android:drawable="@drawable/tom1"
android:maxLevel="2" />
<item
android:drawable="@drawable/tom2"
android:maxLevel="3" />
<item
android:drawable="@drawable/tom3"
android:maxLevel="4" />
<item
android:drawable="@drawable/tom4"
android:maxLevel="5" />
<item
android:drawable="@drawable/tom5"
android:maxLevel="6" />
<item
android:drawable="@drawable/tom6"
android:maxLevel="7" />
<item
android:drawable="@drawable/tom7"
android:maxLevel="8" />
<item
android:drawable="@drawable/tom8"
android:maxLevel="9" />
<item
android:drawable="@drawable/tom9"
android:maxLevel="10" />
</level-list>
<level-list>:必備的根元素。包含一個(gè)或多個(gè) <item> 元素。
-
<item>:在特定級(jí)別下使用的可繪制對(duì)象。
? android:drawable:必備。引用可繪制對(duì)象資源。
? android:maxLevel:整型。表示該Item允許的最高級(jí)別。
? android:minLevel:整型。表示該Item允許的最低級(jí)別。
<2>代碼
class LevelListDrawableFragment : BaseFragment<FragmentLevelListDrawableBinding>() {
private val lld by lazy {
LevelListDrawable().apply {
addLevel(0, 1, getDrawable(R.drawable.nick))
addLevel(0, 2, getDrawable(R.drawable.tom1))
addLevel(0, 3, getDrawable(R.drawable.tom2))
addLevel(0, 4, getDrawable(R.drawable.tom3))
addLevel(0, 5, getDrawable(R.drawable.tom4))
addLevel(0, 6, getDrawable(R.drawable.tom5))
addLevel(0, 7, getDrawable(R.drawable.tom6))
addLevel(0, 8, getDrawable(R.drawable.tom7))
addLevel(0, 9, getDrawable(R.drawable.tom8))
addLevel(0, 10, getDrawable(R.drawable.tom9))
}
}
private fun getDrawable(id: Int): Drawable {
return (ContextCompat.getDrawable(requireContext(), id)
?: ContextCompat.getDrawable(requireContext(), R.drawable.nick)) as Drawable
}
private val levelListDrawable by lazy {
ContextCompat.getDrawable(requireContext(), R.drawable.level_list_drawable)
}
override fun initView() {
binding.levelListDrawableInclude.apply {
tv1.setText(R.string.level_list_drawable)
tv1.background = levelListDrawable
tv2.setText(R.string.level_list_drawable)
tv2.background = lld
}
binding.seekBar.apply {
//init level
levelListDrawable?.level = progress
lld.level = progress
//add listener
setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(
seekBar: SeekBar?,
progress: Int,
fromUser: Boolean
) {
levelListDrawable?.level = progress
lld.level = progress
Log.e(TAG, "onProgressChanged: progreess = $progress")
}
override fun onStartTrackingTouch(seekBar: SeekBar?) {
}
override fun onStopTrackingTouch(seekBar: SeekBar?) {
}
})
}
}
}
5.TransitionDrawable
轉(zhuǎn)換可繪制對(duì)象(TransitionDrawable):可在兩種可繪制對(duì)象資源之間交錯(cuò)淡出。
<1>xml
<?xml version="1.0" encoding="utf-8"?>
<transition xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/ship"
android:drawable="@drawable/nick" />
<item
android:id="@+id/plane"
android:drawable="@drawable/basketball" />
</transition>
其中的屬性:
<transition>:必備的根元素。包含一個(gè)或多個(gè) <item>元素。
-
<item>:轉(zhuǎn)換部分的可繪制對(duì)象。
? android:drawable:必備。引用可繪制對(duì)象資源。
? android:top、android:bottom、android:left、android:right:整型。偏移量(像素)。
注意:不能超過(guò)兩個(gè)Item,調(diào)用 startTransition向前轉(zhuǎn)換,調(diào)用 reverseTransition 向后轉(zhuǎn)換。
<2>代碼
class TransitionDrawableFragment : BaseFragment<FragmentTransitionDrawableBinding>() {
private var isShow = false
private lateinit var bgDrawable: TransitionDrawable
private lateinit var manualDrawable: TransitionDrawable
override fun initView() {
binding.transitionDrawableInclude.apply {
tv1.setText(R.string.transition_drawable)
tv1.background =
ContextCompat.getDrawable(requireContext(), R.drawable.transition_drawable)
tv2.setText(R.string.transition_drawable)
bgDrawable = tv1.background as TransitionDrawable
val drawableArray = arrayOf(
ContextCompat.getDrawable(requireContext(), R.drawable.nick),
ContextCompat.getDrawable(requireContext(), R.drawable.basketball)
)
manualDrawable = TransitionDrawable(drawableArray)
tv2.background = manualDrawable
}
}
private fun setTransition() {
if (isShow) {
bgDrawable.reverseTransition(1500)
manualDrawable.reverseTransition(3000)
} else {
bgDrawable.startTransition(1500)
manualDrawable.startTransition(3000)
}
}
override fun onResume() {
super.onResume()
setTransition()
isShow = !isShow
}
}
6.InsetDrawable
插入可繪制對(duì)象(InsetDrawable):以指定距離插入其他可繪制對(duì)象,當(dāng)視圖需要小于視圖實(shí)際邊界的背景時(shí),此類可繪制對(duì)象很有用。
<1>xml
<?xml version="1.0" encoding="utf-8"?>
<inset xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/nick"
android:insetTop="50dp"
android:insetLeft="150dp">
</inset>
? <inset>:必備。根元素。
? android:drawable:必備。引用可繪制對(duì)象資源。
? android:insetTop、android:insetBottom、android:insetLeft、android:insetRight:尺寸。插入的,表示為尺寸
<2>代碼
binding.insetDrawableInclude.apply {
tv1.setText(R.string.inset_drawable)
tv1.background = ContextCompat.getDrawable(requireContext(), R.drawable.inset_drawable)
tv2.setText(R.string.inset_drawable)
val insetDrawable = InsetDrawable(
ContextCompat.getDrawable(requireContext(), R.drawable.nick),
0f, 0f, 0.5f, 0.25f
)
tv2.background = insetDrawable
}
7.ClipDrawable
裁剪可繪制對(duì)象(ClipDrawable):根據(jù)level等級(jí)對(duì)可繪制對(duì)象進(jìn)行裁剪,可以根據(jù)level與gravity來(lái)控制子可繪制對(duì)象的寬度與高度。
<1>xml
<?xml version="1.0" encoding="utf-8"?>
<clip xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/nick"
android:clipOrientation="horizontal"
android:gravity="center">
</clip>
<clip>:必備。根元素。
android:drawable:必備。引用可繪制對(duì)象資源。
-
android:clipOrientation:裁剪方向。
? horizontal:水平裁剪。
? vertical:垂直裁剪。
android:gravity:重力屬性。
最后通過(guò)設(shè)置level等級(jí)來(lái)實(shí)現(xiàn)裁剪,level 默認(rèn)級(jí)別為 0,即完全裁剪,使圖像不可見(jiàn)。當(dāng)級(jí)別為 10,000 時(shí),圖像不會(huì)裁剪,而是完全可見(jiàn)。
<2>代碼
class ClipDrawableFragment : BaseFragment<FragmentClipDrawableBinding>() {
private val clipDrawable by lazy {
ContextCompat.getDrawable(requireContext(), R.drawable.clip_drawable)
}
private val manualClipDr1awable by lazy {
ClipDrawable(
ContextCompat.getDrawable(requireContext(), R.drawable.nick),
Gravity.CENTER,
ClipDrawable.VERTICAL
)
}
override fun initView() {
binding.clipDrawableInclude.apply {
tv1.setText(R.string.clip_drawable)
tv1.background = clipDrawable
tv2.setText(R.string.clip_drawable)
tv2.background = manualClipDrawable
}
//level 默認(rèn)級(jí)別為 0,即完全裁剪,使圖像不可見(jiàn)。當(dāng)級(jí)別為 10,000 時(shí),圖像不會(huì)裁剪,而是完全可見(jiàn)。
binding.seekBar.apply {
//init level
clipDrawable?.level = progress
manualClipDrawable.level = progress
//add listener
setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(
seekBar: SeekBar?,
progress: Int,
fromUser: Boolean
) {
clipDrawable?.level = progress
manualClipDrawable.level = progress
}
override fun onStartTrackingTouch(seekBar: SeekBar?) {
}
override fun onStopTrackingTouch(seekBar: SeekBar?) {
}
})
}
}
}
8.ScaleDrawable
縮放可繪制對(duì)象(ScaleDrawable):根據(jù)level等級(jí)來(lái)更改其可繪制對(duì)象大小。
<1>xml
<?xml version="1.0" encoding="utf-8"?>
<scale xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/nick"
android:scaleWidth="100%"
android:scaleHeight="100%"
android:scaleGravity="center">
</scale>
其屬性:
-
<scale>:必備。根元素。
android:drawable:必備。引用可繪制對(duì)象資源。
android:scaleGravity:指定縮放后的重力位置。
android:scaleHeight:百分比??s放高度,表示為可繪制對(duì)象邊界的百分比。值的格式為 XX%。例如:100%、12.5% 等。
android:scaleWidth:百分比??s放寬度,表示為可繪制對(duì)象邊界的百分比。值的格式為 XX%。例如:100%、12.5% 等。
<2>代碼
binding.scaleDrawableInclude.apply {
tv1.setText(R.string.scale_drawable)
tv1.background = ContextCompat.getDrawable(requireContext(), R.drawable.scale_drawable)
tv2.setText(R.string.scale_drawable)
val scaleDrawable = ScaleDrawable(
ContextCompat.getDrawable(requireContext(), R.drawable.nick),
Gravity.CENTER,
1f,
1f
)
tv2.background = scaleDrawable
binding.seekBar.apply {
//init level
tv1.background.level = progress
scaleDrawable.level = progress
//add listener
setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(
seekBar: SeekBar?,
progress: Int,
fromUser: Boolean
) {
tv1.background.level = progress
scaleDrawable.level = progress
Log.e(TAG, "onProgressChanged: progreess = $progress")
}
override fun onStartTrackingTouch(seekBar: SeekBar?) {
}
override fun onStopTrackingTouch(seekBar: SeekBar?) {
}
})
}
}
9.ShapeDrawable
形狀可繪制對(duì)象(ShapeDrawable):通過(guò)XML來(lái)定義各種形狀的可繪制對(duì)象。
<1>xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners
android:topLeftRadius="20dp"
android:topRightRadius="20dp" />
<solid android:color="@color/royal_blue" />
<stroke
android:width="3dp"
android:color="@color/purple_200"
android:dashWidth="10dp"
android:dashGap="5dp" />
</shape>
其屬性:
-
<shape>:必備。根元素。
-
android:shape:定義形狀的類型。
rectangle:默認(rèn)形狀,填充包含視圖的矩形。
oval:適應(yīng)包含視圖尺寸的橢圓形狀。
line:跨越包含視圖寬度的水平線。此形狀需要 元素定義線寬。
ring:環(huán)形。
android:innerRadius:尺寸。環(huán)內(nèi)部(中間的孔)的半徑。
android:thickness:尺寸。環(huán)的厚度。
-
-
<corners>:圓角,僅當(dāng)形狀為矩形時(shí)適用。
- android:radius:尺寸。所有角的半徑。如果想要設(shè)置單獨(dú)某個(gè)角,可以使用 android:topLeftRadius、android:topRightRadius、android:bottomLeftRadius、android:bottomRightRadius。
-
<padding>:設(shè)置內(nèi)邊距。
- android:left:尺寸。設(shè)置左內(nèi)邊距。同樣還有 android:right、android:top、android:bottom供選擇。
-
<size>:形狀的大小。
android:height:尺寸。形狀的高度。
android:width:尺寸。形狀的寬度。
-
<solid>:填充形狀的純色。
- android:color:顏色。
-
<stroke>:形狀的筆畫
android:width:尺寸。線寬。
android:color:顏色。線的顏色。
android:dashGap:尺寸。短劃線的間距。虛線效果。
android:dashWidth:尺寸。每個(gè)短劃線的大小。虛線效果。
<2>代碼
class ShapeDrawableFragment : BaseFragment<FragmentShapeDrawableBinding>() {
override fun initView() {
binding.shapeDrawableInclude.apply {
tv1.setText(R.string.shape_drawable)
tv1.background = ContextCompat.getDrawable(requireContext(), R.drawable.shape_drawable)
tv2.setText(R.string.shape_drawable)
val roundRectShape =
RoundRectShape(
floatArrayOf(20f.px, 20f.px, 20f.px, 20f.px, 0f, 0f, 0f, 0f),
null,
null
)
tv2.background = MyShapeDrawable(roundRectShape)
}
}
/**
* TODO: //使用 GradientDrawable 效果更好
*/
class MyShapeDrawable(shape: Shape) : ShapeDrawable(shape) {
private val fillPaint = Paint().apply {
style = Paint.Style.FILL
color = Color.parseColor("#4169E1")
}
private val strokePaint = Paint().apply {
style = Paint.Style.STROKE
color = Color.parseColor("#FFBB86FC")
// strokeCap = Paint.Cap.ROUND
// strokeJoin = Paint.Join.ROUND
strokeMiter = 10f
strokeWidth = 5f.px
pathEffect = DashPathEffect(floatArrayOf(10f.px, 5f.px), 0f)
}
override fun onDraw(shape: Shape?, canvas: Canvas?, paint: Paint?) {
super.onDraw(shape, canvas, paint)
shape?.draw(canvas, fillPaint)
shape?.draw(canvas, strokePaint)
}
}
}
10.GradientDrawable
漸變可繪制對(duì)象(GradientDrawable):如其名,實(shí)現(xiàn)漸變顏色效果。其實(shí)也是屬于ShapeDrawable。
<1>xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<gradient
android:endColor="#BBFFFF"
android:gradientRadius="50%p"
android:startColor="#00F5FF"
android:type="radial" />
</shape>
<shape>:必備。根元素。
-
gradient:表示漸變的顏色。
android:angle:整型。表示漸變的角度。0 表示為從左到右,90 表示為從上到上。注意:必須是 45 的倍數(shù)。默認(rèn)值為 0。
android:centerX:浮點(diǎn)型。表示漸變中心相對(duì) X 軸位置 (0 - 1.0)。 android:centerY同理。
android:startColor:顏色。起始顏色。 android:endColor、android:centerColor分別表示結(jié)束顏色與中間顏色。
android:gradientRadius:浮點(diǎn)型。漸變的半徑。僅在 android:type="radial" 時(shí)適用。
android:type:漸變的類型。
linear:線性漸變。默認(rèn)為該類型。
radial:徑向漸變,也就是雷達(dá)式漸變,起始顏色為中心顏色。
sweep:流線型漸變。
<2>代碼
binding.gradientDrawableInclude.apply {
tv1.setText(R.string.gradient_drawable)
tv1.background =
ContextCompat.getDrawable(requireContext(), R.drawable.gradient_drawable)
tv2.setText(R.string.gradient_drawable)
val gradientDrawable = GradientDrawable().apply {
shape = GradientDrawable.OVAL
gradientType = GradientDrawable.RADIAL_GRADIENT
colors = intArrayOf(Color.parseColor("#00F5FF"), Color.parseColor("#BBFFFF"))
gradientRadius = 100f.px
}
tv2.background = gradientDrawable
}
11.AnimationDrawable
動(dòng)畫可繪制對(duì)象(AnimationDrawable):用于創(chuàng)建逐幀動(dòng)畫的可繪制對(duì)象。
<1>xml
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:drawable="@drawable/nick"
android:duration="1000" />
<item
android:drawable="@drawable/basketball"
android:duration="1000" />
</animation-list>
<animation-list>:必備。根元素。
-
<item>:每一幀的可繪制對(duì)象。
android:drawable:必備。引用可繪制對(duì)象資源。
android:duration:該幀的持續(xù)時(shí)間,單位為毫秒。
android:oneshot:布爾值。代表是否只單次展示該動(dòng)畫,默認(rèn)為false。
<2>代碼
class AnimationDrawableFragment : BaseFragment<FragmentAnimationDrawableBinding>() {
private val animationDrawable by lazy {
ContextCompat.getDrawable(
requireContext(),
R.drawable.animation_drawable
) as AnimationDrawable
}
override fun initView() {
binding.animationDrawableInclude.apply {
tv1.setText(R.string.animation_drawable)
tv1.background = animationDrawable
tv2.setText(R.string.animation_drawable)
animationDrawable.start()
val animationDrawable = AnimationDrawable().apply {
ContextCompat.getDrawable(requireContext(), R.drawable.nick)
?.let { addFrame(it, 1000) }
ContextCompat.getDrawable(requireContext(), R.drawable.basketball)
?.let { addFrame(it, 1000) }
}
tv2.background = animationDrawable
animationDrawable.start()
}
}
}
2.多動(dòng)畫聯(lián)動(dòng)
class AnimatedVectorDrawableFragment : BaseFragment<FragmentAnimatedVectorDrawableBinding>() {
override fun initView() {
(binding.vectorDrawableIv.drawable as Animatable).start()
//如果你想對(duì) VectorDrawable (也就是binding.vectorDrawableIv2.drawable) 做動(dòng)畫處理,你需要使用 AnimatedVectorDrawable
//不然你會(huì)發(fā)現(xiàn)報(bào)錯(cuò)信息 Method setScaleX() with type float not found on target class class android.graphics.drawable.VectorDrawable
//refer link:https://stackoverflow.com/a/32007436/11641198
val iv = binding.vectorDrawableIv2
val an1 = ObjectAnimator.ofFloat(iv, "scaleX", 0f, 1f).apply {
duration = 3000
repeatCount = ValueAnimator.INFINITE
repeatMode = ValueAnimator.REVERSE
}
val an2 = ObjectAnimator.ofFloat(iv, "scaleY", 0f, 1f).apply {
duration = 3000
repeatCount = ValueAnimator.INFINITE
repeatMode = ValueAnimator.REVERSE
}
AnimatorSet().apply {
duration = 3000
playTogether(an1, an2)
start()
}
}
}
12.自定義 Drawable

package com.drawable.learning.fragment.custom.line_chart
import android.graphics.*
import android.graphics.drawable.Drawable
import com.drawable.learning.tools.px
/**
* @author jere
*
* Draws a mesh dotted background
*/
class BgGridDrawable : Drawable() {
private val lineCount = 4
private val columnCount = 5
private val path: Path = Path()
private val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
style = Paint.Style.STROKE
strokeWidth = 1f.px
color = Color.parseColor("#80B5B5B5")
pathEffect = DashPathEffect(floatArrayOf(5f.px, 5f.px, 5f.px, 5f.px), 0f)
}
private var pixelX = 0f
private var pixelY = 0f
override fun draw(canvas: Canvas) {
for (i in 0 until lineCount) {
pixelY = (bounds.top + bounds.height() / lineCount * i).toFloat()
path.moveTo(bounds.left.toFloat(), pixelY)
path.lineTo(bounds.right.toFloat(), pixelY)
canvas.drawPath(path, paint)
}
for (i in 0 until columnCount) {
pixelX = (bounds.left + bounds.width() / columnCount * i).toFloat()
path.moveTo(pixelX, bounds.top.toFloat())
path.lineTo(pixelX, bounds.bottom.toFloat())
canvas.drawPath(path, paint)
}
}
override fun setAlpha(alpha: Int) {
paint.alpha = alpha
}
override fun getOpacity(): Int {
return when (paint.alpha) {
0xff -> PixelFormat.OPAQUE
0x00 -> PixelFormat.TRANSPARENT
else -> PixelFormat.TRANSLUCENT
}
}
override fun setColorFilter(colorFilter: ColorFilter?) {
paint.colorFilter = colorFilter
}
}
package com.drawable.learning.fragment.custom.line_chart
import android.graphics.*
import android.graphics.drawable.Drawable
import com.drawable.learning.fragment.BaseFragment
import com.drawable.learning.databinding.FragmentLineChartBinding
import com.drawable.learning.tools.px
/**
* @author jere
*
* drawing line chart
*/
class LineChartFragment : BaseFragment<FragmentLineChartBinding>() {
override fun initView() {
binding.customDrawableTv.apply {
background = MyDrawable()
}
}
class MyDrawable : Drawable() {
private val bgDrawable = BgGridDrawable()
private val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
style = Paint.Style.FILL_AND_STROKE
strokeWidth = 2f.px
color = Color.parseColor("#96CDCD")
}
//hard code the points of the line chart
private val path = Path()
private fun calculatePath() {
path.apply {
reset()
moveTo(bounds.left.toFloat(), bounds.bottom.toFloat())
lineTo(bounds.left.toFloat(), 150f)
lineTo(bounds.left.toFloat() + bounds.width() * 0.05f, 200f)
lineTo(bounds.left.toFloat() + bounds.width() * 0.1f, 130f)
lineTo(bounds.left.toFloat() + bounds.width() * 0.15f, 250f)
lineTo(bounds.left.toFloat() + bounds.width() * 0.2f, 80f)
lineTo(bounds.left.toFloat() + bounds.width() * 0.25f, 220f)
lineTo(bounds.left.toFloat() + bounds.width() * 0.3f, 200f)
lineTo(bounds.left.toFloat() + bounds.width() * 0.35f, 30f)
lineTo(bounds.left.toFloat() + bounds.width() * 0.4f, 240f)
lineTo(bounds.left.toFloat() + bounds.width() * 0.45f, 260f)
lineTo(bounds.left.toFloat() + bounds.width() * 0.5f, 160f)
lineTo(bounds.left.toFloat() + bounds.width() * 0.55f, 100f)
lineTo(bounds.left.toFloat() + bounds.width() * 0.6f, 80f)
lineTo(bounds.left.toFloat() + bounds.width() * 0.65f, 20f)
lineTo(bounds.left.toFloat() + bounds.width() * 0.7f, 150f)
lineTo(bounds.left.toFloat() + bounds.width() * 0.75f, 170f)
lineTo(bounds.left.toFloat() + bounds.width() * 0.8f, 320f)
lineTo(bounds.left.toFloat() + bounds.width() * 0.85f, 220f)
lineTo(bounds.left.toFloat() + bounds.width() * 0.9f, 300f)
lineTo(bounds.left.toFloat() + bounds.width() * 0.95f, 120f)
lineTo(bounds.left.toFloat() + bounds.width() * 1f, 360f)
lineTo(bounds.left.toFloat() + bounds.width() * 1f, bounds.bottom.toFloat())
}
}
override fun draw(canvas: Canvas) {
paint.shader = null
calculatePath()
canvas.drawRect(
bounds.left.toFloat(),
bounds.top.toFloat(),
bounds.right.toFloat(),
bounds.bottom.toFloat(),
paint
)
bgDrawable.bounds = bounds
bgDrawable.draw(canvas)
// paint.apply {
// style = Paint.Style.STROKE
// strokeWidth = 2f.px
// color = Color.WHITE
// }
// canvas.drawPath(path, paint)
//draw the shader for path
path.close()
paint.apply {
style = Paint.Style.FILL
shader = LinearGradient(
bounds.left.toFloat(), bounds.top.toFloat(),
bounds.left.toFloat(), bounds.bottom.toFloat(),
intArrayOf(Color.parseColor("#FF6A6A"), Color.TRANSPARENT),
null,
Shader.TileMode.CLAMP
)
}
canvas.drawPath(path, paint)
}
override fun setAlpha(alpha: Int) {
paint.alpha = alpha
}
override fun getOpacity(): Int {
return when (paint.alpha) {
0xff -> PixelFormat.OPAQUE
0x00 -> PixelFormat.TRANSPARENT
else -> PixelFormat.TRANSLUCENT
}
}
override fun setColorFilter(colorFilter: ColorFilter?) {
paint.colorFilter = colorFilter
}
}
}
<?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=".fragment.custom.line_chart.LineChartFragment">
<TextView
android:id="@+id/customDrawableTv"
android:layout_width="300dp"
android:layout_height="200dp"
android:layout_marginTop="50dp"
android:gravity="start|center_horizontal"
android:text="@string/line_chart"
android:textColor="@color/white"
android:textSize="20sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

package com.drawable.learning.fragment.custom.ball
import android.graphics.*
import android.graphics.drawable.Drawable
import com.drawable.learning.tools.px
/**
* @author jere
*/
class BallDrawable : Drawable() {
private val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
style = Paint.Style.FILL
color = Color.parseColor("#D2691E")
}
private val linePaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
style = Paint.Style.STROKE
strokeWidth = 1f.px
color = Color.BLACK
}
override fun draw(canvas: Canvas) {
val radius = bounds.width().toFloat() / 2
canvas.drawCircle(
bounds.width().toFloat() / 2,
bounds.height().toFloat() / 2,
radius,
paint
)
//the vertical line of the ball
canvas.drawLine(
bounds.width().toFloat() / 2,
0f,
bounds.width().toFloat() / 2,
bounds.height().toFloat(),
linePaint
)
//the transverse line of the ball
canvas.drawLine(
0f,
bounds.height().toFloat() / 2,
bounds.width().toFloat(),
bounds.height().toFloat() / 2,
linePaint
)
val path = Path()
val sinValue = kotlin.math.sin(Math.toRadians(45.0)).toFloat()
//left curve
path.moveTo(radius - sinValue * radius,
radius - sinValue * radius
)
path.cubicTo(radius - sinValue * radius,
radius - sinValue * radius,
radius,
radius,
radius - sinValue * radius,
radius + sinValue * radius
)
//right curve
path.moveTo(radius + sinValue * radius,
radius - sinValue * radius
)
path.cubicTo(radius + sinValue * radius,
radius - sinValue * radius,
radius,
radius,
radius + sinValue * radius,
radius + sinValue * radius
)
canvas.drawPath(path, linePaint)
}
override fun setAlpha(alpha: Int) {
paint.alpha = alpha
}
override fun getOpacity(): Int {
return when (paint.alpha) {
0xff -> PixelFormat.OPAQUE
0x00 -> PixelFormat.TRANSPARENT
else -> PixelFormat.TRANSLUCENT
}
}
override fun setColorFilter(colorFilter: ColorFilter?) {
paint.colorFilter = colorFilter
}
}
package com.drawable.learning.fragment.custom.ball
import android.animation.AnimatorSet
import android.animation.ObjectAnimator
import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.Path
import android.util.AttributeSet
import android.view.Gravity
import android.view.MotionEvent
import android.widget.FrameLayout
import android.widget.ImageView
import com.drawable.learning.tools.px
/**
* @author jere
*/
class CustomBallMovingSiteView(context: Context, attributeSet: AttributeSet?, defStyleAttr: Int) :
FrameLayout(context, attributeSet, defStyleAttr) {
constructor(context: Context) : this(context, null, 0)
constructor(context: Context, attributeSet: AttributeSet?) : this(context, attributeSet, 0)
private lateinit var ballContainerIv: ImageView
private val ballDrawable = BallDrawable()
private val radius = 50
private var rippleAlpha = 0
private var rippleRadius = 10f
private var rawTouchEventX = 0f
private var rawTouchEventY = 0f
private var touchEventX = 0f
private var touchEventY = 0f
private var lastTouchEventX = 0f
private var lastTouchEventY = 0f
private val ripplePaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
isDither = true
color = Color.RED
style = Paint.Style.STROKE
strokeWidth = 2f.px
alpha = rippleAlpha
}
init {
initView(context, attributeSet)
}
private fun initView(context: Context, attributeSet: AttributeSet?) {
//generate a ball by dynamic
ballContainerIv = ImageView(context).apply {
layoutParams = LayoutParams(radius * 2, radius * 2).apply {
gravity = Gravity.CENTER
}
setImageDrawable(ballDrawable)
//setBackgroundColor(Color.BLUE)
}
addView(ballContainerIv)
setWillNotDraw(false)
}
override fun onTouchEvent(event: MotionEvent?): Boolean {
lastTouchEventX = touchEventX
lastTouchEventY = touchEventY
event?.let {
rawTouchEventX = it.x
rawTouchEventY = it.y
touchEventX = it.x - radius
touchEventY = it.y - radius
}
ObjectAnimator.ofFloat(this, "rippleValue", 0f, 1f).apply {
duration = 1000
start()
}
val path = Path().apply {
moveTo(lastTouchEventX, lastTouchEventY)
quadTo(
lastTouchEventX,
lastTouchEventY,
touchEventX,
touchEventY
)
}
val oaMoving = ObjectAnimator.ofFloat(ballContainerIv, "x", "y", path)
val oaRotating = ObjectAnimator.ofFloat(ballContainerIv, "rotation", 0f, 360f)
AnimatorSet().apply {
duration = 1000
playTogether(oaMoving, oaRotating)
start()
}
return super.onTouchEvent(event)
}
fun setRippleValue(currentValue: Float) {
rippleRadius = currentValue * radius
rippleAlpha = ((1 - currentValue) * 255).toInt()
invalidate()
}
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
ripplePaint.alpha = rippleAlpha
//draw ripple for click event
canvas?.drawCircle(rawTouchEventX, rawTouchEventY, rippleRadius, ripplePaint)
}
}
/**
* @author jere
*/
class MoveBallFragment : BaseFragment<FragmentMoveBallBinding>() {
override fun initView() {
}
}
<?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=".fragment.custom.ball.MoveBallFragment">
<com.drawable.learning.fragment.custom.ball.CustomBallMovingSiteView
android:layout_width="match_parent"
android:layout_height="match_parent" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/try_click_screen"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

package com.drawable.learning.fragment.custom.stroke
import android.graphics.*
import android.graphics.drawable.Drawable
import com.drawable.learning.tools.px
class BubbleChatRectDrawable : Drawable() {
private val pathPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
color = Color.parseColor("#ECF6ED")
style = Paint.Style.FILL
}
private val padding = 2f.px
private var paddingStart = 30f.px
private var paddingBottom = 20f.px
private val outPath = Path()
private val inPath = Path()
override fun draw(canvas: Canvas) {
bounds.apply {
//外邊框
pathPaint.color = Color.parseColor("#2BDEA8")
canvas.drawRoundRect(
left.toFloat(),
top.toFloat(),
right.toFloat(),
bottom.toFloat() - paddingBottom,
5f.px,
5f.px,
pathPaint
)
//內(nèi)邊框
pathPaint.color = Color.parseColor("#ECF6ED")
canvas.drawRoundRect(
left.toFloat() + padding,
top.toFloat() + padding,
right.toFloat() - padding,
bottom.toFloat() - paddingBottom - padding,
5f.px,
5f.px,
pathPaint
)
outPath.reset()
outPath.moveTo(bounds.left + paddingStart, bottom - paddingBottom - padding)
outPath.lineTo(bounds.left + paddingStart + 10f.px, bottom - paddingBottom + 13f.px)
outPath.lineTo(bounds.left + paddingStart + 20f.px, bottom - paddingBottom - padding)
outPath.close()
pathPaint.color = Color.parseColor("#2BDEA8")
canvas.drawPath(outPath, pathPaint)
inPath.reset()
inPath.moveTo(
bounds.left + paddingStart + padding,
bottom - paddingBottom - padding * 2
)
inPath.lineTo(
bounds.left + paddingStart + 10f.px,
bottom - paddingBottom + 13f.px - padding * 2
)
inPath.lineTo(
bounds.left + paddingStart - padding + 20f.px,
bottom - paddingBottom - padding * 2
)
inPath.close()
pathPaint.color = Color.parseColor("#ECF6ED")
canvas.drawPath(inPath, pathPaint)
}
}
override fun setAlpha(alpha: Int) {
pathPaint.alpha = alpha
}
override fun setColorFilter(colorFilter: ColorFilter?) {
pathPaint.colorFilter = colorFilter
}
override fun getOpacity(): Int {
return when (pathPaint.alpha) {
0xff -> PixelFormat.OPAQUE
0x00 -> PixelFormat.TRANSPARENT
else -> PixelFormat.TRANSLUCENT
}
}
}
package com.drawable.learning.fragment.custom.stroke
import android.animation.ValueAnimator
import android.content.Context
import android.graphics.*
import android.util.AttributeSet
import android.util.Log
import android.view.View
import android.view.animation.LinearInterpolator
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
import com.drawable.learning.tools.px
class HighlightAnimView(context: Context, attributeSet: AttributeSet?, defStyleAttr: Int) :
View(context, attributeSet, defStyleAttr), DefaultLifecycleObserver {
private val TAG = "HighlightAnimView"
constructor(context: Context) : this(context, null, 0)
constructor(context: Context, attributeSet: AttributeSet?) : this(context, attributeSet, 0)
private var percent = 0f
private var realValueAnimator: ValueAnimator =
ValueAnimator.ofFloat(0f, 1f).apply {
interpolator = LinearInterpolator()
repeatCount = ValueAnimator.INFINITE
repeatMode = ValueAnimator.RESTART
duration = 600
addUpdateListener {
it.animatedValue.toString().toFloat().let { valuePercent ->
percent = valuePercent
// Log.e(TAG, "percent = $percent: ")
}
postInvalidate()
}
}
private fun startAnim() {
Log.e(TAG, "startAnim: realValueAnimator.isRunning = ${realValueAnimator.isRunning}")
if (!realValueAnimator.isRunning) {
realValueAnimator.start()
}
}
private fun stopAnim() {
Log.e(TAG, "stopAnim: realValueAnimator.isRunning = ${realValueAnimator.isRunning}")
if (realValueAnimator.isRunning) {
realValueAnimator.cancel()
}
}
override fun onResume(owner: LifecycleOwner) {
super.onResume(owner)
Log.e(TAG, "onResume: ")
startAnim()
}
override fun onPause(owner: LifecycleOwner) {
super.onPause(owner)
Log.e(TAG, "onPause: ")
stopAnim()
}
override fun onStop(owner: LifecycleOwner) {
super.onStop(owner)
Log.e(TAG, "onStop: ")
stopAnim()
}
private val dashPathEffect by lazy {
DashPathEffect(floatArrayOf(5f.px, 5f.px, 5f.px, 5f.px), 1f)
}
private val linePaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
color = Color.parseColor("#FFE524")
style = Paint.Style.STROKE
strokeWidth = 5f.px
pathEffect = dashPathEffect
}
private val topPath = Path()
private val rightPath = Path()
private val bottomPath = Path()
private val leftPath = Path()
private val distance = 10f.px
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
topPath.reset()
topPath.moveTo(distance * percent, 0f)
topPath.lineTo(width.toFloat(), 0f)
canvas?.drawPath(topPath, linePaint)
rightPath.reset()
rightPath.moveTo(width.toFloat(), distance * percent)
rightPath.lineTo(width.toFloat(), height.toFloat())
canvas?.drawPath(rightPath, linePaint)
bottomPath.reset()
bottomPath.moveTo(width.toFloat() - distance * percent, height.toFloat())
bottomPath.lineTo(0f, height.toFloat())
canvas?.drawPath(bottomPath, linePaint)
leftPath.reset()
leftPath.moveTo(0f, height.toFloat() - distance * percent)
leftPath.lineTo(0f, 0f)
canvas?.drawPath(leftPath, linePaint)
}
}
package com.drawable.learning.fragment.custom.stroke
import com.drawable.learning.databinding.FragmentHighlightAnimBinding
import com.drawable.learning.fragment.BaseFragment
import com.drawable.learning.tools.px
class HighlightAnimFragment : BaseFragment<FragmentHighlightAnimBinding>() {
override fun initView() {
val bubbleChatRectDrawable = BubbleChatRectDrawable()
val intArray = IntArray(2)
binding.testIv.getLocationOnScreen(intArray)
bubbleChatRectDrawable.setBounds(
intArray[0],
intArray[1],
(intArray[0] + 200f.px).toInt(),
(intArray[1] + 100f.px).toInt()
)
binding.testIv.background = bubbleChatRectDrawable
lifecycle.addObserver(binding.noteSav)
lifecycle.addObserver(binding.sav)
}
}
<?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">
<TextView
android:id="@+id/noteTv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="20dp"
android:padding="10dp"
android:text="@string/please_note"
android:textSize="16sp"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.drawable.learning.fragment.custom.stroke.HighlightAnimView
android:id="@+id/noteSav"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="@android:color/transparent"
app:layout_constraintBottom_toBottomOf="@id/noteTv"
app:layout_constraintEnd_toEndOf="@id/noteTv"
app:layout_constraintStart_toStartOf="@id/noteTv"
app:layout_constraintTop_toTopOf="@id/noteTv" />
<ImageView
android:id="@+id/noteNickIv"
android:layout_width="160dp"
android:layout_height="100dp"
android:scaleType="fitXY"
android:src="@drawable/nick"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.3" />
<com.drawable.learning.fragment.custom.stroke.HighlightAnimView
android:id="@+id/sav"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_margin="-20dp"
android:background="@android:color/transparent"
app:layout_constraintBottom_toBottomOf="@id/noteNickIv"
app:layout_constraintEnd_toEndOf="@id/noteNickIv"
app:layout_constraintStart_toStartOf="@id/noteNickIv"
app:layout_constraintTop_toTopOf="@id/noteNickIv" />
<ImageView
android:id="@+id/testIv"
android:layout_width="200dp"
android:layout_height="100dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginStart="100dp"
android:layout_marginBottom="50dp"/>
</androidx.constraintlayout.widget.ConstraintLayout>
13.其他:
abstract class BaseFragment<B : ViewBinding> : Fragment() {
private var _binding: B? = null
val binding
get() = _binding!!
val TAG = this.javaClass.simpleName
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val type = (javaClass.genericSuperclass as ParameterizedType).actualTypeArguments[0] as Class<B>
val method = type.getDeclaredMethod(
"inflate",
LayoutInflater::class.java,
ViewGroup::class.java,
Boolean::class.java
)
_binding = method.invoke(null, layoutInflater, container, false) as B
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
initView()
}
abstract fun initView()
}