NinePatchDrawable不会从块填充

NinePatchDrawable需要帮助:

我的应用程序可以从networking下载主题。 几乎所有的事情都很好,除了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已经被编译,这意味着它们包含所需的块。

我如何将位图转换为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)应该填充variablesrect与从块获得的填充,不是吗? 但事实并非如此。

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

谢谢阅读。

Solutions Collecting From Web of "NinePatchDrawable不会从块填充"

最后,我做到了。 Android没有正确解释块数据。 可能有错误。 所以你必须自己反序列化块才能得到填充数据。

开始了:

 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)); } 

它会完美的工作!

实际上它比这个稍微复杂一点,但是它归结起来很简单:

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

整个九个补丁的API有点傻:

  • decodeByteArray()调用nativeDecodeByteArray() ,这比在一个ByteArrayInputStream上的nativeDecodeStream()更有效率,但显然开发人员从来没有想过要从内存中解码九个补丁。
  • 填充矩形只能使用九个补丁,因此它更适合作为NinePatch而不是BitmapFactory 。 可悲的是, NinePatch.java并不仅仅是一个将位图和九块大块传递给绘图方法的NinePatch.draw()由于调用了NinePatch.draw() ,大多数的NinePatch.draw()调用都不是线程安全的mRect.set(location) )。
  • NinePatchDrawable没有提供采用NinePatch和填充矩形的方法,这使得NinePatch在应用程序代码中有些无用(除非你想自己做填充)。 没有NinePatchDrawable.getNinePatch()NinePatch.getBitmap()

这个评论总结得非常好:

啊。 decodeStream约定是我们已经分配了pad rect,但是如果位图没有九块,那么这个pad就会被忽略。 如果我们可以改变这个懒惰地分配/分配矩形,我们可以避免GC使得新的Rect只能放在地板上。

我的修复很简单:

 public final class NinePatchWrapper { private final Bitmap mBitmap; private final Rect mPadding; /** * The caller must ensure that that bitmap and padding are not modified after * this method returns. We could copy them, but Bitmap.createBitmap(Bitmap) * does not copy the nine-patch chunk on some Android versions. */ public NinePatchWrapper(Bitmap bitmap, Rect padding) { mBitmap = bitmap; mPadding = padding; } public NinePatchDrawable newDrawable(Resources resources) { return new NinePatchDrawable(mBitmap, mBitmap.getNinePatchChunk(), mPadding, null); } } ... public NinePatchWrapper decodeNinePatch(byte[] byteArray, int density) { Rect padding = new Rect(); ByteArrayInputStream stream = new ByteArrayInputStream(byteArray); Bitmap bitmap = BitmapFactory.decodeStream(stream, padding, null); bitmap.setDensity(density); return new NinePatchWrapper(bitmap, padding); } 

未经testing,因为它大大简化了。 特别是,你可能想检查九块补丁是否有效。

我从来没有见过一个例子, Padding不包含在9-patch中,

在这里输入图像说明

要做到这一点,你应该首先构build一个NinePatch ,然后创build你可以从它绘制:

 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); 

晚会有点迟,但这是我解决问题的方法:

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

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