Androidアプリケーションで、サーバー側から新しい色と画像を取得することにより、グラフィックのパーツをカスタマイズできるようにする必要があります。これらの画像の一部は9パッチ画像です。
これらの9パッチ画像(ネットワーク経由で取得されたもの)を作成して表示する方法が見つかりません。
9パッチ画像が取得され、ビットマップとしてアプリケーションに保持されます。 NinePatchDrawable を作成するには、対応する NinePatch またはNinePatchのチャンク(byte[]
)が必要です。画像が/res/drawable/
に存在しないため、NinePatchをリソースからロードできません。さらに、NinePatchを作成するには、NinePatchのチャンクが必要です。したがって、すべてチャンクまでドリルダウンします。
問題は、既存のビットマップ(NinePatch情報を含む)からチャンクをどのようにフォーマット/生成するかです。
AndroidソースコードとWebを検索しましたが、これの例を見つけることができません。さらに悪いことに、NinePatchリソースのすべてのデコードはネイティブで行われているようです。
誰かがこの種の問題について何か経験をしたことがありますか?
重要な場合は、APIレベル4を対象としています。
getNinePatchChunk
は問題なく動作します。 Bitmap
に「ソース」ninepatchを与えていたため、nullを返しました。 「コンパイルされた」ninepatchイメージが必要です。
Android= world( "source" and "compiled")には、2つのタイプのninepatchファイル形式があります。ソースバージョンは、1pxの透明な境界線をどこにでも追加する場所です-アプリをコンパイルするとき後で.apkに変換すると、aaptは* .9.pngファイルをAndroidが期待するバイナリ形式に変換します。これは、pngファイルが「チャンク」メタデータを取得する場所です。( もっと読む )
さて、今は仕事に取り掛かっています(DJ kanzureを聞いています)。
次のようなクライアントコード:
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);
サーバー側では、イメージを準備する必要があります。 Android Binary Resource Compiler を使用できます。これにより、新しいAndroidプロジェクトを作成して* .9.pngファイルをAndroidネイティブ形式にコンパイルするだけです。これを手動で行うには、基本的にプロジェクトを作成し、いくつかの* .9.pngファイル(「ソース」ファイル)を投入し、すべてを.apk形式にコンパイルし、.apkファイルを解凍してから、*。9.pngを見つけます。ファイル、それはあなたがあなたのクライアントに送るものです。
また、BitmapFactory.decodeStream
がこれらのpngファイルのnpTcチャンクを認識しているかどうかはわかりません。そのため、画像ストリームが正しく処理されている場合とそうでない場合があります。 Bitmap.getNinePatchChunk
の存在は、BitmapFactory
がそうである可能性があることを示唆しています-上流のコードベースでそれを調べることができます。
NpTcチャンクがわからず、画像が大幅にねじ込まれている場合、私の答えは少し変わります。
コンパイルされたninepatchイメージをクライアントに送信する代わりに、簡単なAndroidアプリをコンパイルしてイメージをロードし、byte[]
チャンクを吐き出します。次に、このバイト配列をクライアントに送信します。通常の画像-透明な境界線なし、「ソース」ninepatchイメージ、「コンパイル済み」ninepatchイメージではありません。チャンクを直接使用してオブジェクトを作成できます。
もう1つの方法は、オブジェクトのシリアル化を使用して、JSONや組み込みのシリアライザーなどを使用して、ninepatchイメージ(NinePatch
)をクライアントに送信することです。
編集本当に、独自のチャンクバイト配列を構築する必要がある場合は、do_9patch
、isNinePatchChunk
、Res_png_9patch
とResourceTypes.cppのRes_png_9patch::serialize()
。 Dmitry Skibaの自家製npTcチャンクリーダーもあります。リンクを投稿することはできないので、誰かが私の回答を編集できるようになっていると便利です。
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
その場で9Patchesを作成する必要がある場合は、私が作成した次のGistを確認してください。 https://Gist.github.com/4391807
任意のビットマップを渡してから、iOSと同様のキャップインセットを与えます。
Android Binary Resource Compilerを使用してコンパイル済み9patch pngを準備する必要はありません。Android-sdkでaaptを使用するだけで十分です。コマンドラインは次のようになります。aapt.exe c -v -S /path/to/project -C /path/to/destination
(コンパイルされていない)NinePatchビットマップからNinePatchDrawableを作成するツールを作成します。 https://Gist.github.com/knight9999/86bec38071a9e0a781ee を参照してください。
メソッドNinePatchDrawable createNinePatchDrawable(Resources res, Bitmap bitmap)
が役立ちます。
例えば、
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 );
どこ
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;
}
このサンプルケースでは、my_nine_patch_image.9.png
はassetsディレクトリの下にあります。
ですから、基本的にNinePatchDrawable
をオンデマンドで作成したいのですね。私は次のコードを試しました、おそらくそれはあなたのために機能します:
_InputStream in = getResources().openRawResource(R.raw.test);
Drawable d = NinePatchDrawable.createFromStream(in, null);
System.out.println(d.getMinimumHeight() + ":" + d.getMinimumHeight());
_
これでうまくいくと思います。 WebからInputStreamを取得するには、最初の行を変更するだけです。 getNinePatchChunk()
は、ドキュメントに従って開発者から呼び出されることを意図しておらず、将来は機能しなくなる可能性があります。
動作とテスト-ランタイムナインパッチの作成
これはAndroid Ninepatch Builderの私の実装です。ビットマップを提供することにより、このクラスと以下のコード例を介してランタイムにNinePatchesを作成できます。
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;
}
}
これで、ninepatchビルダーを使用して、NinePatchまたはNinePatchDrawableを作成したり、NinePatch Chunkを作成したりできます。
例:
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();
NinePatchBuilderクラスコードをJavaファイルにコピーして貼り付け、例を使用して、アプリのランタイム中に任意の解像度でその場でNinePatchを作成します。
Bitmapクラスは、これを行うためのメソッドを提供しますyourbitmap.getNinePatchChunk()
。私はそれを使用したことがありませんが、それはあなたが探しているもののようです。