2012-06-16 4 views
9

ich mit NinePatchDrawable Hilfe benötigen:NinePatchDrawable wird nicht Polsterung aus chunk

Meine App Themen aus dem Netz herunterladen. Fast alle Dinge funktionieren gut, außer 9-Patch PNGs.

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() lädt die Bitmap von der SD-Karte. Die 9-Patch-PNGs sind bereits kompiliert, dh sie enthalten den benötigten Chunk.

Die Art und Weise, wie ich die Bitmap in ein NinePatchDrawable-Objekt konvertiere, scheint zu funktionieren, weil das Bild sowohl dehnbar als auch gezeichnet ist.

Das einzige, was nicht funktioniert, ist die Polsterung. Ich habe bereits versucht, die Polsterung der Ansicht wie folgt festgelegt:

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) sollte die Variable rect mit dem Polster aus dem Brocken bekam füllen, sollte es nicht? Aber das tut es nicht.

Ergebnis: TextView (v) zeigt den Text im Inhaltsbereich des 9-Patch-Bildes nicht an. Die Paddings werden in jeder Koordinate auf 0 gesetzt.

Danke fürs Lesen.

Antwort

16

Schließlich habe ich es tat. Android hat die Chunkdaten nicht richtig interpretiert. Es könnte einen Fehler geben. Sie müssen also den Chunk selbst deserialisieren, um die Padding-Daten zu erhalten.

Here we go:

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

Verwenden Sie die Klasse oben, wie folgend:

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

Und es wird perfekt funktionieren!

+1

Stellen Sie ein Problem auf b.android.com, erwähnen Sie auch das verwendete Gerät und die Android-Version. – Reno

+0

Danke. Ich habe es gerade getan. – DragonWork

+0

Wow, vielen Dank! Arbeitete perfekt. – ABentSpoon

0

ich nie ein Beispiel gesehen haben, wo die Padding nicht als Teil der 9-Patch wie so enthalten ist:

enter image description here

Dazu bauen Sie sollten zuerst eine NinePatch und Sie dann erstellen ist Drawable davon:

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

Allerdings scheinen Sie Ihren Drawable mit einem leeren Rechteck zu konstruieren:

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

Wenn Sie wollen, dass die Polsterung programmatisch spezifizieren diese versuchen:

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

Aber meine Frage ist: Wie um die Variablen links, oben, rechts und unten aus dem Bild selbst zu bekommen? – DragonWork

+2

Das erste Beispiel zeigt Ihnen, wie Sie NinePatchDrawable korrekt auffüllen können, um das Padding in den Chunk aufzunehmen. Sobald dies erstellt wurde, können Sie getPadding (rect) – Graeme

+0

Das erste Beispiel sollte es tun. Konstruiere das NinePatch, dann das NinePatchDrawable aus diesem NinePatch. 'getPadding()' sollte dann die korrekten Werte zurückgeben. – kcoppock

4

Es ist eigentlich leicht komplizierter als das, aber es, was läuft darauf hinaus, ist ziemlich einfach:

Die Polsterung rect durch BitmapFactory.decodeStream(InputStream, Rect, Options) zurückgegeben. Es gibt keine Version von decodeByteArray(), die das padding rect zurückgeben kann.

Der ganze neun Patch-API ist ein bisschen dumm:

  • decodeByteArray() Anrufe nativeDecodeByteArray(), die als nativeDecodeStream() auf einem ByteArrayInputStream vermutlich effizienter ist, aber offensichtlich die Devs Sie nie einen neun- entschlüsseln zu wollen erwartet Patch aus dem Speicher.
  • Das Padding Rect ist nur von neun Patches verwendet, so macht es mehr Sinn für es Teil von NinePatch statt BitmapFactory sein. Leider ist NinePatch.java nicht viel mehr als ein Wrapper, der die Bitmap- und Neunpatch-Chunks an Zeichenmethoden übergibt (und die meisten der NinePatch.draw()-Aufrufe sind aufgrund des Aufrufs von mRect.set(location) nicht Thread-sicher).
  • NinePatchDrawable bietet keine Möglichkeit, eine NinePatch und ein Padding-Rect zu nehmen, was NinePatch in Anwendungscode etwas nutzlos macht (es sei denn, Sie möchten das Padding selbst tun). Es gibt keine NinePatchDrawable.getNinePatch() oder NinePatch.getBitmap().

This comment fasst es ziemlich gut:

igitt. Der decodeStream Vertrag ist, dass wir das Pad-Rect bereits zugewiesen haben, aber wenn das Bitmap keinen 9patch Chunk hat, dann wird das Pad ignoriert. Wenn wir dies zu träge ändern könnten alloc/zuweisen die rect, könnten wir vermeiden, die GC Abwanderung von neuen Rect s nur auf den Boden fallen lassen.

Meine Lösung ist recht einfach:

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

Ungeprüfte, da es stark vereinfacht wird. Insbesondere sollten Sie überprüfen, ob der Neun-Patch-Chunk gültig ist.

0

Ein bisschen spät, um die Partei, aber hier ist, wie ich es gelöst:

ich den Decoder Methode verwenden, die NinePatchDrawable liefert, liest die Polsterung richtig:

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