Android Drawable实践
前言
最近看源码、做事情有点累了,抽时间看了看书,发现很多东西只是看过,却没有去实践,看了基本也就忘了,有些内容真好没那么复杂,就花了点零散时间试试。
这篇文章是Android中Drawable的实践内容,从《Android开发艺术探索》的第六章入手的,这里简单记录下。
BitmapDrawable
BitmapDrawable实际就是用来放图片的,下面是例子:
xml
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
android:src="@drawable/ic_launcher"
android:antialias="true"
android:dither="true"
android:filter="true"
android:gravity="fill_vertical|left"
android:mipMap="false"
android:tileMode="disabled"
/>
<!--
antialias 抗锯齿
dither 抖动效果,防止因像素适配问题导致的失真
filter 过滤效果,防止拉伸或压缩时影响显示效果
gravity 当图片尺寸小于容器尺寸时,对图片的定位效果
mipMap 纹理映射,防止远处图片失真
tileMode 平铺模式,和gravity冲突,有平铺repeat、镜像mirror、两边扩展
-->
用法也简单,给view的background设置上就行:
ini
<TextView
android:id="@+id/bitmapDrawable"
android:text="BitmapDrawable"
android:background="@drawable/ic_drawable_bitmap"
android:layout_width="match_parent"
android:layout_height="80dp"
android:layout_margin="5dp"
tools:ignore="HardcodedText"
/>
显示效果:
NinePatchDrawable
NinePatchDrawable和BitmapDrawable类似,需要注意的是Android Studio中只有png才能转成".9"格式图片,NinePatchDrawable里面也不能访问mipmap的图片,需要复制到drawable里面去:
xml
<nine-patch xmlns:android="http://schemas.android.com/apk/res/android"
android:src="@drawable/ic_nine_patch"
android:dither="true"
/>
<!--
antialias 抗锯齿
dither 抖动效果,防止因像素适配问题导致的失真
filter 过滤效果,防止拉伸或压缩时影响显示效果
gravity 当图片尺寸小于容器尺寸时,对图片的定位效果
mipMap 纹理映射,防止远处图片失真
tileMode 平铺模式,和gravity冲突,有平铺repeat、镜像mirror、两边扩展
-->
用法类似:
ini
<TextView
android:id="@+id/ninePatchDrawable"
android:text="NinePatchDrawable"
android:background="@drawable/ic_drawable_nine_patch"
android:layout_width="match_parent"
android:layout_height="80dp"
android:layout_margin="5dp"
tools:ignore="HardcodedText"
/>
配置:
显示效果:
拉伸的很奇怪,不过没问题hh
ShapeDrawable
ShapeDrawable用的比较多吧,各种背景基本是这个,不多说:
xml
<shape android:shape="rectangle"
xmlns:android="http://schemas.android.com/apk/res/android" >
<!-- 线 -->
<stroke
android:width="3dp"
android:color="@color/blue"
android:dashGap="1dp"
android:dashWidth="2dp"/>
<!-- 实心 -->
<solid
android:color="@color/gray"/>
<!-- 圆角 -->
<corners
android:topLeftRadius="5dp"
android:topRightRadius="10dp"
android:bottomRightRadius="15dp"
android:bottomLeftRadius="20dp"/>
<!-- 渐变(和solid冲突) -->
<gradient
android:type="sweep"
android:centerY="50%"
android:centerX="25%"
android:startColor="@color/red"
android:centerColor="@color/yellow"
android:endColor="@color/purple"
/>
<padding
android:top="0dp"
android:left="30dp"
android:right="30dp"
android:bottom="20dp"/>
<size
android:width="300dp"
android:height="100dp"
/>
</shape>
使用:
ini
<TextView
android:id="@+id/shapeDrawable"
android:text="ShapeDrawable"
android:background="@drawable/ic_drawable_shape"
android:layout_width="match_parent"
android:layout_height="80dp"
android:layout_margin="5dp"
tools:ignore="HardcodedText"
/>
实际效果:
shape的几种类型(rectangle、oval、ring)和gradient的几种类型注意下,渐变中心用百分比,其他问题不大。
LayerDrawable
LayerDrawable就是放多层item,模拟阴影还挺好用的:
xml
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 模仿输入框 -->
<!-- 全灰 -->
<item>
<shape android:shape="rectangle">
<solid android:color="@color/gray"/>
</shape>
</item>
<!-- 下面留一条灰线,其他全白 -->
<item android:bottom="30dp" >
<shape android:shape="rectangle">
<solid android:color="@color/white"/>
</shape>
</item>
<!-- 左右留一点,嵌入底部灰线,中间全白 -->
<item android:bottom="15dp" android:left="15dp" android:right="15dp">
<shape android:shape="rectangle">
<solid android:color="@color/white"/>
</shape>
</item>
</layer-list>
使用:
ini
<TextView
android:id="@+id/layerDrawable"
android:text="LayerDrawable"
android:background="@drawable/ic_drawable_layer"
android:layout_width="match_parent"
android:layout_height="80dp"
android:layout_margin="5dp"
tools:ignore="HardcodedText"
/>
效果:
StateListDrawable
StateListDrawable就是selector标签,用的也挺多吧:
xml
<selector xmlns:android="http://schemas.android.com/apk/res/android"
android:constantSize="false"
android:dither="true"
android:variablePadding="false">
<!-- constantSize,状态变化,size保持不变(最大item尺寸) -->
<!-- constantSize,状态变化,padding跟随变化,否则用最大padding -->
<!-- 各种状态,pressed、checked、focused、selected、enabled -->
<item android:state_pressed="true">
<shape>
<solid android:color="@color/gray"/>
</shape>
</item>
<item android:state_focused="true">
<shape>
<stroke android:width="3dp" android:color="@color/blue"/>
</shape>
</item>
<!-- 一般状态放最后 -->
<item>
<shape>
<stroke android:width="3dp" android:color="@color/gray"/>
</shape>
</item>
</selector>
使用,这里要注意下TextView不处理点击事件:
ini
<TextView
android:id="@+id/stateListDrawable"
android:text="StateListDrawable(press)"
android:background="@drawable/ic_drawable_state_list"
android:layout_width="match_parent"
android:layout_height="80dp"
android:layout_margin="5dp"
android:clickable="true"
android:focusable="true"
tools:ignore="HardcodedText"
/>
实际效果:
LevelListDrawable
LevelListDrawable可以设置level,设置level后里面会变化:
xml
<level-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:minLevel="0" android:maxLevel="0">
<shape>
<solid android:color="@color/gray"/>
</shape>
</item>
<item android:minLevel="1" android:maxLevel="2">
<shape>
<solid android:color="@color/yellow"/>
</shape>
</item>
<item android:minLevel="2" android:maxLevel="2">
<shape>
<solid android:color="@color/purple"/>
</shape>
</item>
<item android:minLevel="3" android:maxLevel="3">
<shape>
<solid android:color="@color/red"/>
</shape>
</item>
</level-list>
使用:
ini
<TextView
android:id="@+id/levelListDrawable"
android:text="LevelListDrawable: current level = 0"
android:background="@drawable/ic_drawable_level_list"
android:layout_width="match_parent"
android:layout_height="80dp"
android:layout_margin="5dp"
tools:ignore="HardcodedText"
/>
代码:
ini
// 切换level
binding.levelListDrawable.setOnClickListener {
var level = binding.levelListDrawable.background.level
level = ++level % 5
binding.levelListDrawable.background.level = level
binding.levelListDrawable.text = "LevelListDrawable: current level = $level"
}
效果:
TransitionDrawable
TransitionDrawable有个渐变效果:
ini
<transition xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:top="50dp"
android:bottom="50dp"
android:left="100dp"
android:right="100dp"
>
<shape>
<stroke android:width="3dp" android:color="@color/gray"/>
</shape>
</item>
<item>
<shape>
<solid android:color="@color/gray"/>
</shape>
</item>
</transition>
使用:
ini
<TextView
android:id="@+id/transitionDrawable"
android:text="TransitionDrawable(click)"
android:background="@drawable/ic_drawable_transition"
android:layout_width="match_parent"
android:layout_height="80dp"
android:layout_margin="5dp"
tools:ignore="HardcodedText"
/>
代码:
scss
// 启动transition
binding.transitionDrawable.setOnClickListener {
(binding.transitionDrawable.background as TransitionDrawable).startTransition(1500)
}
实际效果:
InsertDrawable
InsertDrawable就是能包含其他drawable,提供一个padding效果:
ini
<inset xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/ic_launcher"
android:insetTop="20dp"
android:insetBottom="40dp"
android:insetLeft="10dp"
android:insetRight="30dp">
<shape>
<stroke android:width="3dp" android:color="@color/gray"/>
</shape>
</inset>
使用:
ini
<TextView
android:id="@+id/insertDrawable"
android:text="InsertDrawable"
android:background="@drawable/ic_drawable_insert"
android:layout_width="match_parent"
android:layout_height="80dp"
android:layout_margin="5dp"
tools:ignore="HardcodedText"
/>
实际效果:
ScaleDrawable
ScaleDrawable就是会根据level缩放的drawable:
ini
<scale xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/ic_launcher"
android:scaleGravity="center"
android:scaleWidth="75%"
android:scaleHeight="75%"
>
<shape>
<solid android:color="@color/gray"/>
</shape>
</scale>
使用:
ini
<TextView
android:id="@+id/scaleDrawable"
android:text="ScaleDrawable: current level = 0"
android:background="@drawable/ic_drawable_scale"
android:layout_width="match_parent"
android:layout_height="80dp"
android:layout_margin="5dp"
tools:ignore="HardcodedText"
/>
代码:
ini
// 设置缩放
binding.scaleDrawable.setOnClickListener {
var level = binding.scaleDrawable.background.level
level = (level + 1000) % 10000
binding.scaleDrawable.background.level = level
binding.scaleDrawable.text = "ScaleDrawable: current level = $level"
}
实际效果:
ClipDrawable
ClipDrawable会根据level裁切:
ini
<clip xmlns:android="http://schemas.android.com/apk/res/android"
android:clipOrientation="vertical"
android:gravity="center"
android:drawable="@drawable/ic_launcher"
>
</clip>
使用:
ini
<TextView
android:id="@+id/clipDrawable"
android:text="ClipDrawable: current level = 0"
android:background="@drawable/ic_drawable_clip"
android:layout_width="match_parent"
android:layout_height="80dp"
android:layout_margin="5dp"
tools:ignore="HardcodedText"
/>
代码:
ini
// 设置裁切
binding.clipDrawable.setOnClickListener {
var level = binding.clipDrawable.background.level
level = (level + 1000) % 10000
binding.clipDrawable.background.level = level
binding.clipDrawable.text = "ClipDrawable: current level = $level"
}
实际效果:
CustomDrawable
这里我自己写了一个Drawable,凑合看下吧:
kotlin
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.ColorFilter
import android.graphics.Paint
import android.graphics.PixelFormat
import android.graphics.drawable.Drawable
class CustomStarDrawable: Drawable() {
private val mPaint: Paint = Paint()
init {
mPaint.strokeWidth = 5f
mPaint.flags = Paint.ANTI_ALIAS_FLAG
mPaint.style = Paint.Style.FILL
mPaint.color = Color.GRAY
}
override fun draw(canvas: Canvas) {
// 绘制圆圈
val num = level / 2000
val radius = (bounds.bottom - bounds.top) / 4f
val distance = (bounds.right - bounds.left) / 5f
when(num) {
1 -> mPaint.color = Color.RED
2 -> mPaint.color = Color.YELLOW
3 -> mPaint.color = Color.BLUE
4 -> mPaint.color = Color.GREEN
else -> mPaint.color = Color.GRAY
}
// canvas.drawRect(bounds, mPaint)
for (i in 0 until num) {
canvas.drawCircle(distance * i + radius, 2 * radius, radius, mPaint)
}
}
override fun setAlpha(alpha: Int) {
mPaint.alpha = alpha
invalidateSelf()
}
override fun setColorFilter(colorFilter: ColorFilter?) {
mPaint.colorFilter = colorFilter
invalidateSelf()
}
@Deprecated("Deprecated in Java",
ReplaceWith("PixelFormat.TRANSLUCENT", "android.graphics.PixelFormat")
)
override fun getOpacity(): Int {
return PixelFormat.TRANSLUCENT
}
// 默认大小
override fun getIntrinsicWidth(): Int {
return 500
}
override fun getIntrinsicHeight(): Int {
return 100
}
}
使用:
ini
<TextView
android:id="@+id/customDrawable"
android:text="CustomDrawable: current level = 0"
android:layout_width="match_parent"
android:layout_height="80dp"
android:layout_margin="5dp"
tools:ignore="HardcodedText"
/>
代码:
ini
// 设置自定义drawable
binding.customDrawable.background = CustomStarDrawable()
binding.customDrawable.setOnClickListener {
var level = binding.customDrawable.background.level
level = (level + 2000) % 10000
binding.customDrawable.background.level = level
binding.customDrawable.text = "CustomDrawable: current level = $level"
}
实际效果:
RippleDrawable
RippleDrawable水波纹效果,很多点击会用到(注意V21):
xml
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="@color/blue"
android:radius="100dp"
/>
<!-- color,必须,涟漪的颜色 -->
<!-- radius,涟漪最大的半径 -->
使用:
ini
<TextView
android:id="@+id/rippleDrawable"
android:text="RippleDrawable(click)"
android:background="@drawable/ic_drawable_ripple"
android:layout_width="match_parent"
android:layout_height="80dp"
android:layout_margin="5dp"
android:clickable="true"
android:focusable="true"
tools:ignore="HardcodedText"
/>
效果:
RotateDrawable
RotateDrawable旋转的,没什么好说的,也和level有关:
ini
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/ic_launcher"
android:pivotX="50%"
android:pivotY="50%"
android:fromDegrees="0"
android:toDegrees="180"
android:visible="true"
/>
使用:
ini
<TextView
android:id="@+id/rotateDrawable"
android:text="RotateDrawable: current level = 0"
android:background="@drawable/ic_drawable_rotate"
android:layout_width="match_parent"
android:layout_height="80dp"
android:layout_margin="5dp"
tools:ignore="HardcodedText"
/>
代码:
ini
// 设置旋转drawable
binding.rotateDrawable.setOnClickListener {
var level = binding.rotateDrawable.background.level
level = (level + 2000) % 10000
binding.rotateDrawable.background.level = level
binding.rotateDrawable.text = "RotateDrawable: current level = $level"
}
实际效果:
AnimateStateListDrawable
AnimateStateListDrawable就是selector加动画,类似的还有好多个,可以加帧动画或者vector动画,下面我就简单试了下(注意v21):
xml
<?xml version="1.0" encoding="utf-8"?>
<animated-selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 开启状态 -->
<item
android:id="@+id/state_on"
android:state_pressed="true">
<shape>
<solid android:color="@color/blue"/>
</shape>
</item>
<!-- 关闭状态 -->
<item
android:id="@+id/state_off"
android:state_pressed="false">
<shape>
<stroke android:width="3dp" android:color="@color/gray"/>
</shape>
</item>
<!-- 关闭切换到开启的帧动画 -->
<transition
android:fromId="@id/state_off"
android:toId="@id/state_on">
<animation-list>
<item android:duration="200">
<shape><solid android:color="#000000FF"/> </shape>
</item>
<item android:duration="200">
<shape><solid android:color="#220000FF"/> </shape>
</item>
<item android:duration="200">
<shape><solid android:color="#440000FF"/> </shape>
</item>
<item android:duration="200">
<shape><solid android:color="#660000FF"/> </shape>
</item>
<item android:duration="200">
<shape><solid android:color="#880000FF"/> </shape>
</item>
<item android:duration="200">
<shape><solid android:color="#AA0000FF"/> </shape>
</item>
<item android:duration="200">
<shape><solid android:color="#CC0000FF"/> </shape>
</item>
<item android:duration="200">
<shape><solid android:color="#EE0000FF"/> </shape>
</item>
<item android:duration="200">
<shape><solid android:color="#FF0000FF"/> </shape>
</item>
</animation-list>
</transition>
<!-- 开启切换到关闭的动画 -->
<transition
android:fromId="@id/state_on"
android:toId="@id/state_off">
<animation-list>
<item android:duration="200">
<shape><solid android:color="#FF0000FF"/> </shape>
</item>
<item android:duration="200">
<shape><solid android:color="#EE0000FF"/> </shape>
</item>
<item android:duration="200">
<shape><solid android:color="#CC0000FF"/> </shape>
</item>
<item android:duration="200">
<shape><solid android:color="#AA0000FF"/> </shape>
</item>
<item android:duration="200">
<shape><solid android:color="#880000FF"/> </shape>
</item>
<item android:duration="200">
<shape><solid android:color="#660000FF"/> </shape>
</item>
<item android:duration="200">
<shape><solid android:color="#440000FF"/> </shape>
</item>
<item android:duration="200">
<shape><solid android:color="#220000FF"/> </shape>
</item>
<item android:duration="200">
<shape><solid android:color="#000000FF"/> </shape>
</item>
</animation-list>
</transition>
</animated-selector>
使用:
ini
<TextView
android:id="@+id/animateStateListDrawable"
android:text="AnimateStateListDrawable(press)"
android:background="@drawable/ic_drawable_animate_state_list"
android:layout_width="match_parent"
android:layout_height="80dp"
android:layout_margin="5dp"
android:clickable="true"
android:focusable="true"
tools:ignore="HardcodedText"
/>
实际效果:
其他
好像还有其他几个,adaptive-icon、animated-rotate、animated-vector、animation-list,animated的都类似,后面有时间搞下动画,试试vector动画,adaptive-icon好像是和app图标相关的,用到再看。
小结
这里花了点时间,试了下各种drawable的使用效果,有的有状态,有的和level相关,有的配合动画,还挺有意思,也自定义了一个根据level增加圆圈的drawable,又学习了!
8点11,下班!