Crear un NinePatch / NinePatchDrawable en tiempo de ejecución

Tengo un requisito en mi aplicación de Android que las partes de los gráficos deben ser personalizables, recuperando nuevos colores e imágenes desde el lado del servidor. Algunas de estas imágenes son imágenes de nueve parches.

No puedo encontrar una forma de crear y mostrar estas imágenes de nueve parches (que se han recuperado a través de la red).

Las imágenes de nueve parches se recuperan y guardan en la aplicación como Bitmaps. Para crear un NinePatchDrawable , necesita el NinePatch correspondiente o el fragmento ( byte[] ) del NinePatch. El NinePatch no se puede cargar desde los recursos, ya que las imágenes no existen en /res/drawable/ . Además, para crear el NinePatch, necesitas el trozo del NinePatch. Así que, todos los ejercicios hasta el pedazo.
La pregunta es entonces, ¿cómo formatear / generar el fragmento de un Bitmap existente (que contiene la información de NinePatch)?

He buscado a través del código fuente de Android y la Web y no puedo encontrar ningún ejemplo de esto. Para empeorar las cosas, toda la decodificación de los recursos de NinePatch parece hacerse de forma nativa.

¿Alguien ha tenido alguna experiencia con este tipo de problema?

Estoy apuntando API nivel 4, si eso es de importancia.

getNinePatchChunk funciona muy bien. Devolvió nulo porque estaba dando a Bitmap una "fuente" nuevepatch. Necesita una imagen "compilada" de nueve paquetes.

Hay dos tipos de formatos de archivo de nueve paquetes en el mundo de Android ("fuente" y "compilado"). La versión de origen es donde se agrega el borde de transparencia 1px en todas partes. Cuando compila su aplicación en un archivo .apk más tarde, aapt convertirá sus archivos * .9.png al formato binario que espera Android. Aquí es donde el archivo png obtiene su "chunk" metadatos. ( Leer más )

Bueno, ahora a los negocios (estás escuchando DJ kanzure).

  1. Código de cliente, algo así:

     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. Del lado del servidor, usted necesita preparar sus imágenes. Puede utilizar el compilador binario de recursos de Android . Esto automatiza parte del dolor de crear un nuevo proyecto Android sólo para compilar algunos archivos * .9.png en el formato nativo de Android. Si lo hicieras manualmente, básicamente harías un proyecto y lanzarías algunos archivos * .9.png (archivos "fuente"), compilaría todo en el formato .apk, descomprimiría el archivo .apk y encontraría el archivo *. 9.png archivo, y que es el que enviar a sus clientes.

También: No sé si BitmapFactory.decodeStream sabe sobre el pedazo de npTc en estos archivos del png, así que puede o no puede tratar el flujo de la imagen correctamente. La existencia de Bitmap.getNinePatchChunk sugiere que BitmapFactory podría … podrías ir a buscarlo en la base de código ascendente.

En el caso de que no sepa sobre el pedazo de npTc y sus imágenes se estropean para arriba perceptiblemente, entonces mi respuesta cambia un poco.

En lugar de enviar las imágenes compiladas de nueve paquetes al cliente, escribes una rápida aplicación de Android para cargar imágenes compiladas y escupir el fragmento de byte[] . Entonces, usted transmite esta matriz del byte a sus clientes junto con una imagen regular – ningunos bordes transparentes, no la "nueve" imagen de la fuente, no "nueve" imagen compilada. Puede utilizar directamente el trozo para crear su objeto.

Otra alternativa es utilizar la serialización de objetos para enviar imágenes de nueve paquetes ( NinePatch ) a sus clientes, como con JSON o el serializador incorporado.

Editar Si realmente necesitas construir tu propia matriz de bytes, empezaría por ver do_9patch , isNinePatchChunk , Res_png_9patch y Res_png_9patch::serialize() en ResourceTypes.cpp. También hay un lector de trozos npTc hecho en casa de Dmitry Skiba. No puedo publicar enlaces, así que si alguien puede editar mi respuesta, sería genial.

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 cosas: http://code.google.com/p/android4me/source/browse/src/android/graphics/Bitmap.java

