Sur mon application Android, je souhaite que certaines parties des graphiques soient personnalisables, en récupérant de nouvelles couleurs et de nouvelles images côté serveur. Certaines de ces images sont des images de neuf patchs.
Je ne trouve pas de moyen de créer et d’afficher ces images de neuf correctifs (qui ont été récupérées sur le réseau).
Les images de neuf patchs sont récupérées et conservées dans l'application sous forme de bitmaps. Pour créer un NinePatchDrawable , vous avez besoin du NinePatch ou du chunk (byte[]
) correspondant du NinePatch. Le NinePatch NE PEUT PAS être chargé à partir des ressources, car les images n'existent pas dans /res/drawable/
. De plus, pour créer le NinePatch, vous avez besoin du morceau du NinePatch. Donc, tout se passe au morceau.
La question est alors: comment formater/générer le bloc à partir d’un bitmap existant (contenant les informations de NinePatch)?
J'ai effectué des recherches dans le code source Android et sur le Web et je n'arrive pas à trouver d'exemples. Pour aggraver les choses, tout le décodage d’une ressource NinePatch semble se faire de manière native.
Quelqu'un at-il eu des expériences avec ce genre de problème?
Je cible l'API de niveau 4, si cela est important.
getNinePatchChunk
fonctionne très bien. Elle a renvoyé la valeur null car vous donniez à Bitmap
un neufpatch "source". Il faut une image de neuf images "compilées".
Il existe deux types de formats de fichier ninepatch dans le monde Android ("source" et "compilé"). La version source est l'endroit où vous ajoutez la bordure de transparence 1px partout: lorsque vous compilerez votre application dans un fichier .apk plus tard, aapt convertira vos fichiers * .9.png au format binaire attendu par Android. C'est ici que le fichier png obtient ses métadonnées "chunk". ( Lire la suite )
Bon, je suis maintenant aux affaires (vous écoutez DJ kanzure).
Code client, quelque chose comme ceci:
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);
Côté serveur, vous devez préparer vos images. Vous pouvez utiliser le compilateur de ressources binaires Android . Cela automatise une partie de la peine de créer un nouveau projet Android simplement pour compiler des fichiers * .9.png au format natif Android. Si vous deviez le faire manuellement, vous feriez essentiellement un projet et incorporeriez des fichiers * .9.png (fichiers "sources"), tout compiler au format .apk, décompressez le fichier .apk, puis recherchez le fichier *. 9.png, et c’est celui que vous envoyez à vos clients.
Aussi: je ne sais pas si BitmapFactory.decodeStream
connaît l'existence du bloc npTc dans ces fichiers png, il est donc possible que le flux d'images soit traité correctement. L'existence de Bitmap.getNinePatchChunk
suggère que BitmapFactory
pourrait-- vous pourriez aller le chercher dans la base de code en amont.
Dans le cas où il ne connaît pas le bloc npTc et que vos images sont gâchées de manière significative, ma réponse change un peu.
Au lieu d'envoyer les images de neuf correctifs compilés au client, vous écrivez une application Android rapide pour charger les images compilées et cracher le fragment byte[]
. Ensuite, vous transmettez ce tableau d'octets à vos clients avec une image normale - pas de bordures transparentes, pas l'image à neuf sources "source", pas l'image à neuf "séries" compilée. Vous pouvez directement utiliser le morceau pour créer votre objet.
Une autre solution consiste à utiliser la sérialisation d'objet pour envoyer des images à neuf correctifs (NinePatch
) à vos clients, par exemple avec JSON ou le sérialiseur intégré.
Edit Si vous avez vraiment, vraiment besoin de construire votre propre tableau d'octets de morceaux, je commencerais par regarder do_9patch
, isNinePatchChunk
, Res_png_9patch
et Res_png_9patch::serialize()
dans ResourceTypes.cpp. Il y a aussi un lecteur de morceaux npTc fait maison de Dmitry Skiba. Je ne peux pas poster de liens, donc si quelqu'un peut modifier ma réponse, ce serait cool.
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: http://code.google.com/p/Android4me/source/browse/src/Android/graphics/Bitmap.Java
Si vous avez besoin de créer 9Patches à la volée, consultez cette Gist que j’ai faite: https://Gist.github.com/4391807
Vous transmettez une image bitmap, puis lui donnez des encoches similaires à iOS.
Pas besoin d'utiliser Android Binary Resource Compiler pour préparer les pngs 9patch compilés, il est correct d'utiliser aapt dans Android-sdk.aapt.exe c -v -S /path/to/project -C /path/to/destination
Je crée un outil pour créer NinePatchDrawable à partir du bitmap (non compilé) NinePatch . Voir https://Gist.github.com/knight9999/86bec38071a9e0a781ee .
La méthode NinePatchDrawable createNinePatchDrawable(Resources res, Bitmap bitmap)
Vous aide.
Par exemple,
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 );
où
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;
}
Dans cet exemple de cas, le my_nine_patch_image.9.png
se trouve sous le répertoire des actifs.
Donc, vous voulez créer une NinePatchDrawable
à la demande, n'est-ce pas? J'ai essayé le code suivant, peut-être que cela fonctionne pour vous:
InputStream in = getResources().openRawResource(R.raw.test);
Drawable d = NinePatchDrawable.createFromStream(in, null);
System.out.println(d.getMinimumHeight() + ":" + d.getMinimumHeight());
Je pense que cela devrait fonctionner. Il vous suffit de modifier la première ligne pour obtenir InputStream à partir du Web. getNinePatchChunk()
n'est pas destiné à être appelé par les développeurs conformément à la documentation et pourrait ne plus fonctionner correctement.
TRAVAIL ET TESTÉ - CRÉATION NINEPATCH À DURÉE
C’est mon implémentation d’Android Ninepatch Builder, vous pouvez créer NinePatches à l’exécution à travers cette classe et des exemples de code ci-dessous en fournissant des 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.getHeight();
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;
}
}
Nous pouvons maintenant utiliser le générateur ninepatch pour créer NinePatch ou NinePatchDrawable ou pour créer un fragment NinePatch.
Exemple:
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();
Il vous suffit de copier-coller le code de classe NinePatchBuilder dans un fichier Java et d’utiliser les exemples pour créer NinePatch à la volée lors de l’exécution de votre application, quelle que soit la résolution.
La classe Bitmap fournit une méthode pour cela yourbitmap.getNinePatchChunk()
. Je ne l'ai jamais utilisé, mais il semble que ce que vous cherchiez.