样式示意图
自定义属性 style.xml
<declare-styleable name="IconLabelTextView">
<attr name="iconSrc" format="reference"/>
<attr name="iconPaddingStart" format="dimension"/>
<attr name="iconPaddingTop" format="dimension"/>
<attr name="iconPaddingBottom" format="dimension"/>
</declare-styleable>
自定义View-IconLabelTextView代码片段
import android.content.Context
import android.graphics.Canvas
import android.graphics.drawable.Drawable
import android.text.Layout
import android.text.StaticLayout
import android.text.TextPaint
import android.util.AttributeSet
import androidx.appcompat.widget.AppCompatTextView
import com.foreo.common.utils.DensityUtils
import com.xyz.R
class IconLabelTextView : AppCompatTextView {
private var icon: Drawable? = null
private var iconPaddingStart: Int = 0
private var iconPaddingTop: Int = 0
private var iconPaddingBottom: Int = 0
constructor(context: Context) : super(context) {
init(null)
}
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
init(attrs)
}
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
context,
attrs,
defStyleAttr
) {
init(attrs)
}
private fun init(attrs: AttributeSet?) {
val a = context.obtainStyledAttributes(attrs, R.styleable.IconLabelTextView)
icon = a.getDrawable(R.styleable.IconLabelTextView_iconSrc)
iconPaddingStart = a.getDimensionPixelSize(R.styleable.IconLabelTextView_iconPaddingStart, 0)
iconPaddingTop = a.getDimensionPixelSize(R.styleable.IconLabelTextView_iconPaddingTop, 0)
iconPaddingBottom = a.getDimensionPixelSize(R.styleable.IconLabelTextView_iconPaddingBottom, 0)
a.recycle()
}
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
canvas?.let { canvas ->
val textPaint: TextPaint = paint
val lineHeight = textPaint.descent() - textPaint.ascent()
val lineCount = layout.lineCount
val paddingTop = paddingTop.toFloat()
val availableWidth = width - paddingLeft - paddingRight - DensityUtils.dp2px(30)
val layout = layout ?: return@let
for (i in 0 until lineCount) {
val lineStart = layout.getLineStart(i)
val lineEnd = layout.getLineEnd(i)
val lineText = text.subSequence(lineStart, lineEnd).toString()
if (i == lineCount - 1 && layout.getLineWidth(i) >= availableWidth) {
var adjustedText = lineText
var textWidth = textPaint.measureText(lineText)
val iconWidth = icon?.intrinsicWidth ?: 0
while (textWidth + iconWidth + iconPaddingStart > availableWidth) {
adjustedText = adjustedText.substring(0, adjustedText.length - 1)
textWidth = textPaint.measureText(adjustedText)
}
if (!adjustedText.isNullOrBlank() && lineText != adjustedText) {
val splitIndex = text.indexOf(adjustedText) + adjustedText.length
text?.substring(0, splitIndex)?.let { text = "$it..." }
return
}
if (adjustedText.isNotEmpty()) {
val staticLayout = StaticLayout(
adjustedText,
textPaint,
availableWidth.toInt(),
Layout.Alignment.ALIGN_NORMAL,
1f,
0f,
false
)
val iconY =
paddingTop + (lineHeight * i) + (lineHeight - (icon?.intrinsicHeight
?: 0)) / 2 + iconPaddingTop - iconPaddingBottom
val iconX = paddingLeft + staticLayout.width + iconPaddingStart
icon?.setBounds(
iconX.toInt(),
iconY.toInt(),
(iconX + iconWidth).toInt(),
(iconY + (icon?.intrinsicHeight ?: 0)).toInt()
)
icon?.draw(canvas)
}
} else if (i == lineCount - 1 && lineEnd <= text.length) {
icon?.let {
val textWidth = textPaint.measureText(lineText)
val iconY =
paddingTop + (lineHeight * i) + (lineHeight - it.intrinsicHeight) / 2 + iconPaddingTop - iconPaddingBottom
val iconX = paddingLeft + textWidth + iconPaddingStart
it.setBounds(
iconX.toInt(),
iconY.toInt(),
(iconX + it.intrinsicWidth).toInt(),
(iconY + it.intrinsicHeight).toInt()
)
it.draw(canvas)
}
}
}
}
}
fun setIcon(icon: Drawable?, paddingStart: Int, paddingTop: Int, paddingBottom: Int) {
this.icon = icon
this.iconPaddingStart = paddingStart
this.iconPaddingTop = paddingTop
this.iconPaddingBottom = paddingBottom
invalidate()
}
}
xml中使用
<com.xyz.IconLabelTextView
android:id="@+id/productVariation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="2"
android:textSize="12sp"
app:iconPaddingBottom="0dp"
app:iconPaddingStart="5dp"
app:iconPaddingTop="3dp"
app:iconSrc="@drawable/ic_arrow_down_cart"
tools:text="Normal Skin" />
代码中使用
productVariation.setIcon(getDrawable(R.drawable.ic_arrow_down_cart),10,0,0)