Create a Ninepatch/Ninepatchdrawable in Runtime

Building a 9 patch drawable at runtime

Putting together all I've spotted so far, and honing it a bit:

  • There doesn't seem to be a difference in the Res_png_9patch structure (which the byte chunk goes into) between android versions (see http://code.metager.de/source/xref/android), so that doesn't look like the cause.

  • Other answers about nine patch drawables suggest that each region (especially stretchable ones) should be at least 2x2 pixels (if not 3x3), but outside of that smaller is better.

  • However, the way some of the byte chunk is allocated looks like it could be updated. Try setting the 4th byte to 9 (the number of patches, I think), adding 7 more NO_COLORs to the the end and moving its size up to 56 + (7 x 4) = 84 bytes

Creating and combining NinePatchDrawables at runtime

I have finally come up with a way to avoid using a blank dummy NinePatch source as necessary my compromise solution above.

Based on this excellent solution to a similar problem, I have written a utility method to extract the padding info from a NinePatch resource. Using this, I can extract the necessary padding from the adjacent NinePatch regions, and apply them to my new slice. Hope this is helpful to others.

public static Rect getNinePatchPadding(Context context, int drawableId) {
Rect mPadding = new Rect();
Bitmap bm = BitmapFactory.decodeResource(context.getResources(), drawableId);
final byte[] chunk = bm.getNinePatchChunk();
if (!NinePatch.isNinePatchChunk(chunk)) return null;
ByteBuffer byteBuffer = ByteBuffer.wrap(chunk).order(ByteOrder.nativeOrder());

// Skip to padding
for (int i = 0; i < 12; i++) {
byteBuffer.get();
}

mPadding.left = byteBuffer.getInt();
mPadding.top = byteBuffer.getInt();
mPadding.right = byteBuffer.getInt();
mPadding.bottom = byteBuffer.getInt();

return mPadding;
}

Android: how to create a 9patch image from an inputstream?

the createFromStream() method is part of the Drawable class (which NinePatchDrawable extends) So it is returning to you a plain Drawable object, rather than a nine patch. That is why your button turns out looking that way I suspect.

Where is the image that you are trying to make in to a nine-patch? It seems from your example that it may be included in with your application resources (perhaps in one of the drawable folders?) if this is the case is there a reason that using the ID of the drawable will not work for your situation? you should be able to do something like this:

button.setBackgroundResource(R.drawable.btn_default_normal);

If I am missing something(which I definitely could be.) and there is a reason you can't use the resource ID. From the docs it looks like your second block of code is closer to what you'll want.

But the constructor you're using is deprecated. Try using this one instead. which would look like this:

InputStream in = MyClass.class.getResourceAsStream("/images/btn_default_normal.9.png");
Bitmap bitmap = BitmapFactory.decodeStream(in);
byte[] chunk = bitmap.getNinePatchChunk();
NinePatchDrawable drawable = new NinePatchDrawable(getResources(), bitmap, chunk, new Rect(), null);
button.setBackgroundDrawable(drawable);

The only difference being the first paramter should be your Applications Resources object.

However, honestly I could've sworn I read somewhere at some point that the system was incapable of working with NinePatchDrawables dynamically as objects. Even though the NinePatchDrawable object exists, I was under the impression that it was not working / not intended to be part of the public APIs

EDIT:

Does the answer on this question help? Create a NinePatch/NinePatchDrawable in runtime

Also my answer at the bottom of that question reminds me exactly why I was under the impression that it was not working / not indeded for part of the public APIs the docs for the getNinePatchChunk say

Returns an optional array of private data, used by the UI system for some bitmaps. Not intended to be called by applications.

But it looks like they managed to get it working anyway.

Set a NinePatch Background Programmatically

What I end up doing is to use scenario 1 NinePatch (Stretch is bigger then Padding - which I didn't want)

and set up the padding zone manually, with this code:

NinePatchDrawable ninepatch;
Bitmap image = BitmapFactory.decodeResource(getResources(),R.drawable.ninepatch_background);
if (image.getNinePatchChunk()!=null){
byte[] chunk = image.getNinePatchChunk();
Rect paddingRectangle = new Rect(30, 0, 30, 50);
ninepatch = new NinePatchDrawable(getResources(), image, chunk, paddingRectangle, null);
}
int sdk = android.os.Build.VERSION.SDK_INT;
if(sdk < android.os.Build.VERSION_CODES.JELLY_BEAN) {
linLayout.setBackgroundDrawable(ninepatch);
} else {
linLayout.setBackground(ninepatch);
}


Related Topics



Leave a reply



Submit