Android自定义View分享——一个水平的进度条

写在前面

笔者近来在学习Android自定义View,收集了一些不算复杂但又“长得”还可以的自定义View效果实现,这些View的逻辑不算复杂,大多都只用到了Paint、Canvas类的一些常用的API。在后续的博客里面,将分享几个不同的效果,本文作为第一篇,先来一个很简单的——一个水平进度条(跟系统的那个不同)。

本文适合什么样的人

如果你接触自定义View不久,看懂了View绘制基本流程,知道onMeasured()、onLayout()、onDraw(),知道Paint、Canvas类及其API很强大但没用过,正准备大显身手来几个自定义View却又不知道该“画”一个怎么样自定义View比较合适,苦恼于网上各种开源项目太高端,看不懂,那么本文及后续的博客很适合你,来让我们一起拿起铅笔(Paint)、白纸(Canvas)来当一回艺术家呗。

效果展现

看这就是我们要的效果,朝两边滚动的进度条。
TwoSideProgressBar图片展示

设计图

作为一个艺术家(码农),当然要先有设计图,才能“画”出一个靠谱的自定义View,你要认真看我的设计图,尽管文章最后面会给出完整代码地址,不过如果你认真看设计图,完全可以自己画哦~~

第一个情况设计图

第一个情况设计图
好了根据设计图能看到:

  • 我们的View以控件的中间为分界线,分别向左右两边滚动。
  • 每个bar长度都是一样的,除了左右两边的第一个。另外相邻bar之间的间距也是一样。
  • 我们需要关注三个参数,第一个bar的宽度,其他普通bar的宽度,两个bar之间的间隔。

我们只要绘制一边,另一边是对称的,所以绘制的逻辑是:

  1. 从中间开始,先绘制第一个较特殊的bar,然后间隔barSpace距离,绘制第二个宽度为barWidth的bar,重复,直到你的绘制位置超出控间的右边界。
  2. 绘制完成,同样操作对左边也绘制一次,注意传入API里面的关于坐标相关的参数。此时完成一次绘制,注意了只是一次,我们需要将firstBarWidth变大一些,然后调用invalidate()方法重绘,这样才能显示出动态的效果。

代码片段如下所示:

//第一个"bar"宽度小于普通"bar",注意我的坐标系原点在控件中间
if(firstBarWidth<=barWidth){
	//绘制右边第一个“条”
	//index是绘制索引
	int index = 0;
	canvas.drawLine(index, 0, firstBarWidth, 0, barPaint);
	index+=firstBarWidth+barSpace;
	//循环绘制右边其它普通的“条”
	while (index <= measuredWidth/2){
		canvas.drawLine(index, 0, index+barWidth, 0, barPaint);
		index+=barWidth+barSpace;
	}
	//绘制左边第一个“条”
	index = 0;
	canvas.drawLine(-firstBarWidth, 0, index, 0, barPaint);
	index-=(firstBarWidth+barSpace);
	//循环绘制左边其它普通的“条”
	while (index >= -measuredWidth/2){
		canvas.drawLine(index-barWidth, 0, index, 0, barPaint);
		index-=(barWidth+barSpace);
	}
	firstBarWidth+=barWidth/10;
}

第二个情况设计图

为什么会有两种情况,你是否想过,当firstBarWidth(第一个bar的宽度)达到普通的bar宽度时,该怎么办?直接将变量重置为0,从头画过?绝对不是,仔细想想你会发现,如果你这样做的话,会让滚动条有一个被“抽”一下的感觉,这种思考是错误的,那么正确的是怎样,来看第二个设计图
第一个bar宽度达到普通宽度时的情况
当第一个bar达到了普通bar的宽度,我们就要执行另外一个绘制逻辑:

  1. 从绘制起点开始,先间隔一小段的距离(长度为firstBarDistance),才开始绘制第一个bar。
  2. 然后间隔barWidth距离,绘制第二个,重复,直到绘制起点超过控件边界。这样就完成了一次绘制,类似于第一个情况,为了体现动态效果,我们需要将firstBarDistance变大一些,然后调用invalidate()方法重绘,这样才能显示出动态的效果。
  3. 问题在于,firstBarDistance达到什么程度才好,其实当他的值等于barSpace时,就是第二个情况的临界点了,在下一刻,就回到了第一个情况的起点,此时只要重置所有计算参数就可以。

代码片段如下所示:

//第一个"bar"宽度达到普通的"bar"宽度时
//循环绘制右边的“条”
float index = firstBarDistance;
while (index <= measuredWidth/2){
	canvas.drawLine(index, 0, index+barWidth, 0, barPaint);
	index+=barWidth+barSpace;
}
//循环绘制左边的“条”
index = -firstBarDistance;
while (index >= -measuredWidth/2){
	canvas.drawLine(index-barWidth, 0, index, 0, barPaint);
	index-=(barWidth+barSpace);
}
firstBarDistance+=barWidth/10;
//到达临界点,全部重置
if(firstBarDistance > barSpace) {
	firstBarDistance = barWidth/10;
	firstBarWidth = barWidth/10;
}

注意一下

针对我们这个View的特点,在你正式绘图之前,我提一下:

  • 在onDraw()里面,在所有的绘图操作执行之前,调用一下这个方法:
canvas.translate(measuredWidth/2, getMeasuredHeight()/2);

让坐标系移到中间,可以简化一些计算。

  • 提前看看canvas.drawLine()方法,在画线时不要传错参数了哦。
  • invalidate()方法可以引发控间重绘,如果你想控制重绘频率,postInvalidateDelayed();方法是个不错的替代品。
  • 没错,除了一些数学计算之外,我们就仅仅用到了Paint对象基本创建,Canvas类的drawLine()方法、translate()方法,就是这么简单啦。

项目源码:
https://github.com/kingfarou/SimpleCustomView

下期预告

下篇文章我将分享这样子的效果,一个圆形的温度显示器
温度显示器

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值