注册

一个通用的圆角View

这篇文章的目的就是介绍一个通用的圆角View,可以根据圆角的复杂度来灵活切换实现方式。


本自定义View可实现的效果如下:



之前的文章中介绍了实现圆角的各种方式,也比较了各种方案的优势和局限。


圆角实现方式汇总


实际使用中发现在一些简单场景中针对只需要实现上圆角或者下圆角的场景,或者所有圆角一致的需求中,我们使用性能更高的outlineProvider实现是最佳选择,但是在复杂需求中,比如上下左右圆角弧度不一致,这种时候我们要实现的话就需要切换实现方案。


二、源码


1.自定义属性


<declare-styleable name="RoundCornerLayout">
<attr name="topCornerRadius" format="dimension|reference" />
<attr name="topCornerRadiusLeft" format="dimension|reference" />
<attr name="topCornerRadiusRight" format="dimension|reference" />
<attr name="bottomCornerRadius" format="dimension|reference" />
<attr name="bottomCornerRadiusLeft" format="dimension|reference" />
<attr name="bottomCornerRadiusRight" format="dimension|reference" />
<attr name="cornerMode" format="string" >
<enum name="outline" value ="0"/>
<enum name="xfermode" value ="1"/>
<enum name="clip_path" value ="2"/>
</attr>
</declare-styleable>

cornerMode用于选择实现方式,可选实现方式有


outline:


支持同时设置四个圆角以及单独设置上圆角或者下圆角,但所有圆角弧度必须相同,不支持单独配置


性能:绘制性能最优,暂未发现兼容和锯齿问题


xfermode:


支持四个圆角单独设置和同时设置


性能:性能稍差,同时抗锯齿效果比clippath会好一些


clippath:


支持四个圆角单独设置和同时设置,实现最灵活。


性能:性能稍差,同时低版本机型锯齿明显,同时和硬件加速有兼容问题,部分机型存在渲染闪烁了切割黑屏


outline的实现方式需要配置 topCornerRadius或者bottomCornerRadius即可


xfermode和clippath的实现方式则需要根据上下左右四个圆角分别配置


2.自定义圆角View


class RoundCornerLayout @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : FrameLayout(context, attrs, defStyleAttr) {
companion object {
private const val TAG = "RoundCornerLayout"
private const val CORNER_MODE_OUTLINE = 0
private const val CORNER_MODE_XFERMODE = 1
private const val CORNER_MODE_CLIPPATH = 2
}

private var cornerMode = 0
private var topCornerRadius = 0
private var topCornerRadiusLeft = 0
private var topCornerRadiusRight = 0
private var bottomCornerRadius = 0
private var bottomCornerRadiusLeft = 0
private var bottomCornerRadiusRight = 0

private var mRoundRectPath = Path()
private var mPaint = Paint()
private val mRect = RectF()
private var roundedCorners = FloatArray(8)
private var maskBitmap: Bitmap? = null

init {
val typedArray =
context.obtainStyledAttributes(attrs, R.styleable.RoundCornerLayout, defStyleAttr, 0)
cornerMode =
typedArray.getInt(
R.styleable.RoundCornerLayout_cornerMode,
CORNER_MODE_OUTLINE
)
topCornerRadius =
typedArray.getDimensionPixelSize(R.styleable.RoundCornerLayout_topCornerRadius, 0)
topCornerRadiusLeft =
typedArray.getDimensionPixelSize(R.styleable.RoundCornerLayout_topCornerRadiusLeft, 0)
topCornerRadiusRight =
typedArray.getDimensionPixelSize(R.styleable.RoundCornerLayout_topCornerRadiusRight, 0)
bottomCornerRadius =
typedArray.getDimensionPixelSize(R.styleable.RoundCornerLayout_bottomCornerRadius, 0)
bottomCornerRadiusLeft =
typedArray.getDimensionPixelSize(R.styleable.RoundCornerLayout_bottomCornerRadiusLeft, 0)
bottomCornerRadiusRight =
typedArray.getDimensionPixelSize(R.styleable.RoundCornerLayout_bottomCornerRadiusRight, 0)
typedArray.recycle()
mPaint.isAntiAlias = true
updateRoundRectMode()
}

private fun setRoundRectPath() {
roundedCorners[0] = topCornerRadiusLeft.toFloat()
roundedCorners[1] = topCornerRadiusLeft.toFloat()
roundedCorners[2] = topCornerRadiusRight.toFloat()
roundedCorners[3] = topCornerRadiusRight.toFloat()
roundedCorners[4] = bottomCornerRadiusLeft.toFloat()
roundedCorners[5] = bottomCornerRadiusLeft.toFloat()
roundedCorners[6] = bottomCornerRadiusRight.toFloat()
roundedCorners[7] = bottomCornerRadiusRight.toFloat()
mRect.set(0f, 0f, width.toFloat(), height.toFloat())
mRoundRectPath.rewind()
mRoundRectPath.addRoundRect(mRect, roundedCorners, Path.Direction.CW)
}


private fun setOutlineMode() {//讨巧上下多截出去一点,达到只有上圆角或者下圆角,实际还是一致的圆角
when {
topCornerRadius != 0 && bottomCornerRadius == 0 -> {
clipToOutline = true
outlineProvider = object : ViewOutlineProvider() {
override fun getOutline(view: View, outline: Outline) {
outline.setRoundRect(
Rect(0, 0, view.width, view.height + topCornerRadius),
topCornerRadius.toFloat()
)
}
}
}
topCornerRadius == 0 && bottomCornerRadius != 0 -> {
clipToOutline = true
outlineProvider = object : ViewOutlineProvider() {
override fun getOutline(view: View, outline: Outline) {
outline.setRoundRect(
Rect(0, 0 - bottomCornerRadius, view.width, view.height),
bottomCornerRadius.toFloat()
)
}
}
}
topCornerRadius != 0 && bottomCornerRadius != 0 && bottomCornerRadius == topCornerRadius -> {
clipToOutline = true
outlineProvider = object : ViewOutlineProvider() {
override fun getOutline(view: View, outline: Outline) {
outline.setRoundRect(
Rect(0, 0, view.width, view.height),
topCornerRadius.toFloat()
)
}
}
}
}
}

private fun updateRoundRectMode() {
when (cornerMode) {
CORNER_MODE_OUTLINE -> {
setOutlineMode()
}
else -> clipToOutline = false
}
}

override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
when (cornerMode) {
CORNER_MODE_XFERMODE -> {
maskBitmap?.recycle()
maskBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888).apply {
val canvasTmp = Canvas(this)
setRoundRectPath()
canvasTmp.drawPath(mRoundRectPath, mPaint)
}
}
CORNER_MODE_CLIPPATH -> {
setRoundRectPath()
}
}
}

