Создайте NinePatch / NinePatchDrawable во время выполнения

У меня есть требование в моем приложении для Android, что части на графике должны быть настраиваемы, получая новые цвета и изображения со стороны сервера. Некоторые из этих изображений являются изображениями с девятью патчами.

Я не могу найти способ создания и отображения этих изображений с девятью патчами (которые были получены по сети).

Изображения с девятью патчами извлекаются и сохраняются в приложении в виде растровых изображений. Чтобы создать NinePatchDrawable , вам понадобится соответствующий NinePatch или кусок ( byte[] ) для NinePatch. NinePatch НЕ может быть загружен из ресурсов, поскольку изображения не существуют в /res/drawable/ . Кроме того, для создания NinePatch вам нужен fragment NinePatch. Итак, все это сводится к куску.
Вопрос в том, как один форматировать / генерировать fragment из существующего растрового изображения (содержащий информацию NinePatch)?

Я искал в исходном коде Android и в Интернете, и я не могу найти никаких примеров этого. Чтобы все ухудшилось, все декодирование ресурсов NinePatch, похоже, сделано изначально.

Кто-нибудь имел какой-либо опыт в этом вопросе?

Я нацелен на уровень API 4, если это имеет значение.

getNinePatchChunk работает просто отлично. Он возвратил null, потому что вы давали Bitmap «исходный» девятипакет. Ему нужно «скомпилированное» девятикратное изображение.

В мире Android существует два типа форматов файлов с девятью файлами («источник» и «скомпилированный»). Исходная версия – это то, где вы добавляете границу прозрачности 1px везде – когда вы скомпилируете свое приложение в .apk позже, aapt преобразует ваши файлы * .9.png в бинарный формат, который ожидает Android. Здесь png-файл получает свои метаданные «chunk». ( подробнее )

Хорошо, теперь дело до бизнеса (вы слушаете DJ kanzure).

  1. Код клиента, что-то вроде этого:

     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); 
  2. На стороне сервера вам нужно подготовить свои изображения. Вы можете использовать Компилятор двоичных ресурсов Android . Это автоматизирует некоторую боль от создания нового Android-проекта только для компиляции некоторых файлов * .9.png в собственный формат Android. Если бы вы сделали это вручную, вы бы сделали проект и забросили некоторые файлы * .9.png («исходные» файлы), скомпилировали все в формат .apk, распакуйте файл .apk, а затем найдите *. 9.png, и это тот, который вы отправляете своим клиентам.

Кроме того: я не знаю, знает ли BitmapFactory.decodeStream об узле npTc в этих png-файлах, поэтому он может или не может правильно обрабатывать stream изображения. Существование Bitmap.getNinePatchChunk предполагает, что BitmapFactory может … вы можете посмотреть его в восходящей кодовой базе.

В случае, если он не знает о блоке npTc, и ваши изображения значительно прикручиваются, мой ответ немного меняется.

Вместо того, чтобы отправлять скомпилированные снимки девяти изображений клиенту, вы пишете быстрое приложение для Android, чтобы загружать скомпилированные изображения и выплевывать byte[] fragment. Затем вы передаете этот массив байтов своим клиентам вместе с обычным изображением – нет прозрачных границ, а не «исходное» девятикратное изображение, а не «скомпилированное» девятикратное изображение. Вы можете напрямую использовать кусок для создания своего объекта.

Другой альтернативой является использование сериализации объектов для отправки девятикратных изображений ( NinePatch ) вашим клиентам, например, с помощью JSON или встроенного сериализатора.

Редактировать Если вам действительно нужно создать собственный массив байтовых блоков, я бы начал с просмотра do_9patch , isNinePatchChunk , Res_png_9patch и Res_png_9patch::serialize() в ResourceTypes.cpp. Там также есть самодельный читатель npTc chunk от Дмитрия Скиба. Я не могу размещать ссылки, поэтому, если кто-то может отредактировать мой ответ, это будет круто.

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

Материал Дмитрия Скибы: http://code.google.com/p/android4me/source/browse/src/android/graphics/Bitmap.java

Если вам нужно создать 9Patches на лету, проверьте этот смысл, который я сделал: https://gist.github.com/4391807

Вы передаете ему любое bitmap, а затем даете ему вставки на крышке, похожие на iOS.

Не нужно использовать Android Binary Resource Compiler для подготовки скомпилированных 9patch png, просто использование aapt в android-sdk в порядке, командная строка выглядит так:
aapt.exe c -v -S /path/to/project -C /path/to/destination

Я создаю инструмент для создания NinePatchDrawable из (uncompiled) растрового изображения NinePatch. См. 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; } 

В этом примере case my_nine_patch_image.9.png находится в каталоге ресурсов.

Поэтому вы в основном хотите создать NinePatchDrawable по запросу, не так ли? Я попробовал следующий код, возможно, он работает для вас:

 InputStream in = getResources().openRawResource(R.raw.test); Drawable d = NinePatchDrawable.createFromStream(in, null); System.out.println(d.getMinimumHeight() + ":" + d.getMinimumHeight()); 

Я думаю, что это должно сработать. Вам просто нужно изменить первую строку, чтобы получить InputStream из Интернета. getNinePatchChunk() не предназначен для getNinePatchChunk() разработчиками в соответствии с документацией и может быть getNinePatchChunk() в будущем.

РАБОТА И ИСПЫТАНИЕ – СОЗДАНИЕ НИНЕПТИЧЕСКОГО ПРОТИВОДЕЙСТВИЯ

Это моя реализация андроида Ninepatch Builder, вы можете создавать NinePatches в Runtime через этот class и примеры кода ниже, поставляя любой битмап

 public class NinePatchBuilder { int width,height; Bitmap bitmap; Resources resources; private ArrayList xRegions=new ArrayList(); private ArrayList yRegions=new ArrayList(); 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 

Теперь мы можем использовать конструктор 9patch для создания 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(); 

Просто скопируйте код classа NinePatchBuilder в файл java и используйте примеры для создания NinePatch «на лету» во время работы приложения с любым разрешением.

Класс Bitmap предоставляет метод для этого: yourbitmap.getNinePatchChunk() . Я никогда не использовал его, но, похоже, это то, что вы ищете.

  • Предельная точка?
  • Android: Как проверить, доступен ли сервер?
  • Как я могу прервать метод acceptSetServerSocket?
  • Давайте будем гением компьютера.