好的,自从我对九个补丁图像进行了更多调查后,我学到了什么:
主要的错误可能是认为九个补丁的重复/拉伸/保留元数据总是编码在图像文件的边界像素中。这不是正确的。
事实上,有两种九补丁格式,显然称为源格式和编译格式,前者具有众所周知的特殊边框,其中像素指定补丁(其中您可以有超过九个,确实)。这很可能被称为 source 形式,因为像素在图像编辑器中会显示出来(但是,在某些情况下它们可能不会;见下文)。
根据the apktool wiki,第二个——已编译——表单将元信息编码在一个特殊的(私有)PNG 块中;同一页面声称这些块被称为npTc,我尝试使用基本的 Linux 实用程序验证这一点可以证实这一点:
$ strings search_frame.9.png
IHDR
TnpTc
yIDATX
[...]
TweakPNG 也显示这个块。
所以,我的实际问题的答案是:我认为那些 1 像素高/宽的图像是源形式的九个补丁,而它们实际上是编译后的压缩格式。
由于似乎没有官方规范,这是问题的一部分,我只是在这里开始收集信息。
从一种形式转换为另一种形式的工具
-
aapt(随 Android sdk 一起提供)有两个子命令,
-
s[inglecrunch]编译单个边框九个补丁图像到chunked/crunched格式(使用命令行选项-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 之前)中提取的,因为它显示了相同的缺陷(上面提到的紧忙循环和几个段错误,其中一个简单的 if 可以防止这种讨厌的行为(例如,当您不将-C 选项提供给c[runch] 子命令时,它会出现段错误)),而它缺乏对s[ingleCrunch] 子命令的支持。
xUltimate-d9pc(这似乎是 source-to-crunched-格式转换器,但我没有真正测试一下。)
WebLaF(也未经测试;它似乎有来源,可以从中获得更多信息。)
九个补丁的格式
源表格
大多数人都已经知道,九个补丁的 source 形式确实包含图像的正常像素数据以及额外的边框。我将尝试用一个简单的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——这也是它可以在应用程序中显示的最小尺寸,但可以拉伸到任意大小。
重复标记告诉框架在拉伸图像时要重复哪些行,例如。 G。当图像以 26 像素的实际高度显示时,第 7、8、11、12、15 和 16 行并重复一次,以弥补屏幕上的多余行。这样可以确保两个框的边框始终保持 1 像素薄,并且外框的圆角始终具有相同的大小/半径。虽然标记区域将被充分拉伸(如果拉伸尺寸与原始尺寸的差异不是标记线的倍数,则会出现不规则性,但在今天的 160+ dpi 显示器上,这可能并不重要;然而,更少的重复/拉伸标记可能会更高效,但话又说回来......在今天的 1.6+ GHz 四核手机上,这也无关紧要......)
重复/拉伸标记的值
当我发布问题时,我不确定 stretching 和 repeating 之间是否有任何语义差异,我仍然没有。
根据A simple guide to 9-patch for Android UI,标记必须为纯黑色,以重复有问题的行/列,否则结果将是错误的,不会通知用户,除非屏幕上显示不正确;这对于以前的 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.
第二个文件已经是经过编译/处理的九个补丁,而第一个不是。
我从“黑色或红色”消息中读到的是,边框可能不包含除#00000000(纯黑色,用于重复/拉伸标记)、#00ff0000(纯红色,可能相同)以外的像素值,和#xx000000(带有任意透明度的黑色(xx>0)用于“保留”标记,也可能是#xxFF0000,但我没有测试过)。
我仍然不确定红色和黑色是否具有不同的语义,或者红色是否只是使用黑色背景的图像编辑器的替代品,在视觉上很难区分 #00000000 和 #01000000,例如)。
编译/压缩表格
SDK 包含两个类android.graphics.NinePatch 和android.graphics.NinePatch_Delegate,它们利用对本机库的调用,因此源代码并没有提供太多信息。我没有安装 NDK,我什至不知道它是否提供相关库的源代码。
我发现最好的是answer 到Android: compiling 9-patch files to be used outside of the drawable folder?