hello啊,各位老铁,说实在话,真的是许久不见,屈指一数,这次断更了有些许时日了,没别的理由,就是懒,不想更[捂脸哭],之后尽量会多更一更,目前储备了有一些技术文章了,后续陆陆续续就写起来了,2023,开年第一篇,搞一个无比简单的自定义View吧。
这个自定义View,其实就是一个简单的进度条,无非就是平行四边形形状的,加了渐变以及状态颜色的切换。
今天的内容大致如下:
1、效果及代码具体调用。
2、具体实现过程。
3、开源地址。
4、总结及注意事项。
效果的话,没什么复杂的,就是一个纯粹的平行四边形,无非就是带了一点渐变的颜色如下图所示:
此进度条,比较适用于常见的,里程提示,油量多少等需要的场景,进度条的颜色,进度值,以及告警值,都可以动态设置,为了方便大家使用,大家可以下载源码查看,或者使用Maven依赖也行,如果使用Maven,可以参照下面的调用方式。
1、在你的根项目下的build.gradle文件下,引入maven。
allprojects {
repositories {
maven { url "https://gitee.com/AbnerAndroid/almighty/raw/master" }
}
}
2、在你需要使用的Module中build.gradle文件下,引入依赖。
dependencies {
implementation 'com.vip:mprogress:1.0.2'
}
属性 |
类型 |
概述 |
mp_background |
color |
整体的背景颜色 |
mp_angle_width |
dimension |
平行四边形角度的宽(倾斜度) |
mp_anamorphism_color |
reference |
渐变颜色(传递颜色数组,定义array资源) |
mp_max_progress |
Int |
最大进度 |
mp_default_progress |
Int |
默认进度 |
mp_warn_progress |
Int |
告警进度(低于多少就改变进度颜色) |
mp_warn_background |
color |
告警颜色 |
mp_is_dim_progress |
boolean |
是否模糊颜色(进度前进头的颜色) |
方法 |
参数 |
概述 |
changeProgress |
Int |
传递进度值 |
绘制一个平行四边形,这里采用的是path路径绘制,只需要记录四点坐标就可以了,也就是四个顶点的坐标。
宽度和高度,根据在XML中设置得宽高进行适应,这里需要做一个简单的判断,也就是,如果XML中设置的是wrap_content,我们尽量给一个默认的宽度或高度,让其展示出来。
设置默认的宽度和高度,需要在onMeasure方法中进行模式判断,当宽度或高度取得的模式为MeasureSpec.AT_MOST,我们单独给一个默认的值,代码如下:
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
val widthMode = MeasureSpec.getMode(widthMeasureSpec)
val heightMode = MeasureSpec.getMode(heightMeasureSpec)
var windowWidth = heightMeasureSpec
var windowHeight = heightMeasureSpec
if (widthMode == MeasureSpec.AT_MOST) {
windowWidth = getScreenWidth()
}
if (heightMode == MeasureSpec.AT_MOST) {
windowHeight = mDefaultHeight
}
setMeasuredDimension(windowWidth, windowHeight)
}
这里简单的概述一下,MeasureSpec有三个模式,分别为MeasureSpec.EXACTLY,MeasureSpec.AT_MOST和MeasureSpec.UNSPECIFIED。
MeasureSpec.EXACTLY是精确模式,当我们将控件的layout_width或layout_height指定为具体数值时,如andorid:layout_width="50dp",或者为match_parent时,既控件大小已经确定的情况,都是精确模式。
MeasureSpec.AT_MOST是最大模式,当控件的layout_width或layout_height指定为wrap_content时,控件大小一般随着控件的子空间或内容进行变化,此时控件尺寸只要不超过父控件允许的最大尺寸即可。因此,此时的模式是AT_MOST。
MeasureSpec.UNSPECIFIED是未指定尺寸,这种情况不多,一般都是父控件是AdapterView,通过measure方法传入的模式。
是否要设置一个默认的高度或者宽度,取决于实际的业务情况,一般情况下,使用者都会自己来设置宽和高,至于在此做一层约束,只是为了能让View视图展示出来。
绘制,上述已经说过,找到四个顶点即可,依次进行连接,如下代码所示,mAngleWidth变量为平行四边形的倾斜度。
val path = Path()
path.apply {
moveTo(0f, height.toFloat())//第一个点的位置
lineTo(mAngleWidth, 0f)//第二个
lineTo(width.toFloat(), 0f)//第三个
lineTo((width - mAngleWidth), height.toFloat())//第四个
close()
canvas.drawPath(this, mPaint!!)
}
实现渐变色,也是非常的简单,画笔Paint有个设置的方法setShader,也就是着色器,可以帮助我们实现想要的渐变颜色,Shader有五个子类,分别是:BitmapShader、LinearGradient、RadialGradient、SweepGradient和ComposeShader,具体的就不多介绍了,单独介绍一下LinearGradient。
LinearGradient是用来创建线性渐变效果的,它是沿着某条直线的方向渐变的,有四个构造函数,这里我们选取下面这个构造。
public LinearGradient(float x0, float y0, float x1, float y1,
@NonNull long[] colors, @Nullable float[] positions,
@NonNull TileMode tile) {
}
此构造函数中有七个参数,前四个是坐标点,前两个x0,y0是这条渐变直线的起点,后两个x1,y1是这条渐变直线的终点,第五个参数为int数组colors,也就是需要的渐变颜色,颜色值从开始到结束依次排开即可,第六个参数为位置,和前边的颜色数组是一一对应的,用来标记颜色的位置,最后一个参数是颜色渐变的模式,有三种模式。
TileMode.CLAMP:超出其原始边界,则复制边缘颜色。
TileMode.REPEAT: 重复填充以前的渐变色。
TilmMode.MIRROR :镜像填充。
设置shader,如下代码。
val linearShader = LinearGradient(
0f, 0f, width.toFloat(), height.toFloat(), colorArray,
progressArray, Shader.TileMode.CLAMP
)
mPaint?.shader = linearShader
需要注意的是,是否需要模糊进度,也就是进度前边的一块,我这里我采用的方式是,颜色数组和位置数组都增加两个,第二个位置到三个位置,稍微增加一点position即可实现,比如原来是(0f,1f),目前改为了四个,就成了(0f,0,5f,0,54f,1f)类似这样,当然了第二个进度需要动态处理的,毕竟是一个进度条。
具体的代码实现,大家可以去看源码,没有什么特别复杂的地方,都是比较基础的Api操作。
目前项目已经开源,需要的朋友可以查看:https://github.com/AbnerMing888/Mileage。
如此一个简单的自定义View,需要掌握的就三点,一个是LinearGradient渐变色的处理,一个就是path路径的轨迹,最后一个是MeasureSpec模式的判断,其他的都是最基本的绘制方法,也没有什么可说的,大家看源码或第二步实现过程就可以了。