文章目录
Android 圆形和圆角矩形总结
ViewOutlineProvider
ViewOutlineProvider 是 Android 5.0 引入的一个类,用来定义视图的轮廓(outline
)。可以通过它来实现圆角矩形、椭圆、圆形等效果。
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:gravity="center"
android:orientation="horizontal">
<ImageView
android:id="@+id/iv_clip_circle"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_marginRight="10dp"
android:scaleType="centerCrop"
android:src="@drawable/a" />
<ImageView
android:id="@+id/iv_clip_round"
android:layout_width="150dp"
android:layout_height="100dp"
android:scaleType="centerCrop"
android:src="@drawable/a" />
</LinearLayout>
binding.ivClipCircle.clipToCircleView()
binding.ivClipRound.clipToRoundView(10F.dp)
Glide
Glide 是一个强大的图片加载库,通过它的 RequestOptions 可以轻松实现图片的圆角处理。
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:gravity="center"
android:orientation="horizontal">
<ImageView
android:id="@+id/iv_glide_circle"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_marginRight="10dp"
android:scaleType="centerCrop"
android:src="@drawable/a" />
<ImageView
android:id="@+id/iv_glide_round"
android:layout_width="150dp"
android:layout_height="100dp"
android:scaleType="centerCrop"
android:src="@drawable/a" />
</LinearLayout>
Glide.with(this)
.load(R.drawable.a)
.transform(CircleCrop())
.into(binding.ivGlideCircle)
Glide.with(this)
.load(R.drawable.a)
.apply(RequestOptions().transform(CenterCrop(), RoundedCorners(10.dp)))
.into(binding.ivGlideRound)
Canvas#clipPath()
自定义 ImageView 并重写 onDraw() 方法,通过 Canvas.clipPath() 实现图片的圆角矩形和圆形效果。
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:gravity="center"
android:orientation="horizontal">
<com.example.androidui.circleround.view1.CircleImageView
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_marginRight="10dp"
android:scaleType="centerCrop"
android:src="@drawable/a" />
<com.example.androidui.circleround.view1.RoundImageView
android:layout_width="150dp"
android:layout_height="100dp"
android:scaleType="centerCrop"
android:src="@drawable/a" />
</LinearLayout>
class CircleImageView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : AppCompatImageView(context, attrs, defStyleAttr) {
private val path = Path()
override fun onDraw(canvas: Canvas?) {
val radius = minOf(width, height) / 2F
path.addCircle(width / 2F, height / 2F, radius, Path.Direction.CW)
canvas?.clipPath(path)
super.onDraw(canvas)
}
}
class RoundImageView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : AppCompatImageView(context, attrs, defStyleAttr) {
private val path = Path()
override fun onDraw(canvas: Canvas?) {
path.addRoundRect(
RectF(0F, 0F, width.toFloat(), height.toFloat()),
10F.dp,
10F.dp,
Path.Direction.CW
)
canvas?.clipPath(path)
super.onDraw(canvas)
}
}
CardView
通过 CardView 的 app:cardCornerRadius
属性,可以非常方便地实现圆角效果。
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:gravity="center"
android:orientation="horizontal">
<androidx.cardview.widget.CardView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="10dp"
app:cardCornerRadius="100dp">
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
android:scaleType="centerCrop"
android:src="@drawable/a" />
</androidx.cardview.widget.CardView>
<androidx.cardview.widget.CardView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:cardCornerRadius="10dp">
<ImageView
android:layout_width="150dp"
android:layout_height="100dp"
android:scaleType="centerCrop"
android:src="@drawable/a" />
</androidx.cardview.widget.CardView>
</LinearLayout>
BitmapShader
BitmapShader 是 Android 中的一种着色器,通过它可以实现自定义的圆角和圆形图片。
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:gravity="center"
android:orientation="horizontal">
<com.example.androidui.circleround.view2.CircleImageView
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_marginRight="10dp"
android:scaleType="centerCrop"
android:src="@drawable/a" />
<com.example.androidui.circleround.view2.RoundImageView
android:layout_width="150dp"
android:layout_height="100dp"
android:scaleType="centerCrop"
android:src="@drawable/a" />
</LinearLayout>
class CircleImageView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : AppCompatImageView(context, attrs, defStyleAttr) {
private val paint by lazy { Paint(Paint.ANTI_ALIAS_FLAG) }
override fun onDraw(canvas: Canvas) {
drawable ?: return
val bitmap = drawableToBitmap(drawable) ?: return
getShader(bitmap)
val radius = minOf(width, height) / 2f
canvas.drawCircle(width / 2F, height / 2F, radius, paint)
}
private fun drawableToBitmap(drawable: Drawable): Bitmap? {
return when (drawable) {
is BitmapDrawable -> drawable.bitmap
else -> {
val bitmap = Bitmap.createBitmap(
drawable.intrinsicWidth,
drawable.intrinsicHeight,
Bitmap.Config.ARGB_8888
)
val canvas = Canvas(bitmap)
drawable.setBounds(0, 0, canvas.width, canvas.height)
drawable.draw(canvas)
bitmap
}
}
}
private fun getShader(bitmap: Bitmap) {
val shader = BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)
val matrix = Matrix()
val scale: Float
val dx: Float
val dy: Float
if (bitmap.width * height > width * bitmap.height) {
scale = height / bitmap.height.toFloat()
dx = (width - bitmap.width * scale) * 0.5f
dy = 0f
} else {
scale = width / bitmap.width.toFloat()
dx = 0f
dy = (height - bitmap.height * scale) * 0.5f
}
matrix.setScale(scale, scale)
matrix.postTranslate(dx, dy)
shader.setLocalMatrix(matrix)
paint.shader = shader
}
}
class RoundImageView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : AppCompatImageView(context, attrs, defStyleAttr) {
private val paint by lazy { Paint(Paint.ANTI_ALIAS_FLAG) }
override fun onDraw(canvas: Canvas) {
drawable ?: return
val bitmap = drawableToBitmap(drawable) ?: return
getShader(bitmap)
val rect = RectF(0F, 0F, width.toFloat(), height.toFloat())
canvas.drawRoundRect(rect, 10F.dp, 10F.dp, paint)
}
private fun drawableToBitmap(drawable: Drawable): Bitmap? {
return when (drawable) {
is BitmapDrawable -> drawable.bitmap
else -> {
val bitmap = Bitmap.createBitmap(
drawable.intrinsicWidth,
drawable.intrinsicHeight,
Bitmap.Config.ARGB_8888
)
val canvas = Canvas(bitmap)
drawable.setBounds(0, 0, canvas.width, canvas.height)
drawable.draw(canvas)
bitmap
}
}
}
private fun getShader(bitmap: Bitmap) {
val shader = BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)
val matrix = Matrix()
val scale: Float
val dx: Float
val dy: Float
if (bitmap.width * height > width * bitmap.height) {
scale = height / bitmap.height.toFloat()
dx = (width - bitmap.width * scale) * 0.5f
dy = 0f
} else {
scale = width / bitmap.width.toFloat()
dx = 0f
dy = (height - bitmap.height * scale) * 0.5f
}
matrix.setScale(scale, scale)
matrix.postTranslate(dx, dy)
shader.setLocalMatrix(matrix)
paint.shader = shader
}
}
RoundedBitmapDrawable
RoundedBitmapDrawable 是 Android 提供的一个工具类,用于处理圆形或圆角矩形的图片显示。
RoundedBitmapDrawable 不支持 ImageView 的 scaleType属性。
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:gravity="center"
android:orientation="horizontal">
<ImageView
android:id="@+id/iv1"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_marginRight="10dp" />
<ImageView
android:id="@+id/iv2"
android:layout_width="150dp"
android:layout_height="100dp" />
</LinearLayout>
binding.iv1.post {
val bitmap = BitmapFactory.decodeResource(resources, R.drawable.a)
val scaleBitmap =
centerCropBitmap(bitmap, binding.iv1.measuredWidth, binding.iv1.measuredHeight)
val circleDrawable = RoundedBitmapDrawableFactory.create(resources, scaleBitmap).apply {
paint.isAntiAlias = true
isCircular = true
}
binding.iv1.setImageDrawable(circleDrawable)
}
binding.iv2.post {
val bitmap = BitmapFactory.decodeResource(resources, R.drawable.a)
val scaleBitmap =
centerCropBitmap(bitmap, binding.iv2.measuredWidth, binding.iv2.measuredHeight)
val roundDrawable = RoundedBitmapDrawableFactory.create(resources, scaleBitmap).apply {
paint.isAntiAlias = true
setCornerRadius(10F.dp)
}
binding.iv2.setImageDrawable(roundDrawable)
}
ShapeableImageView
ShapeableImageView 是 Android Material Components 库中的一个控件,用于轻松实现自定义形状的 ImageView。通过 ShapeableImageView,开发者可以轻松地实现圆形、圆角矩形、不同角的圆角以及其他复杂形状,而无需依赖 XML 的 shape 文件或者第三方库。
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:gravity="center"
android:orientation="horizontal">
<com.google.android.material.imageview.ShapeableImageView
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_marginRight="10dp"
android:scaleType="centerCrop"
android:src="@drawable/a"
app:shapeAppearance="@style/CircleStyle" />
<com.google.android.material.imageview.ShapeableImageView
android:layout_width="150dp"
android:layout_height="100dp"
android:scaleType="centerCrop"
android:src="@drawable/a"
app:shapeAppearance="@style/RoundStyle" />
</LinearLayout>
<style name="CircleStyle">
<item name="cornerSize">50%</item>
</style>
<style name="RoundStyle">
<item name="cornerSize">10dp</item>
</style>
ImageFilterView
ImageFilterView 是 Android 提供的一个特殊的 ImageView,它继承自 ImageView 并扩展了其功能。
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:gravity="center"
android:orientation="horizontal">
<androidx.constraintlayout.utils.widget.ImageFilterView
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_marginRight="10dp"
android:scaleType="centerCrop"
android:src="@drawable/a"
app:roundPercent="1" />
<androidx.constraintlayout.utils.widget.ImageFilterView
android:layout_width="150dp"
android:layout_height="100dp"
android:scaleType="centerCrop"
android:src="@drawable/a"
app:round="10dp" />
</LinearLayout>