NinePatchDrawable不会从块中获取填充

时间:2012-06-16 18:27:23

标签: android drawable padding nine-patch

我需要NinePatchDrawable的帮助:

我的应用可以从网络下载主题。 除了9-Patch PNG之外,几乎所有的东西都能正常工作。

final Bitmap bubble = getFromTheme("bubble");
if (bubble == null) return null;

final byte[] chunk = bubble.getNinePatchChunk();
if (!NinePatch.isNinePatchChunk(chunk)) return null;

NinePatchDrawable d = new NinePatchDrawable(getResources(), bubble, chunk, new Rect(), null);
v.setBackgroundDrawable(d);

d = null;
System.gc();

getFromTheme()从SD卡加载位图。 9-Patch PNG已经编译,这意味着它们包含了所需的块。

我将Bitmap转换为NinePatchDrawable对象的方式似乎正在起作用,因为图像是可拉伸的,而且我画了它。

唯一不起作用的是填充。我已经尝试将填充设置为这样的视图:

final Rect rect = new Rect();   // or just use the new Rect() set
d.getPadding(rect);             // in the constructor
v.setPadding(rect.left, rect.top, rect.right, rect.bottom);

d.getPadding(rect)应该用填充从块中填充变量rect,不应该吗?但事实并非如此。

结果:TextView(v)不显示9-Patch图像内容区域中的文本。每个坐标中的填充都设置为0.

感谢阅读。

4 个答案:

答案 0 :(得分:17)

最后,我做到了。 Android没有正确解释块数据。可能有bug。因此,您必须自己反序列化块以获取填充数据。

我们走了:

package com.dragonwork.example;

import android.graphics.Rect;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;

class NinePatchChunk {

    public static final int NO_COLOR = 0x00000001;
    public static final int TRANSPARENT_COLOR = 0x00000000;

    public final Rect mPaddings = new Rect();

    public int mDivX[];
    public int mDivY[];
    public int mColor[];

    private static void readIntArray(final int[] data, final ByteBuffer buffer) {
        for (int i = 0, n = data.length; i < n; ++i)
            data[i] = buffer.getInt();
    }

    private static void checkDivCount(final int length) {
        if (length == 0 || (length & 0x01) != 0)
            throw new RuntimeException("invalid nine-patch: " + length);
    }

    public static NinePatchChunk deserialize(final byte[] data) {
        final ByteBuffer byteBuffer =
            ByteBuffer.wrap(data).order(ByteOrder.nativeOrder());

        if (byteBuffer.get() == 0) return null; // is not serialized

        final NinePatchChunk chunk = new NinePatchChunk();
        chunk.mDivX = new int[byteBuffer.get()];
        chunk.mDivY = new int[byteBuffer.get()];
        chunk.mColor = new int[byteBuffer.get()];

        checkDivCount(chunk.mDivX.length);
        checkDivCount(chunk.mDivY.length);

        // skip 8 bytes
        byteBuffer.getInt();
        byteBuffer.getInt();

        chunk.mPaddings.left = byteBuffer.getInt();
        chunk.mPaddings.right = byteBuffer.getInt();
        chunk.mPaddings.top = byteBuffer.getInt();
        chunk.mPaddings.bottom = byteBuffer.getInt();

        // skip 4 bytes
        byteBuffer.getInt();

        readIntArray(chunk.mDivX, byteBuffer);
        readIntArray(chunk.mDivY, byteBuffer);
        readIntArray(chunk.mColor, byteBuffer);

        return chunk;
    }
}

使用上面的课程如下:

final byte[] chunk = bitmap.getNinePatchChunk();
if (NinePatch.isNinePatchChunk(chunk)) {
    textView.setBackgroundDrawable(new NinePatchDrawable(getResources(),
          bitmap, chunk, NinePatchChunk.deserialize(chunk).mPaddings, null));
}

它会完美运作!

答案 1 :(得分:4)

实际上稍微比这更复杂,但它归结为非常简单:

填充矩形由BitmapFactory.decodeStream(InputStream, Rect, Options)返回。没有decodeByteArray()的版本可以返回填充矩形。

整个九补丁API有点傻:

  • decodeByteArray()调用nativeDecodeByteArray(),这可能比nativeDecodeStream()上的ByteArrayInputStream更有效,但显然开发人员从未希望您想要解码九个补丁存储器中。
  • 填充矩形九个补丁使用,因此它更有意义成为NinePatch而不是BitmapFactory的一部分。遗憾的是,NinePatch.java只不过是一个将位图和九补丁块传递给绘图方法的包装器(由于对{{{{{}}的调用,大多数NinePatch.draw()调用都不是线程安全的。 1}})。
  • NinePatchDrawable没有提供一种方法来获取mRect.set(location)和填充矩形,这使得NinePatch在应用程序代码中有些无用(除非您想自己进行填充)。没有NinePatchNinePatchDrawable.getNinePatch()

This comment总结得很好:

  

啊。 NinePatch.getBitmap()合同是我们已经分配的               pad rect,但如果位图没有9patch块,               然后垫将被忽略。如果我们可以改变这个懒惰               分配/分配矩形,我们可以避免制作新的GC流失               decodeStream只能将他们放在地板上。

我的修复很简单:

Rect

未经测试,因为它大大简化了。特别是,您可能希望检查九补丁块是否有效。

答案 2 :(得分:0)

我从来没有见过一个例子,其中Padding不包含在9补丁中,如下所示:

enter image description here

要做到这一点,你应该首先构建一个NinePatch,然后从中创建你的Drawable:

NinePatch ninePatch = new NinePatch(bitmap, chunk, srcName);
NinePatchDrawable d = new NinePatchDrawable(res, ninePatch);

但是,您似乎正在构建一个带有空矩形的Drawable:

NinePatchDrawable d = new NinePatchDrawable(getResources(), bubble, chunk, new Rect(), null);

如果要以编程方式指定填充,请尝试以下操作:

Rect paddingRectangle = new Rect(left, top, right, bottom);
NinePatchDrawable d = new NinePatchDrawable(getResources(), bubble, chunk, paddingRectangle, null);

答案 3 :(得分:0)

派对有点晚了,但这是我解决它的方法:

我使用NinePatchDrawable提供的解码器方法,它正确读取填充:

     var myDrawable = NinePatchDrawable.createFromStream(sr, null);