作为一个移动端工程师,见过太多的设计稿,圆角设计总是经常出现在我们视野里,圆角能带来比较强烈的视觉冲击,更能引起肉眼的注意,另外一个层面来说,也会让UI设计更美观
在最近项目中,遇到一个比较巧妙的控件设计,该控件主要用于温度和时间段的选择,控件设计如下图
1
圆角绘制
接下来进入正题,那么Android自定义View中,有哪些方法可以实现圆角设计呢?我们以上图为例进行讨论。
直接绘制
最常见的可以直接使用Canvas提供的drawRoundRect方法实现圆角,方法申明如下:
drawRoundRect(RectF rect, float rx, float ry, Paint paint);
需要提供一个矩形rect,该rect是你圆角矩形的位置以及大小,rx,ry为圆角大小,paint为画笔,完成的效果图如下:
实现的思路为,先绘制一个圆角矩形,再绘制一个矩形叠加在上面,完美实现,beautiful!
示例代码如下:
拼接绘制
要是你觉得有一部分是重复绘制的觉得不爽,那么换个思路,用拼接实现,先在左边绘制一个半圆,然后绘制一个矩形,再绘制右边半圆,绘制半圆的API如下:
drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter,Paint paint)
oval是你半圆的位置和大小,startAngle为半圆开始的位置,sweepAngle为半圆圆弧大小,useCenter为画笔在STROKE模式下是否连接圆心,这里肯定使用FILL模式,paint为画笔,实现的效果和上图是一样的,代码如下:
路径绘制
当然除了上面的方法以外,还可以利用路径实现,在自定义View中路径可以实现很多不规则图形的绘制,先上个草图:
我们可以使用3个路径来实现各自的部分,然后统一绘制路径,涉及Path类的使用,具体API不介绍,代码如下:
这次效果图我们使用线框方式展示
画笔笔触绘制
到这里想必大部分人已经江郎才尽了,应该没有别的方法了吧?哈哈,转换下思路如果这个圆角矩形看成一条很粗的线呢?
其实就是两条线堆在了一起???
实现的效果还是一样的,代码如下:
你以为到这里就结束了?事情总是没有那么简单的。
2
拖拽
当然这种情况我们经常在一些类似下载进度的控件中见过,如下图
当进度很小的时候,最左边只展示了一小段进度,那么这样的圆角我们又该怎样实现呢?接下来就是进阶圆角绘制了。
三角函数实现
如上图,滑动到的位置我们是可以通过数据计算出来的,所以图中b的长度我们是知道的,r的长度也知道,以此可以计算出α角的大小,蓝色部分圆弧的角度就是2*α,然后我们通过Path拼接路径就能绘制出来了,右边白色部分同样的道理计算角度,拼接路径,此方法需要一定数学功底。
PorterDuffXfermode
其实在Android自定义View中实现圆角还有更骚气的方法,PorterDuffXfermode自带高大上光环,很多人可能都听说过,但是具体不是很清楚用法,PorterDuffXfermode是什么?叫它混合模式也行过度模式也行!能做些什么呢?
我们先来看一张API DEMO里的图片:
这张图片从一定程度上形象地说明了图形混合的作用,两个图形一圆一方通过一定的计算产生不同的组合效果,在API中Android为我们提供了18种(比上图多了两种ADD和OVERLAY)模式:
ADD:饱和相加,对图像饱和度进行相加,不常用
CLEAR:清除图像
DARKEN:变暗,较深的颜色覆盖较浅的颜色,若两者深浅程度相同则混合
DST:只显示目标图像
DST_ATOP:在源图像和目标图像相交的地方绘制,在不相交的地方绘制,相交处的效果受到源图像和目标图像alpha的影响
DST_IN:只在源图像和目标图像相交的地方绘制,绘制效果受到源图像对应地方透明度影响
DST_OUT:只在源图像和目标图像不相交的地方绘制,在相交的地方根据源图像的alpha进行过滤,源图像完全不透明则完全过滤,完全透明则不过滤
DST_OVER:将目标图像放在源图像上方
LIGHTEN:变亮,与DARKEN相反,DARKEN和LIGHTEN生成的图像结果与Android对颜色值深浅的定义有关
MULTIPLY:正片叠底,源图像素颜色值乘以目标图像素颜色值除以255得到混合后图像像素颜色值
OVERLAY:叠加
SCREEN:滤色,色调均和,保留两个图层中较白的部分,较暗的部分被遮盖
SRC:只显示源图像
SRC_ATOP:在源图像和目标图像相交的地方绘制,在不相交的地方绘制,相交处的效果受到源图像和目标图像alpha的影响
SRC_IN:只在源图像和目标图像相交的地方绘制
SRC_OUT:只在源图像和目标图像不相交的地方绘制,相交的地方根据目标图像的对应地方的alpha进行过滤,目标图像完全不透明则完全过滤,完全透明则不过滤
SRC_OVER:将源图像放在目标图像上方
XOR:在源图像和目标图像相交的地方之外绘制它们,在相交的地方受到对应alpha和色值影响,如果完全不透明则相交处完全不绘制
但是这里需要注意的一点就是,这种方式使用的情况是源图像和目标图像是Bitmap,在APIdemo里面是生成对应的Bitmap进行混合的,试过在canvas中新增图层方式绘制,但是没有起作用,那么说了这么多,我们怎么利用这个特点呢?
那么现在可以完全先不管这个圆角,我们使用绘制矩形的方式绘制完中间这些颜色块,但是需要抽离出方法返回Bitmap,因为最终的混合是由两个Bitmap完成的,方法如下:
这里故意让红色块临近左边,为了后面清晰的看到结果,绘制结果如下:
接下来我们再绘制一个同样大小的圆角矩形Bitmap
现在我们有了源Bitmap和目标Bitmap,我们先不用混合模式直接绘制两个Bitmap看看是什么效果:
嗯!还不错,接下来进行混合,混合代码如下:
可以看到使用了DSTP_IN混合方式,实现的效果如下图:
如图中线框部分,完美实现了效果,beautiful!
其实到这里还没有结束!
因为在项目中,我没有采取上面混合的方式(因为我嫌麻烦),而是使用了Canvas画布裁剪实现的,在Android中Canvas可以对自身进行裁剪,裁剪完成后,接下来的绘制只能在裁剪后的画布中进行,画布以外的区域都不可见,根据该特性,我们是不是可以先裁剪出一个圆角画布,然后按照矩形正常绘制呢?答案是肯定的。
利用上面的getSrcBitmap方法生成的Bitmap来实现(偷懒一下),代码如下:
总结
到这里可以说已经基本介绍完Android自定义View中实现圆角的大部分方法,最主要的还是看具体的需求,如果仅仅只是需要一个圆角的背景,直接使用简单的绘制就行,设计到复杂的交互和展示的时候,或许就需要变通思路来实现了