Android: 如何编译.9.png文件以在drawable文件夹外使用?

5
我需要从drawable文件夹之外加载9-patch文件。这是为了让我的应用程序能够从服务器下载新的皮肤。我发现存储在drawable文件夹中的9-patch图像在制作.apk时被编译。但是,从assets文件夹读取的完全相同的文件没有9-patch块。因此,制作.apk的行为会将源9-patch文件编译到drawable文件夹中,但不会编译到assets目录中。
我如何自己编译9-patch文件,以便可以安装在assets目录中?是否有(批处理)工具可以将源文件转换为带有9-patch块的已编译版本?如果可能的话,我真的很想不必使用Eclipse / Ant构建.apk,然后拆开它以提取已编译的9-patch文件。
现在,我只想能够从assets目录中读取(例如,每个皮肤都有一个子目录),以保持简单。下一步是编译源图像以添加9-patch块。之后,我会考虑动态下载到/data文件夹-如果我无法编译9-patch文件,则添加服务器端的努力毫无意义。
2个回答

10

据我所知,没有简单的方法来实现这个。9-patch编译是由aapt完成的,而且非常简单:它会丢弃黑色边框并将其内容编码在PNG块中。你可以很容易地编写类似的工具。请注意,你甚至不需要使用相同的格式。如果你查看文档中的各种NinePatch API,你会发现你可以提交自己的"chunk"(编码拉伸区域和填充的区域)。一个chunk byte[]数组的结构在这里解释:

/**
 * This chunk specifies how to split an image into segments for
 * scaling.
 *
 * There are J horizontal and K vertical segments.  These segments divide
 * the image into J*K regions as follows (where J=4 and K=3):
 *
 *      F0   S0    F1     S1
 *   +-----+----+------+-------+
 * S2|  0  |  1 |  2   |   3   |
 *   +-----+----+------+-------+
 *   |     |    |      |       |
 *   |     |    |      |       |
 * F2|  4  |  5 |  6   |   7   |
 *   |     |    |      |       |
 *   |     |    |      |       |
 *   +-----+----+------+-------+
 * S3|  8  |  9 |  10  |   11  |
 *   +-----+----+------+-------+
 *
 * Each horizontal and vertical segment is considered to by either
 * stretchable (marked by the Sx labels) or fixed (marked by the Fy
 * labels), in the horizontal or vertical axis, respectively. In the
 * above example, the first is horizontal segment (F0) is fixed, the
 * next is stretchable and then they continue to alternate. Note that
 * the segment list for each axis can begin or end with a stretchable
 * or fixed segment.
 *
 * The relative sizes of the stretchy segments indicates the relative
 * amount of stretchiness of the regions bordered by the segments.  For
 * example, regions 3, 7 and 11 above will take up more horizontal space
 * than regions 1, 5 and 9 since the horizontal segment associated with
 * the first set of regions is larger than the other set of regions.  The
 * ratios of the amount of horizontal (or vertical) space taken by any
 * two stretchable slices is exactly the ratio of their corresponding
 * segment lengths.
 *
 * xDivs and yDivs point to arrays of horizontal and vertical pixel
 * indices.  The first pair of Divs (in either array) indicate the
 * starting and ending points of the first stretchable segment in that
 * axis. The next pair specifies the next stretchable segment, etc. So
 * in the above example xDiv[0] and xDiv[1] specify the horizontal
 * coordinates for the regions labeled 1, 5 and 9.  xDiv[2] and
 * xDiv[3] specify the coordinates for regions 3, 7 and 11. Note that
 * the leftmost slices always start at x=0 and the rightmost slices
 * always end at the end of the image. So, for example, the regions 0,
 * 4 and 8 (which are fixed along the X axis) start at x value 0 and
 * go to xDiv[0] and slices 2, 6 and 10 start at xDiv[1] and end at
 * xDiv[2].
 *
 * The array pointed to by the colors field lists contains hints for
 * each of the regions.  They are ordered according left-to-right and
 * top-to-bottom as indicated above. For each segment that is a solid
 * color the array entry will contain that color value; otherwise it
 * will contain NO_COLOR.  Segments that are completely transparent
 * will always have the value TRANSPARENT_COLOR.
 *
 * The PNG chunk type is "npTc".
 */
