Подтвердить что ты не робот

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

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

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

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

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

Есть ли у кого-нибудь опыт такого рода проблем?

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

4b9b3361

Ответ 1

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

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

Хорошо, теперь дело до бизнеса (вы слушаете 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 о блоке npTc в этих файлах png, поэтому он может или не может правильно обрабатывать поток изображения. Существование Bitmap.getNinePatchChunk предполагает, что BitmapFactory может... вы могли бы посмотреть его в восходящей кодовой базе.

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

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

Другой альтернативой является использование сериализации объектов для отправки девяти снимков (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

Ответ 2

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

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

Ответ 3

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

Ответ 4

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

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

Ответ 5

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

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

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

Ответ 6

РАБОТА И ИСПЫТАНИЕ - СОГЛАШЕНИЕ НИНЕПТИЧЕСКОГО ПРОТИВНИКА

Это моя реализация Android Ninepatch Builder, вы можете создавать NinePatches в Runtime через этот класс и примеры кода ниже, поставляя любой 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;
    }
}

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

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

Ответ 7

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