在运行时创建NinePatch / NinePatchDrawable

时间:2011-02-22 15:07:04

标签: android networking runtime nine-patch

我的Android应用程序要求通过从服务器端检索新颜色和图像来定制图形上的部件。其中一些图像是九个补丁图像。

我找不到创建和显示这些九个补丁图像(已通过网络检索)的方法。

检索九个补丁图像并将其作为位图保存在应用程序中。要创建NinePatchDrawable,您需要相应的NinePatch或NinePatch的块(byte[])。无法从资源中加载NinePatch,因为/res/drawable/中不存在图像。此外,为了创建NinePatch,您需要NinePatch的大块。所以,这一切都深入到了大块 那么问题是,如何从现有的Bitmap(包含NinePatch信息)格式化/生成块?

我搜索了Android源代码和Web,但我似乎找不到任何这样的例子。更糟糕的是,NinePatch资源的所有解码似乎都是本地完成的。

有没有人遇到过这类问题?

我的目标是API级别4,如果这很重要的话。

7 个答案:

答案 0 :(得分:55)

getNinePatchChunk效果很好。它返回null因为你给了Bitmap一个“源”九个补丁。它需要一个“编译”的九个图像。

Android世界中有两种类型的九种文件格式(“源”和“已编译”)。源版本是在任何地方添加1px透明边框的地方 - 当您将应用程序编译为.apk之后,aapt会将您的* .9.png文件转换为Android期望的二进制格式。这是png文件获取其“块”元数据的地方。 (read more

好的,现在开始营业了(你正在听DJ kanzure)。

  1. 客户端代码,如下所示:

    InputStream stream = .. //whatever
    Bitmap bitmap = BitmapFactory.decodeStream(stream);
    byte[] chunk = bitmap.getNinePatchChunk();
    boolean result = NinePatch.isNinePatchChunk(chunk);
    NinePatchDrawable patchy = new NinePatchDrawable(bitmap, chunk, new Rect(), null);
    
  2. 服务器端,您需要准备图像。您可以使用Android Binary Resource Compiler。这样就可以自动创建一个新的Android项目,将一些* .9.png文件编译成Android原生格式。如果您手动执行此操作,您实际上将创建一个项目并输入一些* .9.png文件(“源”文件),将所有内容编译为.apk格式,解压缩.apk文件,然后找到*。 9.png文件,这是您发送给客户的文件。

  3. 另外:我不知道BitmapFactory.decodeStream是否知道这些png文件中的npTc块,因此它可能会也可能不会正确处理图像流。 Bitmap.getNinePatchChunk的存在表明BitmapFactory可能 - 您可以在上游代码库中查找它。

    如果它不知道npTc块并且你的图像被大大搞砸了,那么我的答案会有所改变。

    您可以编写一个快速的Android应用程序来加载已编译的图像并吐出byte[]块,而不是将已编译的九个补丁图像发送到客户端。然后,将此字节数组与常规图像一起传输给客户端 - 没有透明边框,不是“源”九个图像,而不是“已编译”的九个图像。您可以直接使用块来创建对象。

    另一种方法是使用对象序列化将九个图像(NinePatch)发送到您的客户端,例如使用JSON或内置序列化器。

    编辑如果您确实需要构建自己的块字节数组,我首先要看do_9patchisNinePatchChunkRes_png_9patch和{ ResourceTypes.cpp中的{1}}。 Dmitry Skiba还有一个自制的npTc大块阅读器。我无法发布链接,所以如果有人可以编辑我很酷的答案。

    do_9patch: https://android.googlesource.com/platform/frameworks/base/+/gingerbread/tools/aapt/Images.cpp

    isNinePatchChunk:http://netmite.com/android/mydroid/1.6/frameworks/base/core/jni/android/graphics/NinePatch.cpp

    struct Res_png_9patch:https://scm.sipfoundry.org/rep/sipX/main/sipXmediaLib/contrib/android/android_2_0_headers/frameworks/base/include/utils/ResourceTypes.h

    Dmitry Skiba东西:http://code.google.com/p/android4me/source/browse/src/android/graphics/Bitmap.java

答案 1 :(得分:23)

如果您需要动态创建9Patches,请查看我提出的要点:https://gist.github.com/4391807

你传递任何位图,然后给它类似于iOS的上限。

答案 2 :(得分:6)

无需使用Android二进制资源编译器来编写已编译的9patch png,只需在android-sdk中使用aapt即可,命令行如下:
aapt.exe c -v -S /path/to/project -C /path/to/destination

答案 3 :(得分:5)

我创建了一个工具,用于从(未编译的)NinePatch位图创建NinePatchDrawable。 请参阅https://gist.github.com/knight9999/86bec38071a9e0a781ee

方法 NinePatchDrawable createNinePatchDrawable(Resources res, Bitmap bitmap) 帮助你。

例如,

    ImageView imageView = (ImageView) findViewById(R.id.imageview);
    Bitmap bitmap = loadBitmapAsset("my_nine_patch_image.9.png", this);
    NinePatchDrawable drawable = NinePatchBitmapFactory.createNinePatchDrawable(getResources(), bitmap);
    imageView.setBackground( drawable );

其中

public static final Bitmap loadBitmapAsset(String fileName,Context context) {
    final AssetManager assetManager = context.getAssets();
    BufferedInputStream bis = null;
    try {
        bis = new BufferedInputStream(assetManager.open(fileName));
        return BitmapFactory.decodeStream(bis);
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            bis.close();
        } catch (Exception e) {

        }
    }
    return null;
}

在此示例中,my_nine_patch_image.9.png位于资产目录下。

答案 4 :(得分:4)

所以你基本上想要按需创建一个NinePatchDrawable,不是吗?我尝试了以下代码,也许它适用于您:

InputStream in = getResources().openRawResource(R.raw.test);
Drawable d = NinePatchDrawable.createFromStream(in, null);
System.out.println(d.getMinimumHeight() + ":" + d.getMinimumHeight());

我认为这应该有效。您只需更改第一行即可从Web获取InputStream。 getNinePatchChunk()不打算根据文档从开发人员那里调用,并且将来可能会中断。

答案 5 :(得分:2)

工作和测试 - RUNTIME NINEPATCH CREATION

这是我对Android Ninepatch Builder的实现,你可以通过这个类和下面的代码示例通过提供任何Bitmap在运行时创建NinePatches

NinePatchBuilder builder=new NinePatchBuilder(getResources(), bitmap);
NinePatchDrawable drawable=builder.addXCenteredRegion(2).addYCenteredRegion(2).build();

//or add multiple patches

NinePatchBuilder builder=new NinePatchBuilder(getResources(), bitmap);
builder.addXRegion(30,2).addXRegion(50,1).addYRegion(20,4);
byte[] chunk=builder.buildChunk();
NinePatch ninepatch=builder.buildNinePatch();
NinePatchDrawable drawable=builder.build();

//Here if you don't want ninepatch and only want chunk use
NinePatchBuilder builder=new NinePatchBuilder(width, height);
byte[] chunk=builder.addXCenteredRegion(1).addYCenteredRegion(1).buildChunk();

现在我们可以使用ninepatch builder来创建NinePatch或NinePatchDrawable或创建NinePatch Chunk。

示例:

$ /path/to/bin/<project-name> -J-Xms128M -J-Xmx512m -J-server

只需将NinePatchBuilder类代码复制粘贴到java文件中,然后使用这些示例在应用运行时期间动态创建NinePatch,并获得任何分辨率。

答案 6 :(得分:0)

Bitmap类提供了执行此操作的方法yourbitmap.getNinePatchChunk()。我从来没有使用它,但它似乎就是你想要的。