如何实现为APP图标添加可自定义颜色的圆形背景的自定义View?
实现带彩色圆形背景的图标自定义View
嘿,这个需求用自定义View实现起来挺顺手的,我给你一步步拆解,保证能轻松搞定~
核心思路
本质就是先画圆形背景,再把图标叠在背景上面。为了让背景颜色能灵活配置,我们需要给自定义View加自定义属性,这样不管是在布局里静态设置,还是代码里动态修改都很方便。
步骤1:定义自定义属性
先在res/values/attrs.xml里添加我们需要的属性,比如背景色、图标资源、图标尺寸:
<resources> <declare-styleable name="IconWithCircleBgView"> <!-- 圆形背景颜色 --> <attr name="circleBgColor" format="color" /> <!-- 图标资源 --> <attr name="iconSrc" format="reference" /> <!-- 图标大小 --> <attr name="iconSize" format="dimension" /> </declare-styleable> </resources>
步骤2:编写自定义View类
接下来写View的核心代码,主要做三件事:解析属性、测量View尺寸、绘制背景和图标。这里用Kotlin示例,Java写法逻辑是一样的:
import android.content.Context import android.graphics.Canvas import android.graphics.Paint import android.graphics.Rect import android.graphics.drawable.Drawable import android.util.AttributeSet import android.view.View import androidx.core.content.ContextCompat class IconWithCircleBgView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 ) : View(context, attrs, defStyleAttr) { // 绘制圆形背景的画笔(开抗锯齿让边缘更顺滑) private val circlePaint = Paint(Paint.ANTI_ALIAS_FLAG) // 要显示的图标 private var iconDrawable: Drawable? = null // 图标尺寸,默认24dp private var iconSize = dpToPx(24f).toInt() // 圆形背景的半径 private var circleRadius = 0f // 图标绘制的区域(用来让图标居中) private val iconRect = Rect() init { // 解析布局里的自定义属性 attrs?.let { val typedArray = context.obtainStyledAttributes(it, R.styleable.IconWithCircleBgView) // 获取背景色,默认用浅灰色 val bgColor = typedArray.getColor( R.styleable.IconWithCircleBgView_circleBgColor, ContextCompat.getColor(context, R.color.default_light_gray) ) circlePaint.color = bgColor // 获取图标资源 iconDrawable = typedArray.getDrawable(R.styleable.IconWithCircleBgView_iconSrc) // 获取图标尺寸 iconSize = typedArray.getDimensionPixelSize( R.styleable.IconWithCircleBgView_iconSize, dpToPx(24f).toInt() ) // 记得回收TypedArray,避免内存泄漏 typedArray.recycle() } } override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { // 圆形背景需要正方形的View,所以取宽高中的最大值,或者用默认尺寸 val desiredSize = dpToPx(48f).toInt() // 默认View大小48dp,可按需调整 val finalWidth = resolveSize(desiredSize, widthMeasureSpec) val finalHeight = resolveSize(desiredSize, heightMeasureSpec) setMeasuredDimension(finalWidth, finalHeight) // 计算圆形半径(取View宽高的一半) circleRadius = Math.min(finalWidth, finalHeight) / 2f // 计算图标居中的绘制区域 val iconLeft = (finalWidth - iconSize) / 2 val iconTop = (finalHeight - iconSize) / 2 iconRect.set(iconLeft, iconTop, iconLeft + iconSize, iconTop + iconSize) } override fun onDraw(canvas: Canvas) { super.onDraw(canvas) // 1. 先画圆形背景,中心就是View的中心 canvas.drawCircle(width / 2f, height / 2f, circleRadius, circlePaint) // 2. 再画图标,把图标限制在提前计算好的居中区域里 iconDrawable?.apply { bounds = iconRect draw(canvas) } } // 工具方法:dp转px private fun dpToPx(dp: Float): Float { return dp * context.resources.displayMetrics.density } // 对外提供动态修改背景色的方法 fun setCircleBgColor(color: Int) { circlePaint.color = color invalidate() // 通知View重绘 } // 对外提供动态修改图标的方法 fun setIconSrc(drawable: Drawable) { iconDrawable = drawable invalidate() } }
步骤3:在布局中使用
现在就可以直接在xml里用这个View了,还能自定义各种属性:
<com.yourpackage.IconWithCircleBgView android:layout_width="48dp" android:layout_height="48dp" app:circleBgColor="#FF4081" <!-- 粉色背景 --> app:iconSrc="@drawable/ic_favorite" <!-- 爱心图标 --> app:iconSize="28dp" /> <!-- 图标放大到28dp -->
额外小技巧
- 如果需要给圆形背景加阴影,给
circlePaint加上阴影效果就行:
注意:阴影在硬件加速下可能不生效,需要在View的xml里加circlePaint.setShadowLayer(4f, 0f, 2f, ContextCompat.getColor(context, R.color.shadow_color))android:layerType="software"。 - 要是需要批量创建带不同背景色的图标,直接在代码里循环调用
setCircleBgColor()就行,非常灵活。
这样每个图标都会被彩色圆形背景包裹,完全满足你的需求啦~
内容的提问来源于stack exchange,提问作者MichelReap
相关产品推荐
相关产品推荐

