.9图的快速使用

什么是.9图

首先引用一段Android官方文档对.9图的介绍:

NinePatchDrawable 图形是一种可拉伸的位图,可用作视图的背景。Android 会自动调整图形的大小以适应视图的内容。NinePatch 图片的其中一项用途是用作标准 Android 按钮(按钮必须拉伸以适应各种长度的字符串)的背景。NinePatch 图形是标准 PNG 图片,包含一个额外的 1 像素边框。必须使用 9.png 扩展名将其保存在项目的 res/drawable/ 目录下。

总结就是.9图是一种Android开发中特殊的图片格式。跟普通png格式图片的区别在于,如果图片的宽高被不规则的拉伸,普通png的一些细节会被拉伸导致变形,而.9图可以让这些细节在被拉伸时保持不变。

比较常见的场景就是作为一些宽高不固定的控件的背景,如标签,聊天气泡等,当控件内文本内容变化时,控件的宽高会动态变化,此时.9图作为背景图片,他的宽高也能跟着一起变化的同时确保一些细节不变形。

以一个聊天气泡为例,png格式的原图是这样的:

如果直接用做一个 TextView 的背景:

1
2
3
4
5
6
7
8
9
10
11
12

<TextView
android:id="@+id/message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="20dp"
android:background="@mipmap/bubble"
android:padding="20dp"
android:text="这是一条很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很很长很长很长很长很长很长很长很长很长的文本内容"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

效果如下:

很明显能看到气泡左侧的三角箭头,四个圆角,背景中的引号,都被拉伸扭曲了。而把他转成.9图并做好适配,效果如下:

这就是一个常见的聊天气泡了。

如何制作.9图

正如官方文档所说,.9图实际上就是在一张标准的 PNG 原图基础上加了1个像素宽度的边框,通过这个边框在图片中的投影,来标识图片中哪些部分在宽高变化时可以被拉伸,哪些部分则固定不变,以及内容显示区域的范围是多大。

首先最简单的方法是设计同学可以在出切图的时候就可以直接做好处理。不过这一方法在给到我们开发时具体使用的时不是很方便,因为他们划定的边框不一定能刚好满足我们的需求,很多时候需要来回调整,比较麻烦。因此我们可以直接使用AndroidSutdio自带的工具将任意一张 PNG 图片直接转换为.9图,按我们的需要来具体调整拉伸和显示规则。

Draw 9-patch 工具是 Android Studio 中包含的一种 WYSIWYG(所见即所得)编辑器。借助该工具,您可以创建能够自动调整大小以适应视图内容和屏幕尺寸的位图图像。您可以根据图片内绘制的指示器在图片上水平或垂直缩放所选部分。

制作工具的使用

这里还是以那个聊天气泡的图片为例。首先直接把需要转换的原png图重命名,以 .9 结尾:

使用AndroidStudio,打开这张图,界面如下:

屏幕中间是可操作区,可以自行通过绘制边框来控制拉伸区域和内容展示区域;右侧是预览区域,即当前.9图在被拉伸后的效果。底部有一排勾选框,可以配合预览使用。

接下来开始绘制边框。首先鼠标指针移动到图片的任意一边 1 像素左右范围,可以看到指针上出现了一个小图标,提示可以左右、上下拖动。按住沿着某一边拖动后,就会在这一边上留下一条黑色边框:

如图,通过顶部通过拖动,绘制出了一条短黑线。用同样的方法,可以在左边、右边、底部都可以绘制黑线,这样每一条边框上都有一段(也可以是多段)黑线,剩下为透明部分,这段线条的含义概括来说:

  • 顶部和左边黑线的辐射区域相交部分,在宽高变化时会同时拉伸,其余部分则会固定不变
  • 底部和右边黑线的辐射区域相交部分,限制了内容显示区域,其余部分则会留出空白

上面那张图中,在顶部绘制了一条短黑线,他向下辐射的区域覆盖了背景中引号部分的右半边,因此在图片宽度变化时,左半边的引号会固定不变,右半边的引号会被拉伸,在右边可以看到实时预览效果:

因此,要为了保证背景中引号部分显示正常,顶部的黑线就要避开引号,比如可以直接挪到正中间位置,这里是纯色部分,拉伸不会有影响:

看右侧的预览效果:

果然,在宽度变化时,引号部分是不会变的。

同样的,为了让气泡的四个圆角和箭头都保持不变,顶部和左侧的两天黑线都应该只覆盖纯色部分,注意这里黑线的长短其实无所谓,多短都是可以的:

这样避开圆角和箭头后,看右侧的预览效果:

右侧和底部的黑线控制的是内容显示区域。首先可以在底部勾选这个按钮:

这样在预览区域可以显示出具体的内容显示区域了,这个内容显示区域具体是什么意思呢?以聊天气泡为例,既然气泡内部是有文本显示的,如果不做任何处理,那文本显示区域的上下左右四个边就会刚好跟气泡背景图的四个边重合。如果右侧和底部都有划线,那边就相当于给文本显示区域和图片背景区域之间增加了间距,缩小了文字显示的范围。比如现在在底部和右侧用同样的方法任意画一条线:

右侧的预览就显示出内容显示区域:

将这张图片设为 TextView 的背景:

1
2
3
4
5
6
7
8
9
10
11

<TextView
android:id="@+id/message2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="20dp"
android:background="@mipmap/bubble3"
android:text="这是一条很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很很长很长很长很长很长很长很长很长很长的文本内容"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/message" />

效果如下:

可以看到,文字部分刚好就处于刚才划定的范围内。如果把整个底边和右边都划满黑线,就需要给 TextView 手动添加 padding 来控制显示区域了。

总结

以上,一个完整的聊天气泡背景图就制作完成了,其他类似场景也是一样的,只要保证左侧和顶部的黑线要避开拉伸后会异常的图像部分,右侧和底部的黑线则要根据实际情况适当的划定范围即可。