顶部带有背景的动态文本的自定义图像

问题描述

我正在尝试在 Kotlin 中创建自定义 ImageViewDrawable,以便在运行时可以在基本图像上绘制动态文件扩展名。最终结果将如下所示。尝试创建自定义 AppCompatimageView 类并覆盖 onDraw() 没有运气。作为这方面的新手,您能建议我一个好的起点来实现这一目标吗?

enter image description here

编辑

文件扩展名是需要在带有背景的基础图像上绘制的文本,如附件所示。

解决方法

您可以在运行时创建一个 LayerDrawable 以叠加两个 drawable(一个用于背景,一个用于扩展)并将扩展 drawable 放置在右下角。

看起来像这样

val layerDrawable = LayerDrawable(
                        arrayOf(
                            AppCompatResources.getDrawable(context,R.drawable.ic_base_sound_file),AppCompatResources.getDrawable(context,R.drawable.ic_aiff_extension)
                        )
                    ).apply {
                        setLayerInset(1,20,40,10)
                    }
imageView.setImageDrawable(layerDrawable)

方法 setLayerInset(index,left,top,right,bottom) 将在位置 'index' 的 drawable(这里是 1 -> 扩展 drawable)添加插图。

如果基础镜像需要,您也可以使用远程镜像。

,

我可以想到 2 个解决方案。我只是在这里分享想法/方法,相关代码很容易找到。

  1. 更简单的方法是在您的 xml 中设计此布局。然后创建扩展 ViewGroup 的自定义类,并在其构造函数中,您可以扩充视图 xml 并初始化内容。然后您可以定义任何辅助方法,例如 setData(),您可以在其中传递文件扩展名信息和/或拇指图像。然后你可以在那里更新你的视图。

  2. 另一种方法是不创建任何 xml,而是在您的自定义 ViewGroup 构造函数中以编程方式创建它们。然后,您可以使用与上述类似的辅助方法来为各种视图组件设置值。设置完所有内容后,最后调用 requestLayout() 。然后,如果需要,您可以更新 onLayout() 中的视图并执行任何间距/边距计算。然后使用这些值在 onDraw() 中绘制它们。

,

我更喜欢使用自定义视图而不是自定义可绘制对象。因为它可以灵活地测量和自定义高度和宽度。

所以我创建了 FileView:

import android.content.Context
import android.content.res.Resources
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.Rect
import android.graphics.drawable.Drawable
import android.text.TextPaint
import android.util.AttributeSet
import androidx.appcompat.widget.AppCompatImageView

class FileView @JvmOverloads constructor(
    context: Context,attrs: AttributeSet? = null,defStyleAttr: Int = 0
) : AppCompatImageView(context,attrs,defStyleAttr) {

    init {
        setImageResource(R.drawable.ic_file)
    }

    var icon: Drawable? = null
        set(value) {
            field = value
            postInvalidate()
        }

    var ext: CharSequence? = null
        set(value) {
            field = value
            postInvalidate()
        }

    private val iconRect = Rect()
    private val extRect = Rect()
    private val extPaint by lazy {
        TextPaint().apply {
            style = Paint.Style.FILL
            color = Color.WHITE
            isAntiAlias = true
            textAlign = Paint.Align.CENTER
            textSize = 12f * Resources.getSystem().displayMetrics.density + 0.5f
        }
    }
    private val extBackgroundPaint by lazy {
        TextPaint().apply {
            style = Paint.Style.FILL
            color = Color.BLACK
            isAntiAlias = true
        }
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)

        val centerX = width / 2
        val centerY = height / 2

        icon?.let { icon ->
            iconRect.set(
                centerX - icon.intrinsicWidth / 2,centerY - icon.intrinsicHeight / 2,centerX + icon.intrinsicWidth / 2,centerY + icon.intrinsicHeight / 2
            )

            icon.bounds = iconRect
            icon.draw(canvas)
        }


        ext?.let { ext ->
            val truncatedExt =
                if (ext.length > 6) ext.subSequence(0,6).toString().plus('…')
                else ext

            // extRect is used for measured ext height
            extPaint.getTextBounds("X",1,extRect)
            val extHeight = extRect.height() // keep ext height
            val extWidth = extPaint.measureText(truncatedExt,truncatedExt.length).toInt() // keep ext width

            val extPadding = 4.toPx
            val extMargin = 4.toPx

            val extRight = width - extMargin
            val extBottom = height - extMargin
            // extRect is reused for ext background bound
            extRect.set(
                extRight - extWidth - extPadding * 2,extBottom - extHeight - extPadding * 2,extRight,extBottom
            )
            canvas.drawRect(extRect,extBackgroundPaint)

            canvas.drawText(
                truncatedExt,truncatedExt.length,extRect.exactCenterX(),extRect.bottom - ((extRect.height() - extHeight) / 2f),extPaint
            )
        }
    }

    private val Int.toPx get() = (this * Resources.getSystem().displayMetrics.density).toInt()
}

并使用它:

with(binding.fileView) {
    icon = ContextCompat.getDrawable(context,R.drawable.ic_music)
    ext = ".aiff"
}

输出: enter image description here