2012-06-16 12 views
9

ho bisogno di aiuto con NinePatchDrawable:NinePatchDrawable non ottiene imbottitura dal pezzo

La mia app è possibile scaricare temi dalla rete. Quasi tutte le cose funzionano bene, tranne i PNG a 9 patch.

final Bitmap bubble = getFromTheme("bubble"); 
if (bubble == null) return null; 

final byte[] chunk = bubble.getNinePatchChunk(); 
if (!NinePatch.isNinePatchChunk(chunk)) return null; 

NinePatchDrawable d = new NinePatchDrawable(getResources(), bubble, chunk, new Rect(), null); 
v.setBackgroundDrawable(d); 

d = null; 
System.gc(); 

getFromTheme() carica Bitmap dalla scheda SD. I PNG a 9 patch sono già compilati, il che significa che includono il blocco richiesto.

Il modo in cui converto la bitmap in un oggetto NinePatchDrawable sembra funzionare, perché l'immagine è estensibile così come l'ho disegnata.

L'unica cosa che non funziona è il padding. Ho già provato a impostare l'imbottitura alla vista in questo modo:

final Rect rect = new Rect(); // or just use the new Rect() set 
d.getPadding(rect);    // in the constructor 
v.setPadding(rect.left, rect.top, rect.right, rect.bottom); 

d.getPadding (rect) dovrebbe riempire il rect variabile con l'imbottitura ottenuto dal pezzo, non dovrebbe? Ma non è così.

Risultato: TextView (v) non mostra il testo nell'area del contenuto dell'immagine a 9 patch. I padding sono impostati su 0 in ciascuna coordinata.

Grazie per la lettura.

risposta

16

Infine, l'ho fatto. Android non stava interpretando correttamente i dati del blocco. Potrebbe esserci un bug Quindi devi deserializzare il blocco per ottenere i dati di riempimento.

Qui andiamo:

package com.dragonwork.example; 

import android.graphics.Rect; 

import java.nio.ByteBuffer; 
import java.nio.ByteOrder; 

class NinePatchChunk { 

    public static final int NO_COLOR = 0x00000001; 
    public static final int TRANSPARENT_COLOR = 0x00000000; 

    public final Rect mPaddings = new Rect(); 

    public int mDivX[]; 
    public int mDivY[]; 
    public int mColor[]; 

    private static void readIntArray(final int[] data, final ByteBuffer buffer) { 
     for (int i = 0, n = data.length; i < n; ++i) 
      data[i] = buffer.getInt(); 
    } 

    private static void checkDivCount(final int length) { 
     if (length == 0 || (length & 0x01) != 0) 
      throw new RuntimeException("invalid nine-patch: " + length); 
    } 

    public static NinePatchChunk deserialize(final byte[] data) { 
     final ByteBuffer byteBuffer = 
      ByteBuffer.wrap(data).order(ByteOrder.nativeOrder()); 

     if (byteBuffer.get() == 0) return null; // is not serialized 

     final NinePatchChunk chunk = new NinePatchChunk(); 
     chunk.mDivX = new int[byteBuffer.get()]; 
     chunk.mDivY = new int[byteBuffer.get()]; 
     chunk.mColor = new int[byteBuffer.get()]; 

     checkDivCount(chunk.mDivX.length); 
     checkDivCount(chunk.mDivY.length); 

     // skip 8 bytes 
     byteBuffer.getInt(); 
     byteBuffer.getInt(); 

     chunk.mPaddings.left = byteBuffer.getInt(); 
     chunk.mPaddings.right = byteBuffer.getInt(); 
     chunk.mPaddings.top = byteBuffer.getInt(); 
     chunk.mPaddings.bottom = byteBuffer.getInt(); 

     // skip 4 bytes 
     byteBuffer.getInt(); 

     readIntArray(chunk.mDivX, byteBuffer); 
     readIntArray(chunk.mDivY, byteBuffer); 
     readIntArray(chunk.mColor, byteBuffer); 

     return chunk; 
    } 
} 

Utilizzare la classe di cui sopra come segue:

