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).
- Androide; Msgstr "Se ha agotado el tiempo de envío de claves ..."
- Android Convertir hora central a hora local
- Android estableció la última hora modificada para el archivo
- Notificación de Android Push (lento)
- SoundPool de Android tartamudea, retrasa, a veces no juega en absoluto
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.
- ¿Cómo obtener la hora actual de Google para Android?
- ¿Hay una manera de cargar arbitrariamente recursos en tiempo de ejecución en Android?
- Dibuja línea personalizada entre dos elementos en TableLayout Android
- Android 4.0 org.apache.http.conn.ConnectTimeoutException: Conectarse a ... agotado el tiempo de espera
- ¿Es posible ver variables en tiempo de ejecución?
- ¿Cómo se puede comprobar el permiso en tiempo de ejecución sin lanzar SecurityException?
- Android startService () tarda mucho tiempo en volver al subproceso de interfaz de usuario
- ¿Cómo se muestra correctamente la posición / duración de un MediaPlayer?
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).
-
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);
-
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.
- Cómo actualizar / actualizar un elemento específico en RecyclerView
- Después de la actualización de Android Studio: Gradle DSL método no encontrado: 'runProguard ()'