Android: компиляция файлов с 9 исправлениями для использования вне папки с возможностью рисования?

Мне нужно загрузить файлы с 9 исправлениями из-за пределов папки с возможностью рисования. Это для того, чтобы мое приложение могло скачивать новые скины с сервера, например. Я обнаружил, что изображение с 9 патчами, хранящееся в папке drawable, компилируется при создании .apk. Точно такой же файл, прочитанный из папки с ресурсами, не имеет фрагмента из 9 патчей. Таким образом, процесс создания .apk заключается в компиляции исходных файлов с 9 патчами в папке drawable, а не в каталоге assets.

Как мне самому скомпилировать файл с 9 патчами, чтобы установить его в папку с ресурсами? Существует ли (пакетный) инструмент для преобразования исходного кода в скомпилированную версию с фрагментом из 9 исправлений? Мне бы очень, очень хотелось не использовать Eclipse/Ant для создания .apk, а затем разбирать его, чтобы извлечь скомпилированный файл с 9 патчами, если это вообще возможно.

На данный момент я просто хочу иметь возможность читать из каталога ресурсов (например, иметь подкаталог для каждого скина), чтобы упростить его. Следующим шагом является компиляция исходного изображения для добавления в фрагмент из 9 патчей. После этого я буду беспокоиться о загрузке на лету в папку /data - если я не могу скомпилировать файл с 9 патчами, то нет смысла добавлять усилия на стороне сервера.


person Colin    schedule 28.06.2011    source источник


Ответы (2)


Нет простого способа сделать это afaik. Компиляция 9-патчей выполняется aapt и довольно проста: она отбрасывает черные рамки и кодирует их содержимое внутри фрагмента PNG. Для вас было бы довольно тривиально написать инструмент, который делает что-то подобное. Обратите внимание, что вам даже не нужно использовать один и тот же формат. Если вы посмотрите на различные API-интерфейсы NinePatch в документе, вы увидите, что вы можете отправить свой собственный «чанк» (который кодирует области растяжения и отступы). Структура массива байтов [] фрагмента объясняется здесь:

/**
 * This chunk specifies how to split an image into segments for
 * scaling.
 *
 * There are J horizontal and K vertical segments.  These segments divide
 * the image into J*K regions as follows (where J=4 and K=3):
 *
 *      F0   S0    F1     S1
 *   +-----+----+------+-------+
 * S2|  0  |  1 |  2   |   3   |
 *   +-----+----+------+-------+
 *   |     |    |      |       |
 *   |     |    |      |       |
 * F2|  4  |  5 |  6   |   7   |
 *   |     |    |      |       |
 *   |     |    |      |       |
 *   +-----+----+------+-------+
 * S3|  8  |  9 |  10  |   11  |
 *   +-----+----+------+-------+
 *
 * Each horizontal and vertical segment is considered to by either
 * stretchable (marked by the Sx labels) or fixed (marked by the Fy
 * labels), in the horizontal or vertical axis, respectively. In the
 * above example, the first is horizontal segment (F0) is fixed, the
 * next is stretchable and then they continue to alternate. Note that
 * the segment list for each axis can begin or end with a stretchable
 * or fixed segment.
 *
 * The relative sizes of the stretchy segments indicates the relative
 * amount of stretchiness of the regions bordered by the segments.  For
 * example, regions 3, 7 and 11 above will take up more horizontal space
 * than regions 1, 5 and 9 since the horizontal segment associated with
 * the first set of regions is larger than the other set of regions.  The
 * ratios of the amount of horizontal (or vertical) space taken by any
 * two stretchable slices is exactly the ratio of their corresponding
 * segment lengths.
 *
 * xDivs and yDivs point to arrays of horizontal and vertical pixel
 * indices.  The first pair of Divs (in either array) indicate the
 * starting and ending points of the first stretchable segment in that
 * axis. The next pair specifies the next stretchable segment, etc. So
 * in the above example xDiv[0] and xDiv[1] specify the horizontal
 * coordinates for the regions labeled 1, 5 and 9.  xDiv[2] and
 * xDiv[3] specify the coordinates for regions 3, 7 and 11. Note that
 * the leftmost slices always start at x=0 and the rightmost slices
 * always end at the end of the image. So, for example, the regions 0,
 * 4 and 8 (which are fixed along the X axis) start at x value 0 and
 * go to xDiv[0] and slices 2, 6 and 10 start at xDiv[1] and end at
 * xDiv[2].
 *
 * The array pointed to by the colors field lists contains hints for
 * each of the regions.  They are ordered according left-to-right and
 * top-to-bottom as indicated above. For each segment that is a solid
 * color the array entry will contain that color value; otherwise it
 * will contain NO_COLOR.  Segments that are completely transparent
 * will always have the value TRANSPARENT_COLOR.
 *
 * The PNG chunk type is "npTc".
 */