final byte[] chunk = bitmap.getNinePatchChunk(); 
if (NinePatch.isNinePatchChunk(chunk)) { 
    textView.setBackgroundDrawable(new NinePatchDrawable(getResources(), 
      bitmap, chunk, NinePatchChunk.deserialize(chunk).mPaddings, null)); 
} 

e funzionerà perfettamente!

+1

Segnala un problema su b.android.com, menziona anche il dispositivo utilizzato e la versione di Android. – Reno

+0

Grazie. L'ho appena fatto. – DragonWork

+0

Wow, grazie mille! Ha funzionato perfettamente – ABentSpoon

0

Non ho mai visto un esempio in cui la Padding non è incluso come parte del 9-patch in questo modo:

enter image description here

Per fare questo si deve prima costruire una NinePatch e quindi creare voi 're Drawable da esso:

NinePatch ninePatch = new NinePatch(bitmap, chunk, srcName); 
NinePatchDrawable d = new NinePatchDrawable(res, ninePatch); 

Tuttavia, ti sembra di essere costruire il vostro Drawable con un rettangolo vuoto:

NinePatchDrawable d = new NinePatchDrawable(getResources(), bubble, chunk, new Rect(), null); 

Se si desidera specificare programatically l'imbottitura provare questo:

Rect paddingRectangle = new Rect(left, top, right, bottom); 
NinePatchDrawable d = new NinePatchDrawable(getResources(), bubble, chunk, paddingRectangle, null); 
+0

Ma la mia domanda è: come per ottenere le variabili a sinistra, in alto, a destra e in basso dall'immagine stessa? – DragonWork

+2

Il primo esempio mostra come compilare correttamente un NinePatchDrawable per includere il riempimento nel blocco. Una volta che questo è stato costruito puoi fare getPadding (rect) – Graeme

+0

Il primo esempio dovrebbe farlo. Costruisci il NinePatch, quindi il NinePatchDrawable da quel NinePatch. 'getPadding()' dovrebbe restituire i valori corretti. – kcoppock

4

In realtà è un po ' più complicato di così, ma ciò che si riduce a è piuttosto semplice:

RECT imbottitura viene restituito da BitmapFactory.decodeStream(InputStream, Rect, Options). Non esiste una versione di decodeByteArray() che può restituire il padding rect.

L'intero nove patch di API è un po 'sciocco:

  • decodeByteArray() chiamate nativeDecodeByteArray(), che è presumibilmente più efficiente di nativeDecodeStream() su un ByteArrayInputStream, ma ovviamente gli sviluppatori non si aspetta che vuole decodificare un nove patch dalla memoria.
  • Il valore di riempimento è solo utilizzato da nove patch, quindi ha più senso che faccia parte di NinePatch anziché BitmapFactory. Purtroppo, NinePatch.java non è molto più di un wrapper che passa il bitmap e il patch a nove pezzi a metodi di disegno (e la maggior parte delle chiamate NinePatch.draw() non sono thread-safe a causa della chiamata a mRect.set(location)).
  • NinePatchDrawable non offre un modo per prendere uno NinePatch e un valore di riempimento, il che rende NinePatch un po 'inutile nel codice dell'applicazione (a meno che non si desideri eseguire il riempimento da soli). Non c'è NinePatchDrawable.getNinePatch() o NinePatch.getBitmap().

This comment riassume abbastanza bene:

ugh. Il contratto decodeStream è che abbiamo già assegnato il pad rect , ma se la bitmap non ha un blocco da nove lotti, , il pad verrà ignorato. Se potessimo cambiarlo in modo pigramente allocare/assegnare il rect, potremmo evitare l'abbandono del GC di creare nuovi Rect s solo per lasciarli cadere sul pavimento.

La mia soluzione è abbastanza semplice:

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

testato, dal momento che è molto semplificato. In particolare, potresti voler controllare che il chunk di nove patch sia valido.

0

Un po 'in ritardo alla festa, ma ecco come ho risolto:

io uso il metodo decoder che NinePatchDrawable prevede, si legge l'imbottitura in modo corretto:

 var myDrawable = NinePatchDrawable.createFromStream(sr, null);