override fun dispatchDraw(canvas: Canvas?) {
when (cornerMode) {
CORNER_MODE_CLIPPATH -> {
canvas?.clipPath(mRoundRectPath) //切割指定区域
super.dispatchDraw(canvas)
}
CORNER_MODE_XFERMODE -> {
val layerId = canvas?.saveLayer(mRect, mPaint) ?: -1
super.dispatchDraw(canvas)
mPaint.xfermode = PorterDuffXfermode(PorterDuff.Mode.DST_IN) //设置图层混合模式
maskBitmap?.run {
canvas?.drawBitmap(this, 0f, 0f, mPaint)
}
mPaint.xfermode = null
canvas?.restoreToCount(layerId)
}
else -> {
super.dispatchDraw(canvas)
}
}
}
}

三、使用


本文开头的圆角效果就是下面这段代码实现的:


<com.ui.RoundCornerLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_marginTop="20dp"
android:layout_marginRight="20dp"
app:topCornerRadius="@dimen/roundRectCornerTop">

<TextView
android:layout_width="match_parent"
android:layout_height="50dp"
android:text="outline"
android:background="@android:color/holo_blue_light"
android:gravity="center" />
</com.ui.RoundCornerLayout>

<com.ui.RoundCornerLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_marginTop="20dp"
android:layout_marginRight="20dp"
app:bottomCornerRadius="@dimen/roundRectCornerTop">

<TextView
android:layout_width="match_parent"
android:layout_height="50dp"
android:text="outline"
android:background="@android:color/holo_blue_light"
android:gravity="center" />
</com.ui.RoundCornerLayout>

<com.ui.RoundCornerLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_marginTop="20dp"
android:layout_marginRight="20dp"
app:bottomCornerRadius="@dimen/roundRectCornerTop"
app:topCornerRadius="@dimen/roundRectCornerTop">

<TextView
android:layout_width="match_parent"
android:layout_height="80dp"
android:text="outline"
android:background="@android:color/holo_blue_light"
android:gravity="center" />
</com.ui.RoundCornerLayout>

<com.ui.RoundCornerLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_marginTop="20dp"
android:layout_marginRight="20dp"
app:cornerMode="xfermode"
app:topCornerRadiusLeft="@dimen/roundRectCornerTop"
app:topCornerRadiusRight="@dimen/roundRectCornerTop"
app:bottomCornerRadiusLeft="0dp"
app:bottomCornerRadiusRight="@dimen/roundRectCornerTop2"
>
<TextView
android:layout_width="match_parent"
android:layout_height="80dp"
android:text="xfermode"
android:background="@android:color/holo_blue_light"
android:gravity="center" />
</com.ui.RoundCornerLayout>
<com.ui.RoundCornerLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_marginTop="20dp"
android:layout_marginRight="20dp"
app:cornerMode="clip_path"
app:topCornerRadiusLeft="0dp"
app:topCornerRadiusRight="@dimen/roundRectCornerTop"
app:bottomCornerRadiusLeft="0dp"
app:bottomCornerRadiusRight="@dimen/roundRectCornerTop2"
>
<TextView
android:layout_width="match_parent"
android:layout_height="80dp"
android:text="clippath"
android:background="@android:color/holo_blue_light"
android:gravity="center" />
</com.ui.RoundCornerLayout>

作者:zzy的学习笔记
链接:https://juejin.cn/post/7222079859710115877
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

0 个评论

要回复文章请先登录注册