继承 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居中