在运行时构建9 patch可绘制对象

14
我成功地在Android 3.0+上使用Brian Griffey提供的绝妙代码片段(在这里找到)动态构建了一个9patch可绘制对象。
基本上,我从网络中加载原始(无补丁)图像文件,并且文件名包含我需要使用的插入区域来按比例缩放图像。然后,我使用上面找到的类与这些值,并将图像应用为各种元素的背景(例如 TextView , ImageButton , Button , ViewGroup 等)。
正如您在此处所看到的,这完美地运行:

Android 3.0+ result

然而,在Android 2.3.x上运行相同的代码会产生以下结果:

Android 2.x result

我已经查看了Android用于解析9patch图像的源代码(这里这里),但是没有找到使其正常工作的方法。我尝试了几乎所有可以想到的方法,但都无济于事。
记录一下,9patch图像在每个轴上有三列,一个固定的、一个可伸缩的和一个固定的。
希望有人在此之前解决了这个问题。
提前感谢。 编辑 我只对在Android 2.3及以上版本上复制此行为感兴趣(最初是2.x)。 编辑 #2 这个要点描述了 我正在尝试做的事情 + 源图像:源图像 编辑 #3 图像大小为 22px/58px(宽度/高度),插入值为 14/6/14/6(上/左/下/右)。

你如何应用生成的9patch? - a.bertucci
@mr_archano 我使用上面的代码片段中的类,并传递与我想要显示的图像的可缩放部分匹配的cap insets。这将生成一个NinePatchDrawable,然后我将其设置为TextView的背景。 - Andre
好的,我明白了。我只是想知道Drawable.setBounds()是否与这个问题有关。 - a.bertucci
很遗憾,情况似乎并非如此。不过还是感谢您的意见。 - Andre
你是否在Android 2.3和2.2上测试过你的问题?这可能是JDK版本的问题。 - alexei burmistrov
显示剩余5条评论
4个回答

20

我更新了代码后,这个功能对我来说是可以正常工作的。我认为颜色尺寸某种原因让它感到不满意(根据 Android 源代码中的评论,每个补丁都有一个颜色提示,在这种情况下设置少于 9 个部分似乎会引起问题)。我还没有用你的图像进行测试。

public static NinePatch createFixedNinePatch(Resources res, Bitmap bitmap, int top, int left, int bottom, int right, String srcName){
    ByteBuffer buffer = getByteBufferFixed(top, left, bottom, right);
    NinePatch patch = new NinePatch(bitmap, buffer.array(), srcName);
    return patch;
}

public static ByteBuffer getByteBufferFixed(int top, int left, int bottom, int right) {
    //Docs check the NinePatchChunkFile
    ByteBuffer buffer = ByteBuffer.allocate(84).order(ByteOrder.nativeOrder());
    //was translated
    buffer.put((byte)0x01);
    //divx size
    buffer.put((byte)0x02);
    //divy size
    buffer.put((byte)0x02);
    //color size
    buffer.put(( byte)0x09);

    //skip
    buffer.putInt(0);
    buffer.putInt(0);

    //padding
    buffer.putInt(0);
    buffer.putInt(0);
    buffer.putInt(0);
    buffer.putInt(0);

    //skip 4 bytes
    buffer.putInt(0);

    buffer.putInt(left);
    buffer.putInt(right);
    buffer.putInt(top);
    buffer.putInt(bottom);
    buffer.putInt(NO_COLOR);
    buffer.putInt(NO_COLOR);
    buffer.putInt(NO_COLOR);
    buffer.putInt(NO_COLOR);
    buffer.putInt(NO_COLOR);
    buffer.putInt(NO_COLOR);
    buffer.putInt(NO_COLOR);
    buffer.putInt(NO_COLOR);
    buffer.putInt(NO_COLOR);
    return buffer;
}

谢谢,但是top、left、bottom和right的正确值是多少?目前,我使用top = bitmapWidth / 4,left = bitmapHeight / 2,bottom = bitmapWidth - left,right = bitmapHeight- left。背景很好,但内容(按钮中的文本)仍然超出图像。如何使按钮中的内容居中于位图中间? - Paul

5

综合我目前所观察到的,并稍加磨合:

  • 在不同版本的Android中(请参见http://code.metager.de/source/xref/android),Res_png_9patch结构(其中包含字节块)似乎没有区别,因此这看起来不是原因。

  • 关于九宫格图案的其他答案表明,每个区域(特别是可拉伸的区域)应至少为2x2像素(如果不是3x3),但除此之外小一些更好。

  • 然而,某些字节块分配的方式似乎可以更新。尝试将第4个字节设置为9(我认为是补丁的数量),在末尾添加7个NO_COLOR并将其大小增加到56 +(7 x 4)= 84字节


我已经尝试了我能想到的几乎所有组合。那个解释的问题在于它没有定义如何标记可伸缩和固定的部分(即使我知道它们总是SFSxSFS)。我还比较了通过正常的.9.png文件加载的相同图像的字节数据和我正在生成的一个,将一个复制到另一个(.9.png块到生成版本块)会产生相同的效果。 - Andre
如果我通过appt/.9png正常方法加载图像,它可以正常工作。我添加了一个gist,展示了我的源代码和图片。 - Andre
1
谢谢(该链接确实解释了如何定义可伸缩的部分,但阅读起来并不容易)。然而,如果我理解你的数字正确,中间的可伸缩部分大小为零? - Neil Townsend
1
也许我误解了你的意思,但是左边和右边都是从左边开始计算的,所以我会期望你说的是14/6/44/16(上/左/下/右),这样可以得到一个10x30像素(宽 x 高)的中间部分。或者我理解错了你的意思吗? - Neil Townsend
有兴趣问一下,为什么要点踩?如果我的回答有待改进,请告诉我如何改进。 - Neil Townsend
显示剩余4条评论

1

这并没有回答我的问题,我的问题与Android 2.3版本有关。 - Andre

0

虽然不是回答问题的方法,但这是一个有用的解决方法...

我一直觉得在运行时处理9-patch文件很棘手。我已成功地使用FrameLayout和文本的自动调整大小来实现我想要的效果。如果您的应用程序只是使用此9-patch,而不是允许用户将其作为图像下载(类似于Android 9-patch图像生成器),我建议您研究一下这个。


谢谢,但用户确实需要下载图像,并且它需要应用于任意大小的元素,所以不幸的是,在这种情况下这种方法行不通。 - Andre

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