这篇文章的目的便是介绍一个通用的圆角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>