struct Res_png_9patch
{
    Res_png_9patch() : wasDeserialized(false), xDivs(NULL),
                       yDivs(NULL), colors(NULL) { }

    int8_t wasDeserialized;
    int8_t numXDivs;
    int8_t numYDivs;
    int8_t numColors;

    // These tell where the next section of a patch starts.
    // For example, the first patch includes the pixels from
    // 0 to xDivs[0]-1 and the second patch includes the pixels
    // from xDivs[0] to xDivs[1]-1.
    // Note: allocation/free of these pointers is left to the caller.
    int32_t* xDivs;
    int32_t* yDivs;

    int32_t paddingLeft, paddingRight;
    int32_t paddingTop, paddingBottom;

    enum {
        // The 9 patch segment is not a solid color.
        NO_COLOR = 0x00000001,

        // The 9 patch segment is completely transparent.
        TRANSPARENT_COLOR = 0x00000000
    };
    // Note: allocation/free of this pointer is left to the caller.
    uint32_t* colors;

    // Convert data from device representation to PNG file representation.
    void deviceToFile();
    // Convert data from PNG file representation to device representation.
    void fileToDevice();
    // Serialize/Marshall the patch data into a newly malloc-ed block
    void* serialize();
    // Serialize/Marshall the patch data
    void serialize(void* outData);
    // Deserialize/Unmarshall the patch data
    static Res_png_9patch* deserialize(const void* data);
    // Compute the size of the serialized data structure
    size_t serializedSize();
};
person Romain Guy    schedule 28.06.2011
comment
Отличная информация, спасибо. Где вы взяли этот файл? Мне явно не хватает жизненно важного источника информации. Не знаю, насколько тривиально будет написать компилятор с девятью патчами (никогда не любил компьютерную графику), но я опубликую что-нибудь полуприличное. Надеюсь, кто-то другой опередил меня в этом ударе. - person Colin; 29.06.2011
comment
Это на android.git.kernel.org - person Romain Guy; 29.06.2011
comment
Выспавшись на этом, я теперь склоняюсь к написанию Java-метода для загрузки исходного файла с девятью патчами, обрезки границы в 1 пиксель для изображения и создания фрагмента (как описано выше) из границы в 1 пиксель. Это вместо компилятора. Пусть этот метод читает уже открытый поток, чтобы файл мог поступать из любого каталога. Я могу сделать это, так как склонен к кэшированию и повторному использованию растровых изображений, поэтому я бы использовал преобразование только один раз для каждого используемого файла изображения с девятью патчами. - person Colin; 29.06.2011
comment
Было бы неплохо, если бы инструмент draw9patch мог сохранить скомпилированную версию. Может сэкономить так много времени сообществу разработчиков, чтобы заново не изобретать велосипед. - person Amir Uval; 24.09.2013
comment
draw9patch не компилирует файлы 9patch. Он не знает об окончательном двоичном формате, aapt знает. - person Romain Guy; 25.09.2013

Вот обходной путь, который работает в моем случае.

В моем приложении есть изображения с 9 патчами по умолчанию, которые я использую в качестве всплывающих окон сообщений. Я хотел создать загружаемый themes, который меняет пузыри и другие вещи, такие как цвета шрифта и т. д.

Изображения *.9.png, включенные в папки с возможностью рисования, используются по умолчанию, они включают черные пиксели вокруг изображения:

Исходящее изображение по умолчанию default_outgoing.9.png

«Пользовательские» / тематические пузыри имеют точно такие же размеры, размещение с небольшими изменениями, которые по-прежнему используют ту же область «куска», что и по умолчанию, но они не включают черные пиксели или .9 в имени файла:

Розовое круглое исходящее изображение pink_round_outgoing.png

Так как же пользовательский работает и при этом хорошо выглядит? Вот фрагмент кода, который извлекает пользовательское изображение, получает некоторые значения из изображения с 9 патчами по умолчанию и применяет их к пользовательскому растровому изображению:

//Get the custom replacement image
Bitmap bitmap = BitmapFactory.decodeFile(folderpath + File.separator + "pink_round_outgoing.png");

//Get padding from the default 9-patch drawable
Drawable existingDrawable = ContextCompat.getDrawable(this, R.drawable.default_outgoing);
Rect padding = new Rect();
if (existingDrawable != null) {
    existingDrawable.getPadding(padding);
}

//Get 9-patch chunk from the default 9-patch drawable
Bitmap existingBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.default_outgoing);
byte[] chunk = existingBitmap.getNinePatchChunk();

//Finally create your custom 9-Patch drawable and set it to background
NinePatchDrawable d = new NinePatchDrawable(getResources(), bitmap, chunk, padding, null);
view.setBackground(d);
person Pierre    schedule 24.10.2019