UI
会设计成圆角矩形或圆形效果。本文将介绍几种常见的实现方式,并提供对应的代码示例。
ViewOutlineProvider 是 Android 5.0 引入的一个类,用来定义视图的轮廓(outline
)。可以通过它来实现圆角矩形、椭圆、圆形等效果。
//设置成扩展方法
fun View.clipToRoundView(type: Int = RoundImgView.SHAPE_ROUND_RECT) {
if (Build.VERSION.SDK_INT >= 21) {
outlineProvider = object : ViewOutlineProvider() {
override fun getOutline(view: View?, outline: Outline?) {
if (view == null) return
if (type == RoundImgView.SHAPE_ROUND_RECT) {
//设置一个矩形的轮廓,并指定其圆角半径
outline?.setRoundRect(0, 0, view.width, view.height, 15.dp2px().toFloat())
} else {
//设置成椭圆或者圆形
outline?.setOval(0, 0, view.width, view.height)
}
}
}
//视图会根据outlineProvider提供的轮廓进行裁剪。任何超出轮廓的部分都会被裁剪掉
clipToOutline = true
}
}
可以看到通过outline
中的setRoundRect/setOval
方法来设置圆角矩形、椭圆等形状,还有其他API方法可以自行了解。在Activity中使用:
private val mIvTarget: AppCompatImageView by id(R.id.iv_round_img)
private val mIvTarget2: AppCompatImageView by id(R.id.iv_round_img2)
val bitmap = BitmapFactory.decodeResource(resources, R.drawable.icon_cat_w)
mIvTarget.clipToRoundView(RoundImgView.SHAPE_ROUND_RECT)
mIvTarget.setImageBitmap(bitmap)
mIvTarget2.clipToRoundView(RoundImgView.SHAPE_CIRCLE)
mIvTarget2.setImageBitmap(bitmap)
效果如下:
Glide 是一个强大的图片加载库,通过它的 RequestOptions 可以轻松实现图片的圆角处理。
//1、图片设置的不是CenterCrop
Glide.with(this)
.load(R.drawable.icon_cat_w)
.transform(RoundedCorners(16.dp2px()))
.into(mIvTarget)
//2、如果图片设置的是CenterCrop,可能会导致圆角效果被 CenterCrop 操作覆盖,最终看不到圆角效果,需要用下面的方式处理CenterCrop与圆角矩形冲突问题
val requestOptions = RequestOptions().transform(CenterCrop(), RoundedCorners(16.dp2px()))
Glide.with(this)
.load(R.drawable.icon_cat_w)
.apply(requestOptions)
.into(mIvTarget)
自定义 ImageView 并重写 onDraw() 方法,通过 Canvas.clipPath() 实现图片的圆角矩形和圆形效果。
/**
* 通过clipPath的方式实现圆角矩形和圆形图片
*
* @param context
* @param attrs
* @param defStyleAttr
*/
class RoundImgView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : AppCompatImageView(context, attrs, defStyleAttr) {
companion object {
const val SHAPE_ROUND_RECT = 0
const val SHAPE_CIRCLE = 1
}
private val path = Path()
private val strokePath = Path()
private var cornerRadius = 10.dp2px().toFloat()
private var mStrokeWidth = 10.dp2px().toFloat()
private var mShapeType = SHAPE_ROUND_RECT
private var isHasStroke = false //是否设置描边
// 创建一个画笔
val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
color = Color.BLUE
style = Paint.Style.STROKE
strokeWidth = mStrokeWidth
}
override fun onDraw(canvas: Canvas) {
//canvas.drawColor(Color.RED)
//设置描边
if (isHasStroke) {
processPath(strokePath) //设置path
canvas.save()
canvas.drawPath(strokePath, paint)
canvas.restore()
}
processPath(path)
canvas.clipPath(path)
super.onDraw(canvas)
}
private fun processPath(path: Path) {
if (mShapeType == SHAPE_CIRCLE) {
//圆形
val radius = (maxOf(width, height) - mStrokeWidth) / 2f
path.addCircle(width / 2f, height / 2f, radius, Path.Direction.CW)
} else {
//圆角矩形
path.addRoundRect(
mStrokeWidth, mStrokeWidth,
width.toFloat() - mStrokeWidth,
height.toFloat() - mStrokeWidth,
floatArrayOf(
cornerRadius, cornerRadius, cornerRadius, cornerRadius,
cornerRadius, cornerRadius, cornerRadius, cornerRadius
),
Path.Direction.CW
)
}
}
/**
* 设置描边宽度
* @param width 宽度
*/
fun setStrokeWidth(width: Float): RoundImgView {
this.mStrokeWidth = width
paint.strokeWidth = mStrokeWidth
isHasStroke = true
return this
}
/**
* 设置圆角半径
* @param radius 半径
*/
fun setCornerRadius(radius: Float): RoundImgView {
cornerRadius = radius
return this
}
/**
* 设置图片类型
* @param type
*/
fun setShapeType(type: Int): RoundImgView {
this.mShapeType = type
return this
}
override fun setImageBitmap(bm: Bitmap?) {
super.setImageBitmap(bm)
}
}
Activity中使用:
private val mIvCustomImg: RoundImgView by id(R.id.iv_custom_img)
private val mIvCustomImg2: RoundImgView by id(R.id.iv_custom_img2)
val bitmap = BitmapFactory.decodeResource(resources, R.drawable.icon_cat_w)
mIvCustomImg.setCornerRadius(15.dp2px().toFloat())
.setShapeType(RoundImgView.SHAPE_ROUND_RECT)
.setStrokeWidth(10f)
.setImageBitmap(bitmap)
mIvCustomImg2.setCornerRadius(15.dp2px().toFloat())
.setShapeType(RoundImgView.SHAPE_CIRCLE)
.setStrokeWidth(10f)
.setImageBitmap(bitmap)
效果图:
通过 CardView 的 app:cardCornerRadius
属性,可以非常方便地实现圆角效果。
<androidx.cardview.widget.CardView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="50dp"
app:cardCornerRadius="10dp">
<androidx.appcompat.widget.AppCompatImageView
android:layout_width="150dp"
android:layout_height="150dp"
android:scaleType="centerCrop"
android:src="@drawable/icon_cat_w" />
</androidx.cardview.widget.CardView>
BitmapShader 是 Android 中的一种着色器,通过它可以实现自定义的圆角和圆形图片。
/**
* @param bitmap bitmap
* @param outWidth 输出的宽
* @param outHeight 输出的高
* @param radius 半径
* @param border 描边
* @param shapeType 图形类型:圆角矩形 or 圆形
*/
private fun getBitmapByShader(
bitmap: Bitmap?,
outWidth: Int,
outHeight: Int,
radius: Int,
border: Int,
shapeType: Int = RoundImgView.SHAPE_ROUND_RECT
): Bitmap? {
if (bitmap == null || bitmap.height <= 0 || bitmap.width <= 0) {
return null
}
//缩放比例
val scale = minOf(outWidth.toFloat() / bitmap.width, outHeight.toFloat() / bitmap.height)
//创建矩阵
val matrix = Matrix().apply {
setScale(scale, scale)
}
//创建shader
val shader = BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP).apply {
setLocalMatrix(matrix)
}
//通过shader着色器来绘制图像
val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
this.shader = shader
}
return Bitmap.createBitmap(outWidth, outHeight, Bitmap.Config.ARGB_8888).also { output ->
Canvas(output).apply {
val rect = RectF(
border.toFloat(),
border.toFloat(),
(outWidth - border).toFloat(),
(outHeight - border).toFloat()
)
if (shapeType == RoundImgView.SHAPE_ROUND_RECT) {
//绘制圆角矩形
drawRoundRect(rect, radius.toFloat(), radius.toFloat(), paint)
} else {
//绘制圆形
drawCircle(outWidth / 2f, outHeight / 2f, outWidth / 2f, paint)
}
if (border > 0) {
//如果有描边,绘制描边
val strokePaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
color = Color.RED
style = Paint.Style.STROKE
strokeWidth = border.toFloat()
}
if (shapeType == RoundImgView.SHAPE_ROUND_RECT) {
//绘制圆角矩形
drawRoundRect(rect, radius.toFloat(), radius.toFloat(), strokePaint)
} else {
//绘制圆形
drawCircle(outWidth / 2f, outHeight / 2f, (outWidth - border) / 2f, strokePaint)
}
}
}
}
}
Activity中使用:
val bitmap = BitmapFactory.decodeResource(resources, R.drawable.icon_cat_w)
val targetBitmap = getBitmapByShader(bitmap, 200.dp2px(), 200.dp2px(), 20.dp2px(), 5.dp2px(), RoundImgView.SHAPE_ROUND_RECT)
mIvTarget.setImageBitmap(targetBitmap)
或者在自定义Drawable中使用BitmapShader:
class RoundDrawable(val bitmap: Bitmap, val targetWH: Float) : Drawable() {
private val paint = Paint().apply {
//缩放比例
val scale = minOf(targetWH / bitmap.width, targetWH / bitmap.height)
//创建矩阵
val matrix = Matrix().apply {
setScale(scale, scale)
}
isAntiAlias = true
shader = BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP).apply {
setLocalMatrix(matrix)
}
}
override fun draw(canvas: Canvas) {
val rectF = RectF(0f, 0f, targetWH, targetWH)
canvas.drawRoundRect(rectF, 20f, 20f, paint)
}
override fun setAlpha(alpha: Int) {
paint.alpha = alpha
}
override fun setColorFilter(colorFilter: ColorFilter?) {
paint.colorFilter = colorFilter
}
override fun getOpacity(): Int = PixelFormat.TRANSLUCENT
}
Activity中:
val roundDrawable = RoundDrawable(bitmap, 200.dp2px().toFloat())
mIvTarget.setImageDrawable(roundDrawable)
RoundedBitmapDrawable 是 Android 提供的一个工具类,用于处理圆形或圆角矩形的图片显示。
val bitmap = BitmapFactory.decodeResource(resources, R.drawable.icon_cat_w)
val roundBitmapDrawable = RoundedBitmapDrawableFactory.create(resources, bitmap).apply {
paint.isAntiAlias = true
cornerRadius = 20.dp2px().toFloat()
}
mIvTarget.setImageDrawable(roundBitmapDrawable)
以上介绍了几种常见的在 Android 中实现图片圆角矩形和圆形效果的方法,每种方式都有其使用场景和特点。推荐优先使用系统已经实现好的,比如ViewOutlineProvider
或者 优秀的Glide
图片加载库,如果有额外样式,可以按需实现。