struct Res_png_9patch
{
    Res_png_9patch() : wasDeserialized(false), xDivs(NULL),
                       yDivs(NULL), colors(NULL) { }

    int8_t wasDeserialized;
    int8_t numXDivs;
    int8_t numYDivs;
    int8_t numColors;

    // These tell where the next section of a patch starts.
    // For example, the first patch includes the pixels from
    // 0 to xDivs[0]-1 and the second patch includes the pixels
    // from xDivs[0] to xDivs[1]-1.
    // Note: allocation/free of these pointers is left to the caller.
    int32_t* xDivs;
    int32_t* yDivs;

    int32_t paddingLeft, paddingRight;
    int32_t paddingTop, paddingBottom;

    enum {
        // The 9 patch segment is not a solid color.
        NO_COLOR = 0x00000001,

        // The 9 patch segment is completely transparent.
        TRANSPARENT_COLOR = 0x00000000
    };
    // Note: allocation/free of this pointer is left to the caller.
    uint32_t* colors;

    // Convert data from device representation to PNG file representation.
    void deviceToFile();
    // Convert data from PNG file representation to device representation.
    void fileToDevice();
    // Serialize/Marshall the patch data into a newly malloc-ed block
    void* serialize();
    // Serialize/Marshall the patch data
    void serialize(void* outData);
    // Deserialize/Unmarshall the patch data
    static Res_png_9patch* deserialize(const void* data);
    // Compute the size of the serialized data structure
    size_t serializedSize();
};

非常有用的信息,谢谢。你从哪里得到这个文件的?我显然缺少了一个重要的信息来源。不确定编写一个九宫格编译器会有多么琐碎(从来不喜欢计算机图形学),但我会发布任何半可接受的东西。希望其他人已经超越了我。 - Colin
它在android.git.kernel.org上。 - Romain Guy
2
经过一夜的思考,我现在倾向于编写一个Java方法来加载“源”九宫格文件,裁剪图像的1像素边框,并从1像素边框创建块(如上所述),而不是使用“编译器”。让这个方法读取已经打开的流,以便文件可以来自任何目录。我可以这样做,因为我倾向于缓存和重用位图,所以每个使用的九宫格图像文件只需要进行一次转换。 - Colin
如果draw9patch工具可以“另存为”已编译的版本,那就太好了。这将节省开发社区大量时间,避免重复劳动。 - Amir Uval
draw9patch不能编译9patch文件。它不了解最终的二进制格式,而是由aapt实现。 - Romain Guy

1

这是一个在我的情况下有效的解决方法。

我在我的应用程序中使用默认的9-patch图像作为消息气泡。我想创建可下载的“主题”,以更改气泡和其他内容,如字体颜色等。

在drawable文件夹中包含的*.9.png图像是默认值,它们包括图像周围的黑色像素:

Default Outgoing Image default_outgoing.9.png

“自定义”/主题气泡与默认气泡具有完全相同的尺寸和位置,仅有轻微变化,仍然使用与默认气泡相同的“块”区域,但这些不包括文件名中的黑色像素或.9

Pink Round Outgoing Image pink_round_outgoing.png

那么如何让自定义的代码仍然正常工作并且外观良好呢?下面是一些代码,用于获取自定义图像,从默认的9-patch图像中获取一些值,并将它们应用于自定义位图。
//Get the custom replacement image
Bitmap bitmap = BitmapFactory.decodeFile(folderpath + File.separator + "pink_round_outgoing.png");

//Get padding from the default 9-patch drawable
Drawable existingDrawable = ContextCompat.getDrawable(this, R.drawable.default_outgoing);
Rect padding = new Rect();
if (existingDrawable != null) {
    existingDrawable.getPadding(padding);
}

//Get 9-patch chunk from the default 9-patch drawable
Bitmap existingBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.default_outgoing);
byte[] chunk = existingBitmap.getNinePatchChunk();

//Finally create your custom 9-Patch drawable and set it to background
NinePatchDrawable d = new NinePatchDrawable(getResources(), bitmap, chunk, padding, null);
view.setBackground(d);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接