写点什么

Android 中绘制圆角的三种方式

用户头像
teoking
关注
发布于: 2021 年 05 月 06 日

Android 中绘制圆角的三种方式

在 iOS 平台中,给 View 指定圆角是很方便的。只需要在代码中设置layer.cornerRadius即可。但在 Android 平台中,事情却有些复杂:


  • 5.0 之前,需要以背景贴图、PathCornerPathEffect来实现圆角

  • 5.0 之后,新增ViewOutlineProvider 也可以来实现圆角

demo 效果

demo 效果图如下:



demo 完整代码地址:ImageCornerDemo

实现方式

下面我们就分别来看一下如何基于Path, CornerPathEffectViewOutlineProvider 这三个 API 实现圆角。

方法一:基于Path的实现

internal class ShaderRoundCornerImage(context: Context) : CornerResizeView(context) {
private var radii: FloatArray = floatArrayOf( 20f, 20f, 20f, 20f, 20f, 20f, 20f, 20f )
@RequiresApi(Build.VERSION_CODES.LOLLIPOP) override fun onDraw(canvas: Canvas) { drawPath(canvas) }
// Draw the round corner path and the image @RequiresApi(Build.VERSION_CODES.LOLLIPOP) private fun drawPath(canvas: Canvas) { path.reset() path.addRoundRect(bounds, radii, Path.Direction.CW) // Step 1 canvas.drawPath(path, paint) // Step 2 }
override fun changeCorner(percent: Int) { val radius = width * percent / 100f radii.fill(radius) invalidate() // Step 3 }}
复制代码


代码说明:


  • Step 1: 给 path 指定一个圆角矩形,radii 是一个包含四组[X, Y]的数组,共八个数

  • Step 2: 将 path 绘制到 canvas 上

  • Step 3: 更新 view

方法二:基于ViewOutlineProvider 的实现

// Step 1private class ClipOutlineProvider(var radius: Float) : ViewOutlineProvider() {    override fun getOutline(view: View, outline: Outline) {        outline.setRoundRect(            0, 0, view.width, view.height, radius        )    }}
internal class OutlineRoundCornerImage(context: Context) : CornerResizeView(context) { private val clipOutlineProvider = ClipOutlineProvider(20f) // Step 2
init { outlineProvider = clipOutlineProvider // Step 3 clipToOutline = true // Step 4 }
override fun changeCorner(percent: Int) { clipOutlineProvider.radius = width * percent / 100f invalidateOutline() // Step 5 }}
复制代码


代码说明:


  • Step 1: 实现一个圆角ViewOutlineProvider

  • Step 2: 创建实例并指定圆角弧度

  • Step 3: 指定 outlineProvider

  • Step 4: 设置为 true 表示使用该 outline 来裁切 view 的内容

  • Step 5: 更新 outline


需要注意的是,outline 需要使用invalidateOutline 来请求更新

方法三:基于CornerPathEffect来实现

internal class CornerEffectCornerImage(context: Context) : CornerResizeView(context) {
private var cornerEffect = CornerPathEffect(20f)
override fun onDraw(canvas: Canvas) { // Install the corner effect to the drawable's paint drawable.paint.pathEffect = cornerEffect // Step 1 // Set the bitmap shader to the drawable's paint drawable.paint.shader= shader drawable.setBounds(0, 0,width,height) drawable.draw(canvas) }
override fun changeCorner(percent: Int) { // Percent to radius value val radius =width* percent / 100f
// Update effect with new radius cornerEffect = CornerPathEffect(radius) // Step 2
invalidate() // Step 3 }}
复制代码


代码说明:


  • Step 1: 给 drawable 的 paint 指定 cornerEffect

  • Step 2: 重建 cornerEffect

  • Step 3: 更新 view

性能

Outline 方式


Path 方式


CornerEffect 方式


通过 HWUI 呈现模式分析 可以看出,三种实现方式,性能上差异不大(Outline 方式性能应该更高一些)。


总结

  • 通常情况下,Path/ViewOutlineProvider/CornerEffect三种方式性能相当,也都可以在 Android 5.0+版本中正常工作(无论 hardware/software layer)

  • 当 View 的圆角需要频繁变化时,ViewOutlineProvider方式性能更高,因为它的invalidateOutline 函数只会让 outline 重建和更新,而不是整个 view(invalidate 会更新整个 view)

  • 从功能上,Path/ViewOutlineProvider是比较接近的,后者也支持setPath,这样就可以实现复杂的边框裁剪

  • CornerEffectPathEffect的继承者,更适合于实现复杂的边框效果,比如虚线(DashPathEffect)、离散线(DiscretePathEffect)等

发布于: 2021 年 05 月 06 日阅读数: 18
用户头像

teoking

关注

Monkey plays software. 2018.11.28 加入

程序员。目前主要从事Android和iOS开发。

评论

发布
暂无评论
Android中绘制圆角的三种方式