卖不甜枣 发表于 2025-1-20 19:31:30

Android | 多种方式实现图片圆角矩形和圆形效果

在项目开发中,为了让图片表现得更加美观,通常 UI 会设计成圆角矩形或圆形效果。本文将介绍几种常见的实现方式,并提供对应的代码示例。
方式一:ViewOutlineProvider可以设置圆角矩形、椭圆、圆形等

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)
效果如下:
https://i-blog.csdnimg.cn/direct/0a3fe8a6c9df4a8b84824436764f0636.png
方式二:使用 Glide 进行图片加载和圆角处置惩罚

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)
方式三:Canvas.clipPath()

自定义 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)
效果图:
https://i-blog.csdnimg.cn/direct/5625a56fc14c4148b0b753c48d6fcf35.png
方式四:CardView

通过 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

BitmapShader 是 Android 中的一种着色器,通过它可以实现自定义的圆角和圆形图片。
    /**
   * @param bitmapbitmap
   * @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

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 图片加载库,如果有额外样式,可以按需实现。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: Android | 多种方式实现图片圆角矩形和圆形效果