天天看点

TextView上下左右Drawable居中

TextView上下左右Drawable居中

继承 AppCompatTextView ,通过设置 setBounds 限制drawable的绘制位置,实现上下左右drawable跟随文字剧中。

 原理:android:gravity="center" 会让内容居于控件的中间,如果有drawable,则要把drawable占用的长度排除,所以实际的居中,是居于两个drawable的中间,而非控件总长度的中间。

只适配了 Gravity.CENTER的情况

代码:

import android.content.Context
import android.graphics.Canvas
import android.graphics.Rect
import android.util.AttributeSet
import android.view.Gravity
import androidx.appcompat.widget.AppCompatTextView

/**
 *
 */
class CenterTextView : AppCompatTextView {

    constructor(context: Context) : super(context)
    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
        context,
        attrs,
        defStyleAttr
    )

    private val rect: Rect by lazy { Rect() }

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

        if (gravity != Gravity.CENTER) {
            return
        }

        // 计算文字内容的宽高
        paint.getTextBounds(text.toString(), 0, text.toString().length, rect)
        val textWidth: Int = rect.width()
        val textHeight: Int = rect.height()

        // final Drawable[] mShowing = new Drawable[4];
        val drawableLeft = compoundDrawables[0]
        val drawableTop = compoundDrawables[1]
        val drawableRight = compoundDrawables[2]
        val drawableBottom = compoundDrawables[3]

        // 移动距离 Gravity.CENTER 主要是计算左右drawable需要移动的距离,移除padding,移除内容,移除左右drawable的width,最后移除drawablePadding。
        val translationX =
            (width - (drawableLeft?.intrinsicWidth ?: 0) - (drawableRight?.intrinsicWidth
                ?: 0) - paddingLeft - paddingRight - textWidth) / 2 - compoundDrawablePadding
        val translationY =
            (height - (drawableTop?.intrinsicHeight ?: 0) - (drawableBottom?.intrinsicHeight
                ?: 0) - paddingTop - paddingBottom - textHeight) / 2 - compoundDrawablePadding

        // 设置左侧drawable 如果左侧存在
        drawableLeft?.apply {
            setBounds(translationX, 0, translationX + intrinsicWidth, intrinsicHeight)
        }
        // 设置右侧drawable 如果右侧存在
        drawableRight?.apply {
            // 右侧的drawable坐标轴原点(0,0)在右侧
            setBounds(-translationX, 0, -translationX + intrinsicWidth, intrinsicHeight)
        }

        // 设置上侧drawable 如果上侧存在
        drawableTop?.apply {
            setBounds(0, translationY, intrinsicWidth, translationY + intrinsicHeight)
        }
        // 设置下侧drawable 如果下侧存在
        drawableBottom?.apply {
            // 下侧的drawable坐标轴原点(0,0)在下侧
            setBounds(0, -translationY, intrinsicWidth, -translationY + intrinsicHeight)
        }
    }

}
           
<developer.liwl.widget.CenterTextView
    android:layout_width="match_parent"
    android:layout_height="150dp"
    android:button="@null"
    android:drawableStart="@mipmap/ic_test"
    android:drawableTop="@mipmap/ic_test"
    android:drawableEnd="@mipmap/ic_test"
    android:drawableBottom="@mipmap/ic_test"
    android:gravity="center"
    android:text="上下左右Drawable居中"
    android:textSize="18sp" />
           

setBounds :限定绘制drawable的位置,  drawable原点(0,0)在左上角,通过(left,top)和(right,bottom)确定了绘制drawable的空间,下移右移为正,左移上移为负。

/**
     * Specify a bounding rectangle for the Drawable. This is where the drawable
     * will draw when its draw() method is called.
     */
    public void setBounds(int left, int top, int right, int bottom) {
        ...
    }
           

适配 android:supportsRtl="true"

类似drawable剧中的方式还有,手动计算setPadding,移动画布canvas.translate(x, y)等方式。各有优劣。

由于Button,RadioButton都是继承自TextView,所以,都可以通过以上方式实现drawable跟随textview居中