在更改Launcher2.apk
中发现的图像的过程中,我发现一些文件显然是九个补丁,但高度或宽度仅为1px:
$ file **/*.9.png G "( 1 x|x 1,)"
Launcher2/res/drawable-land-xhdpi/workspace_bg.9.png: PNG image, 400 x 1, 8-bit gray+alpha, non-interlaced
Launcher2/res/drawable-sw720dp-xhdpi/workspace_bg.9.png: PNG image, 1 x 400, 8-bit gray+alpha, non-interlaced
Launcher2/res/drawable-xhdpi/workspace_bg.9.png: PNG image, 1 x 400, 8-bit gray+alpha, non-interlaced
这些文件是由创建者搞砸的,还是特殊情况,即固定宽度但可变高度,反之亦然?
九个补丁文件的格式有什么正式的定义吗?当然,我在谷歌上搜索了一下,花了我很长时间,没有找到任何对我有帮助的信息。最好能涵盖其他方面,因为我发现了很多其他方面多个包中的可疑图像在最左边/最上面/最右边/最下面的像素中不包含二进制信息;因此,我也想确切地知道哪些值按原样编码repeat/estretch/,哪些值被忽略。
谷歌Canvas and Drawables文档中的这一部分没有提供太多细节(实际上没有)。分布式draw9patch.jar也没有多大帮助。
以防万一:我从Launcher2.apk
和其他包中拿走的设备是Moto G,库存4.3固件。
提前感谢
好吧,自从我对九个补丁图像进行了更多研究以来,我学到了什么:
主要错误可能是认为九个补丁的重复/拉伸/保持元数据总是编码在图像文件的边界像素中。这不是真的。
事实上,有两种九种补丁格式,显然被称为源格式和编译格式,前者具有众所周知的特殊边界,其中像素指定补丁(实际上,其中可以有九种以上)。这很可能被称为源形式,因为像素在图像编辑器中会显示出来(然而,在某些情况下,它们可能不会显示出来;请参阅下文)。
根据apktool wiki;已编译—表单将这个元信息编码在一个特殊的(私有的)PNG块中;同一页声称这些块被称为npTc
,我尝试用基本的Linux实用程序来验证这一点,可以证实这一点:
$ strings search_frame.9.png
IHDR
TnpTc
yIDATX
[...]
另外,TweakPNG显示了这个区块。
所以,我实际问题的答案是:我认为那些1像素高/宽的图像是源形式的九个补丁,而它们实际上是经过编译的压缩格式。
由于似乎没有官方规范,而且这是问题的一部分,我只是在这里开始收集信息。
从一种形式转换为另一种形式的工具
aapt
(安卓sdk附带)有两个子命令,s[inglecrunch]
将一个带边框的九个补丁图像编译为分块/压缩格式(分别对输入和输出文件使用命令行选项-i
和-o
)c[runch]
获取一个资源目录(选项-S
)和一个输出目录的名称(选项-C
),遍历整个源目录,并根据需要创建目标子目录
;
(请注意:如果您忘记事先创建目标目录树的根,
aapt
不会出错,而是忙于$whatever,占用了整个CPU核心/线程的注意力和能力,因此有效地表明它很忙,而它至少没有达到用户的期望。(为了完整起见:我正在运行SDK 19.0.1)apktool
是一个自定义的java应用程序,它能够解包整个*.apk
文件,将*.xml
文件转换为纯文本文件,并将*.9.png
文件转换为其源格式。要将所有内容(可能修改的内容)放回
*.apk
归档中,您需要从原始包中手动提取一些文件,大多数zip
/unzip
实用程序应该能够做到这一点,但无需转换。在重新包装的过程中,apktool
显然使用了aapt
,它捆绑了一个版本。这个捆绑的
aapt
可能是从一个旧的SDK(19之前)中提取的,因为它显示了相同的缺陷(上面提到的紧密繁忙循环,以及几个segfault,其中一个简单的if
可以防止这种恶劣的行为(例如,当你不给c[runch]
子命令-C
选项时,它会segfault)),而它缺乏对s[ingleCrunch]
子命令的支持。xUltimate-d9pc
(这似乎是一个源-到经过处理的格式转换器,但我并没有真正测试它。)WebLaF
(同样未经测试;它似乎有来源,可以从中获得更多信息。)
九个补丁的格式
源窗体
大多数人已经知道,源形式的九个补丁确实包括图像的正常像素数据,以及一个额外的边界。我将尝试用一个简单的ASCII
表示来说明(并希望您的字体能够识别它的含义):
-------##--#####--##-------
-......XXXXXXXXXXXXX......-
-...XXX.............XXX...-
-..X...................X..-
-.X.....................X.- Legend
-.X.....................X.-
#X.......................X- (top and left borders)
#X.......................X- # denotes a "stretch"/"repeat" mark
-X.......XXXXXXXXX.......X- - denotes a "keep" mark
-X.......X.......X.......X#
#X.......X.......X.......X# (bottom and right borders)
#X.......X.......X.......X# # marks the content area (aka fill area)
-X.......X.......X.......X# - marks ... umm... what is it called?
-X.......XXXXXXXXX.......X-
#X.......................X- (image area)
#X.......................X- . pixel in "background" color
-.X.....................X.- X pixel in foreground color
-.X.....................X.-
-..X...................X..-
-...XXX.............XXX...-
-......XXXXXXXXXXXXX......-
----------#######----------
这是某种双盒,其外部有圆角。这种九块补丁的大小是27x22px,但实际图像大小只有25x20px;这也是它在应用程序中显示的最小大小,但它可以扩展到任意大小。
重复标记告诉框架,当图像被拉伸时,例如,当图像以26个像素的实际高度显示时,要重复哪些行,第7、8、11、12、15和16行,并且每行重复一次,以弥补屏幕上多余的行。这样可以确保两个框的边界始终保持1像素薄,并且外框的圆角始终具有相同的大小/半径。而标记区域将被充分拉伸(好吧,如果拉伸尺寸与原始尺寸的差异不是标记线的倍数,就会有不规则性,但在今天的160+dpi显示屏上,这可能无关紧要;然而,较少的重复/拉伸标记可能会更具性能,但话说回来…在今天的1.6+GHz四核手机上,这也无关紧要…)
重复/拉伸标记的值当我发布问题时,我不确定拉伸和重复之间是否有任何语义差异,但我仍然没有。
根据Android UI的9补丁简单指南,标记必须是实心黑色,以重复有问题的行/列,否则结果将是错误的,除非在屏幕上错误显示;这对于以前的SDK版本可能是正确的(文章来自2011年5月)。
然而,两个aapt
版本(SDK 19和apktool
1.5.2附带的版本)都抱怨边界中的任意颜色,例如:
ERROR: 9-patch image test/search_frame_.9.png malformed.
Ticks in transparent frame must be black or red.
Found at pixel #21 along top edge.
ERROR: 9-patch image test/tab_unselected_pressed_focused_holo.9.png malformed.
Must have one-pixel frame that is either transparent or white.
其中第二个文件已经是一个编译/处理过的九个补丁,而第一个不是。
我从"黑色或红色"消息中了解到,边框可能不包含#000000000(纯黑色,用于重复/拉伸标记)、#000ff0000(纯红色,可能用于相同标记)和#xx000000(黑色,具有任意透明度(xx>0),用于"保留"标记,可能也包含#xxFF0000,但我没有测试)以外的像素值。
我仍然不确定的是,红色和黑色是否有不同的语义,或者红色是否只是使用黑色背景的图像编辑器的替代品,例如,在黑色背景下,很难在视觉上区分#000000000和#001000000)。
编译/处理过的表格
SDK包含两个类android.graphics.NinePatch
和android.graphics.NinePatch_Delegate
,它们使用对本机库的调用,因此源代码不会给我们太多信息。我没有安装NDK,甚至不知道它是否为相关库提供了源代码。
我发现最好的是Android的答案:编译9个补丁文件,在可绘制文件夹之外使用?