Android圆角、圆形ImageView的实现方式有很多,这应该是最简单的实现方式,通过裁剪画布canvas,但是clipPath()方法不支持抗锯齿,图片边缘会有明显的毛糙感
class XImageView : ImageView {
private var currentStyle = 0
private val STYLE_ROUND = 1
private val STYLE_CIRCLE = 2
private var radius = 0f
private val path = Path()
private var rectF: RectF = RectF()
constructor(context: Context) : this(context, null)
constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
initAttr(context, attrs)
}
private fun initAttr(context: Context, attrs: AttributeSet?) {
val typedArray = context.obtainStyledAttributes(attrs, R.styleable.XImageView)
radius = typedArray.getDimension(R.styleable.XImageView_radius, radius)
currentStyle = typedArray.getInt(R.styleable.XImageView_showType, STYLE_ROUND)
typedArray.recycle()
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
if (currentStyle == STYLE_CIRCLE) {
val min = Math.min(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.getSize(heightMeasureSpec))
setMeasuredDimension(min, min)
}
rectF.set(0f, 0f, width.toFloat(), height.toFloat())
}
override fun onDraw(canvas: Canvas) {
path.reset()
when (currentStyle) {
STYLE_ROUND -> {
if (radius > 0) {
path.addRoundRect(rectF, radius, radius, Path.Direction.CW)
}
}
STYLE_CIRCLE -> {
val circle = width / 2f
path.addCircle(circle, circle, circle, Path.Direction.CW)
}
}
if (!path.isEmpty) {
canvas.clipPath(path)
}
super.onDraw(canvas)
}
}
另一种方法是使用图像的 Alpha 合成模式,过程就是先绘制目标图像,也就是图片;再绘制一个圆角矩形或者圆形,这样最终目标图像和原图像重合的区域。
class XImageView : ImageView {
private var currentStyle = 0
private val STYLE_ROUND = 1
private val STYLE_CIRCLE = 2
private var radius = 0f
private val path = Path()
private var rectF: RectF = RectF()
private val paint by lazy {
Paint().apply {
isAntiAlias = true
style = Paint.Style.FILL
xfermode = PorterDuffXfermode(PorterDuff.Mode.DST_IN)
}
}
constructor(context: Context) : this(context, null)
constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
initAttr(context, attrs)
}
private fun initAttr(context: Context, attrs: AttributeSet?) {
val typedArray = context.obtainStyledAttributes(attrs, R.styleable.XImageView)
radius = typedArray.getDimension(R.styleable.XImageView_radius, radius)
currentStyle = typedArray.getInt(R.styleable.XImageView_showType, STYLE_ROUND)
typedArray.recycle()
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
if (currentStyle == STYLE_CIRCLE) {
val min = Math.min(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.getSize(heightMeasureSpec))
setMeasuredDimension(min, min)
}
rectF.set(0f, 0f, width.toFloat(), height.toFloat())
}
override fun onDraw(canvas: Canvas) {
path.reset()
canvas.saveLayer(rectF, null, Canvas.ALL_SAVE_FLAG)
super.onDraw(canvas)
when (currentStyle) {
STYLE_ROUND -> {
if (radius > 0) {
path.addRoundRect(rectF, radius, radius, Path.Direction.CW)
}
}
STYLE_CIRCLE -> {
val circle = width / 2f
path.addCircle(circle, circle, circle, Path.Direction.CW)
}
}
canvas.drawPath(path, paint)
canvas.restore()
}
}