在应用开发中,为了友好的用户体验,我们常常需要在文字旁边添加 ICON,因为用户对图形的感知往往要强于文字,比如这样子:

大多数新手看一眼觉得很简单,使用 ImageView 和 TextView 组合即可。
的确,这是一种很简单的方法,但如上图中使用 GridLayout 来展示多个子项,明明 ICON 和文字属于同一项,组合写法会导致代码成倍增加,再加上需要用一个父容器(如 LinearLayout)来包裹这两个子项,布局文件就如老太太的裹脚布——又长又臭。
当然,你也可以把这个组合抽取出来,做成 Adapter 通用,尽管布局清晰了,但逻辑代码也增加不少。
实际上,Android 本身就为 TextView 提供了此功能,我们只需一行代码就可以使用:
<TextView
...
android:drawableTop="@drawable/ic_drawable" />
该属性可以为 TextView 在某一方向上添加对应的 Drawable,其他方向只需修改属性中的方向名称即可。
AppCompat 提供了一些 Compat 属性,所以你也可以这样写:
<TextView
...
app:drawableTopCompat="@drawable/ic_drawable" />
如果你想调整 Drawable 和文字的间隔,可以使用这个属性:
<TextView
...
android:drawablePadding="16dp"
app:drawableTopCompat="@drawable/ic_drawable" />
当 Drawable 尺寸过大时,你可能会注意到 Drawable 和文字并不是居中的,这时你要牢记,这个 Drawable 也是 TextView 的一部分:

对于 View 或 ViewGroup 内部元素的对齐方式,我们通常可以使用:
<TextView
...
android:gravity="center"
app:drawableTopCompat="@drawable/ic_drawable" />
假如你需要在逻辑代码中动态修改该 Drawable,同样提供支持:
textView.setCompoundDrawablesWithIntrinsicBounds(getResources().getDrawable(R.drawable.ic_drawable), null, null, null);
setCompoundDrawablesWithIntrinsicBounds() 方法接收四个参数,分别是四个方向上的 Drawable 资源,在对应方向传入所需的 Drawable 即可。
该方法同时也支持以资源 ID 的方式传入 Drawable,所以也可以这样写:
textView.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_drawable, 0, 0, 0);
当你觉得这个功能挺好用时,假如美工让你调整一下 Drawable 的大小,你可能一下子就懵了,似乎找不到这个属性,还不如 ImageView 和 TextView 组合来得方便呀。
的确,在布局文件中没有调整 Drawable 尺寸的属性,但你要记得,这是一个 Drawable,既然是 Drawable,我们就可以通过逻辑代码控制它的尺寸:
Drawable drawable = getResources().getDrawable(R.drawable.ic_account);
drawable.setBounds(0, 0, 180, 180); // 设置图片的大小
textView.setCompoundDrawables(drawable, null, null, null); // 设置图片的位置
Drawable 的 setBounds() 方法接收四个参数,四个坐标形成一个矩形,Drawable 将在被绘制在这个矩形区域内。
接着调用的 setCompoundDrawables() 和上面提到的 setCompoundDrawablesWithIntrinsicBounds() 类似,将 Drawable 传入到对应位置即可。
另外我们知道,EditText 实际上是继承于 TextView 的,那么 EditText 也会有相同的属性,所以我们可以轻而易举的实现类似的功能:

属性和方法基本和 TextView 一致,不再重复贴代码。