Si necesitas crear 9Patches sobre la marcha, mira esto que hice: https://gist.github.com/4391807

Se pasa cualquier mapa de bits y, a continuación, darle tapa inserciones similares a iOS.

Creo una herramienta para crear NinePatchDrawable de (sin compilar) NinePatch bitmap. Consulte https://gist.github.com/knight9999/86bec38071a9e0a781ee .

El método NinePatchDrawable createNinePatchDrawable(Resources res, Bitmap bitmap) ayuda.

Por ejemplo,

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

dónde

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

En este caso de ejemplo, my_nine_patch_image.9.png está en el directorio assets.

No hay necesidad de utilizar Android Binario Resource Compiler para preparar compilado 9patch pngs, sólo con aapt en android-sdk está bien, la línea de comandos es como esto:
aapt.exe c -v -S /path/to/project -C /path/to/destination

Así que básicamente quieres crear un NinePatchDrawable a petición, ¿no? He probado el siguiente código, tal vez funcione para usted:

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

Creo que esto debería funcionar. Sólo tienes que cambiar la primera línea para obtener el InputStream de la web. getNinePatchChunk() no está destinado a ser llamado de los desarrolladores de acuerdo con la documentación, y podría romperse en el futuro.

TRABAJAR Y PRUEBAS

Esta es mi implementación de andine Ninepatch Builder, puede crear NinePatches en Runtime a través de esta clase y ejemplos de código a continuación suministrando cualquier Bitmap

 public class NinePatchBuilder { int width,height; Bitmap bitmap; Resources resources; private ArrayList<Integer> xRegions=new ArrayList<Integer>(); private ArrayList<Integer> yRegions=new ArrayList<Integer>(); public NinePatchBuilder(Resources resources,Bitmap bitmap){ width=bitmap.getWidth(); height=bitmap.getWidth(); this.bitmap=bitmap; this.resources=resources; } public NinePatchBuilder(int width, int height){ this.width=width; this.height=height; } public NinePatchBuilder addXRegion(int x, int width){ xRegions.add(x); xRegions.add(x+width); return this; } public NinePatchBuilder addXRegionPoints(int x1, int x2){ xRegions.add(x1); xRegions.add(x2); return this; } public NinePatchBuilder addXRegion(float xPercent, float widthPercent){ int xtmp=(int)(xPercent*this.width); xRegions.add(xtmp); xRegions.add(xtmp+(int)(widthPercent*this.width)); return this; } public NinePatchBuilder addXRegionPoints(float x1Percent, float x2Percent){ xRegions.add((int)(x1Percent*this.width)); xRegions.add((int)(x2Percent*this.width)); return this; } public NinePatchBuilder addXCenteredRegion(int width){ int x=(int)((this.width-width)/2); xRegions.add(x); xRegions.add(x+width); return this; } public NinePatchBuilder addXCenteredRegion(float widthPercent){ int width=(int)(widthPercent*this.width); int x=(int)((this.width-width)/2); xRegions.add(x); xRegions.add(x+width); return this; } public NinePatchBuilder addYRegion(int y, int height){ yRegions.add(y); yRegions.add(y+height); return this; } public NinePatchBuilder addYRegionPoints(int y1, int y2){ yRegions.add(y1); yRegions.add(y2); return this; } public NinePatchBuilder addYRegion(float yPercent, float heightPercent){ int ytmp=(int)(yPercent*this.height); yRegions.add(ytmp); yRegions.add(ytmp+(int)(heightPercent*this.height)); return this; } public NinePatchBuilder addYRegionPoints(float y1Percent, float y2Percent){ yRegions.add((int)(y1Percent*this.height)); yRegions.add((int)(y2Percent*this.height)); return this; } public NinePatchBuilder addYCenteredRegion(int height){ int y=(int)((this.height-height)/2); yRegions.add(y); yRegions.add(y+height); return this; } public NinePatchBuilder addYCenteredRegion(float heightPercent){ int height=(int)(heightPercent*this.height); int y=(int)((this.height-height)/2); yRegions.add(y); yRegions.add(y+height); return this; } public byte[] buildChunk(){ if(xRegions.size()==0){ xRegions.add(0); xRegions.add(width); } if(yRegions.size()==0){ yRegions.add(0); yRegions.add(height); } /* example code from a anwser above // The 9 patch segment is not a solid color. private static final int NO_COLOR = 0x00000001; ByteBuffer buffer = ByteBuffer.allocate(56).order(ByteOrder.nativeOrder()); //was translated buffer.put((byte)0x01); //divx size buffer.put((byte)0x02); //divy size buffer.put((byte)0x02); //color size buffer.put(( byte)0x02); //skip buffer.putInt(0); buffer.putInt(0); //padding buffer.putInt(0); buffer.putInt(0); buffer.putInt(0); buffer.putInt(0); //skip 4 bytes buffer.putInt(0); buffer.putInt(left); buffer.putInt(right); buffer.putInt(top); buffer.putInt(bottom); buffer.putInt(NO_COLOR); buffer.putInt(NO_COLOR); return buffer;*/ int NO_COLOR = 1;//0x00000001; int COLOR_SIZE=9;//could change, may be 2 or 6 or 15 - but has no effect on output int arraySize=1+2+4+1+xRegions.size()+yRegions.size()+COLOR_SIZE; ByteBuffer byteBuffer=ByteBuffer.allocate(arraySize * 4).order(ByteOrder.nativeOrder()); byteBuffer.put((byte) 1);//was translated byteBuffer.put((byte) xRegions.size());//divisions x byteBuffer.put((byte) yRegions.size());//divisions y byteBuffer.put((byte) COLOR_SIZE);//color size //skip byteBuffer.putInt(0); byteBuffer.putInt(0); //padding -- always 0 -- left right top bottom byteBuffer.putInt(0); byteBuffer.putInt(0); byteBuffer.putInt(0); byteBuffer.putInt(0); //skip byteBuffer.putInt(0); for(int rx:xRegions) byteBuffer.putInt(rx); // regions left right left right ... for(int ry:yRegions) byteBuffer.putInt(ry);// regions top bottom top bottom ... for(int i=0;i<COLOR_SIZE;i++) byteBuffer.putInt(NO_COLOR); return byteBuffer.array(); } public NinePatch buildNinePatch(){ byte[] chunk=buildChunk(); if(bitmap!=null) return new NinePatch(bitmap,chunk,null); return null; } public NinePatchDrawable build(){ NinePatch ninePatch=buildNinePatch(); if(ninePatch!=null) return new NinePatchDrawable(resources, ninePatch); return null; } } 

Ahora podemos usar el constructor ninepatch para crear NinePatch o NinePatchDrawable o para crear NinePatch Chunk.

Ejemplo:

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

Simplemente copie el código de clase NinePatchBuilder en un archivo java y utilice los ejemplos para crear NinePatch durante la ejecución de la aplicación, con cualquier resolución.

La clase Bitmap proporciona un método para hacer esto yourbitmap.getNinePatchChunk() . Nunca lo he usado, pero parece que eso es lo que buscas.

  • No se puede cargar Android Device Monitor
  • Hora del reloj Android GMT
  • Cómo obtener el tiempo de computación en NDK
  • Problema extraño con los tiempos de espera de conexión y Google Volley
  • Hora del formato de la clase de tiempo de Android
  • ¿Cómo puedo obtener el tiempo de "red" (desde el ajuste "Automático" llamado "Usar valores proporcionados por la red"), NO el tiempo en el teléfono?
  • ¿Cómo puedo comprobar si el sistema es de 12 o 24 horas?
  • ¿Mostrando la hora actual en Android y actualizarla?
  • ¿Cómo cambiar el nombre de la aplicación de Android y el icono en tiempo de ejecución?
  • Formato de fecha y hora
  • android: mostrar la notificación en un momento específico?
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.