Android TextView图标对齐优化:使用LayerList精准控制drawable位置

0 阅读2分钟

背景

我们经常使用TextView的drawableStartdrawableEnd等属性来为文字添加图标。但有时会遇到这样的问题:

<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:drawableStartCompat="@drawable/icon_left_arrow"
    android:includeFontPadding="false"
    android:drawablePadding="2dp"
    android:text="我是文字"
    android:textColor="@color/red"
    android:textSize="18sp" />

执行结果:

结果

当图标尺寸大于或小于文字高度时,有时会出现图标位置偏移、视觉不居中的问题,而XML中又没有直接调整图标上下位置的属性,这时候该咋办呢?

:上述效果也可以通过代码来实现,XML中不再设置drawableXXX等属性:

<TextView
    android:id="@+id/tv_text"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:text="我是文字"
    android:includeFontPadding="false"
    android:textColor="@color/red"
    android:textSize="18sp" />

代码中动态设置:

//扩展函数
fun TextView.setLeftDrawable(leftDrawableRes: Int, widthInDp: Int = 10, heightInDp: Int = 10) {
    val drawable = ContextCompat.getDrawable(context, leftDrawableRes)

    val scale = context.resources.displayMetrics.density
    val widthPx = (widthInDp * scale + 0.5f).toInt() //dp to px
    val heightPx = (heightInDp * scale + 0.5f).toInt() //dp to px
    drawable?.setBounds(0, 0, widthPx, heightPx)
    compoundDrawablePadding = (2 * scale + 0.5f).toInt() //设置padding
    setCompoundDrawables(drawable, null, null, null)
}

//使用
mTvText.setCenteredDrawable(R.drawable.icon_left_arrow, 6, 10)

执行效果同上。

优化方式

方案1:分成两个控件

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center">

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_marginEnd="2dp"
        android:src="@drawable/icon_left_arrow" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:includeFontPadding="false"
        android:text="我是文字"
        android:textColor="@color/red"
        android:textSize="18sp" />
</LinearLayout>

执行结果:

结果

把箭头单独拆成一个控件来处理,如果不居中还可以自由调整。

方案2:使用LayerList来控制(推荐)

LayerList(层叠列表)是一种Drawable资源,它允许我们将多个drawable组合在一起,并精确控制每个元素的位置、尺寸和对齐方式。实现步骤如下:

  • 创建LayerList Drawable

res/drawable/目录下创建layer_arrow_icon

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:width="6dp"
        android:height="10dp" 
        android:gravity="center"
        android:drawable="@drawable/icon_left_arrow"
        android:top="1dp">
    </item>
</layer-list>

android:drawable:引用的原始图标资源,android:width/height:精确控制图标尺寸,android:top/bottom等可以调整图标上下间距。

  • 在TextView中应用优化后的Drawable
<TextView
    app:drawableStartCompat="@drawable/layer_arrow_icon"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:text="我是文字"
    android:drawablePadding="2dp"
    android:textColor="@color/red"
    android:textSize="18sp" />

执行效果同上面居中样式的,不再贴了。

扩展:多图层控制

比如现在需要对图标加个背景,依然可以使用多层LayerList:

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- 背景层 -->
    <item
        android:width="16dp"
        android:height="16dp"
        android:gravity="center"
        android:top="1dp">
        <shape android:shape="oval">
            <solid android:color="#33000000" />
        </shape>
    </item>

    <item
        android:width="6dp"
        android:height="10dp"
        android:drawable="@drawable/icon_left_arrow"
        android:gravity="center"
        android:top="1dp" />
</layer-list>

执行结果:

结果

通过LayerList优化TextView中drawable的位置和尺寸,可以动态调整图标尺寸,避免尺寸不匹配,优化居中效果,且无需修改现有布局结构。