NinePatchDrawable does not get padding from chunk

后端 未结 4 1420
鱼传尺愫
鱼传尺愫 2021-02-06 03:59

I need help with NinePatchDrawable:

My app can download themes from the network. Almost all things work fine, except 9-Patch PNGs.

final Bitmap bubble =          


        
4条回答
  •  悲&欢浪女
    2021-02-06 04:35

    It's actually slightly more complicated than that, but what it boils down to is pretty simple:

    The padding rect is returned by BitmapFactory.decodeStream(InputStream, Rect, Options). There is no version of decodeByteArray() which can return the padding rect.

    The whole nine-patch API is a bit silly:

    • decodeByteArray() calls nativeDecodeByteArray(), which is presumably more efficient than nativeDecodeStream() on a ByteArrayInputStream, but obviously the devs never expected you to want to decode a nine-patch from memory.
    • The padding rect is only used by nine-patches, so it makes more sense for it to be part of NinePatch instead of BitmapFactory. Sadly, NinePatch.java is not much more than a wrapper that passes the bitmap and nine-patch chunk to drawing methods (and most of the NinePatch.draw() calls aren't thread-safe due to the call to mRect.set(location)).
    • NinePatchDrawable doesn't offer a way to take a NinePatch and a padding rect, which makes NinePatch somewhat useless in application code (unless you want to do the padding yourself). There is no NinePatchDrawable.getNinePatch() or NinePatch.getBitmap().

    This comment sums it up pretty well:

    ugh. The decodeStream contract is that we have already allocated the pad rect, but if the bitmap does not had a ninepatch chunk, then the pad will be ignored. If we could change this to lazily alloc/assign the rect, we could avoid the GC churn of making new Rects only to drop them on the floor.

    My fix is fairly simple:

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

    Untested, since it's greatly simplified. In particular, you might want to check that the nine-patch chunk is valid.

提交回复
热议问题