Explanation of Android point nine diagram mechanism and its application in chat bubble. What should Android development learn

Execute successful instance

jundeMacBook-Pro:Phase I bubble junxu$ ./aapt c -S /Users/junxu/Desktop/Phase I bubble/Bubble demand finishing -C /Users/junxu/Desktop/Phase I bubble/output 
Crunching PNG Files in source dir: /Users/junxu/Desktop/Phase I bubble/Bubble demand finishing
To destination dir: /Users/junxu/Desktop/Phase I bubble/output 

be careful:

If it is not a standard point nine diagram, an error will be reported in the conversion process. At this time, please design and provide a new point nine diagram

Problems encountered in actual development

Small screen mobile phone adaptation problem

At the beginning, our cut image was cut according to the 2x image, so the bubble height of the mobile phone would be too large on the small screen mobile phone.

Cause analysis:

The essence of this phenomenon is that the height of point nine picture is greater than that of single line text message.

Solution 1 (not desirable for the time being):

  1. I tried to compress the point nine diagram, but in the end, the display on some mobile phones was disordered. I don't know if the method of compressing the point nine diagram was wrong.

Solution II

For low resolution mobile phones and high-resolution mobile phones that send different picture URLs respectively, we try to use the double picture when density < 2 and the double picture when density > = 2.

Solution III

Some people may have such questions. Why use the solution of double graph and double graph? Directly ask the UI designer to give a set of pictures. If the height of the picture is moderate, it will be solved. Yes, we think so too, but they say that for the dot nine pictures with some decorations, if the height is reduced, they are not easy to cut some decorative patterns. For example, the stars in the picture below.

Summary

In the final analysis, scheme 2 and scheme 3 are actually a compromise. If you can directly scale the point nine diagram, it will be a perfect solution. The nine point diagram of drawable or mipmap in the res directory of Android can really do it. After looking at the relevant code, I haven't found any good solution. If you have a good solution, please leave a message.

The padding in Figure 9 is invalid on some mobile phones

This is a bug in some Android phones. The solution is shown in: stackoverflow.com/questions/1...

public class NinePatchChunk {

    private static final String TAG = "NinePatchChunk";

    public final Rect mPaddings = new Rect();

    public int mDivX[];
    public int mDivY[];
    public int mColor[];

    private static float density = IMO.getInstance().getResources().getDisplayMetrics().density;

    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 IllegalStateException("invalid nine-patch: " + length);
    }

    public static Rect getPaddingRect(final byte[] data) {
        NinePatchChunk deserialize = deserialize(data);
        if (deserialize == null) {
            return new Rect();
        }
    }

    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()];

        try {
            checkDivCount(chunk.mDivX.length);
            checkDivCount(chunk.mDivY.length);
        } catch (Exception e) {
            return null;
        }


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

NinePatchDrawable patchy = new NinePatchDrawable(view.getResources(), bitmap, chunk, NinePatchChunk.getPaddingRect(chunk), null);
view.setBackground(patchy); 

Dynamic downloading of point nine diagram will cause the chat bubble to flash

  1. The scheme we adopt here is pre download (pre download 10)
  2. The chat bubble adopts memory cache and disk cache to ensure that the RecyclerView will not flash when sliding quickly

Understanding point nine diagram

The following contents refer to the of Tencent music Introduction to Android dynamic layout and NinePatchChunk decryption

Review the construction method of NinePatchDrawable, and the third parameter bitmap Getninepatchchunk(), the author guesses that the aapt command actually adds NinePatchChunk information to the bitmap image, so as long as we can construct this thing ourselves, can we pull up any image in the way we want?

However, after checking a bunch of official documents, it seems that there is no corresponding method to obtain the chunk parameter of this byte [] type.

Since we can't know how to generate this chunk, can we reverse the generation method of this NinePatchChunk from the perspective of parsing?

Now we need to start with the source code.

NinePatchChunk.java

public static NinePatchChunk deserialize(byte[] data) {
    ByteBuffer byteBuffer =
            ByteBuffer.wrap(data).order(ByteOrder.nativeOrder());
    byte wasSerialized = byteBuffer.get();
    if (wasSerialized == 0) return null;
    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;
} 

In fact, by analyzing the source code of byte[] chunk from this part, we can deduce the general structure. As shown below,

According to the conjecture in the figure above and. 9 Png's understanding and intuition feel that the three arrays of mdivx, mdivy and mcolor are the most critical, but we need to continue to look at the source code.

ResourceTypes.h

/**
 * 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.
 * / 

As noted in the source code, the NinePatch Chunk divides the picture into several regions from the x-axis and y-axis. The F region represents fixation and the S region represents stretching. Mdivx and mdivy describe the starting position of all s regions, while mColor describes the color of each Segment. Generally, it is assigned as no defined in the source code_ Color = 0x00000001. Take the example in the source code comments. mDivX,mDivY, and mColor are as follows:

mDivX = [ S0.start, S0.end, S1.start, S1.end];
mDivY = [ S2.start, S2.end, S3.start, S3.end];
mColor = [c[0],c[1],...,c[11]] 

For the mColor array, the length is equal to the number of areas divided. It is used to describe the color of each area. If we only describe the stretching method of a bitmap, we don't need color, that is, no in the source code_ COLOR = 0x00000001

Having said that, let's illustrate how to construct a NinePatchDrawable stretched by the center point through a simple example,

Bitmap bitmap = BitmapFactory.decodeFile(filepath);
int[] xRegions = new int[]{bitmap.getWidth() / 2, bitmap.getWidth() / 2 + 1};
int[] yRegions = new int[]{bitmap.getWidth() / 2, bitmap.getWidth() / 2 + 1};
int NO_COLOR = 0x00000001;
int colorSize = 9;
int bufferSize = xRegions.length * 4 + yRegions.length * 4 + colorSize * 4 + 32;

ByteBuffer byteBuffer = ByteBuffer.allocate(bufferSize).order(ByteOrder.nativeOrder());
// The first byte is not equal to 0
byteBuffer.put((byte) 1);

//mDivX length
byteBuffer.put((byte) 2);
//mDivY length
byteBuffer.put((byte) 2);
//mColors length
byteBuffer.put((byte) colorSize);

//skip
byteBuffer.putInt(0);
byteBuffer.putInt(0);

//Setting padding to 0
byteBuffer.putInt(0);
byteBuffer.putInt(0);
byteBuffer.putInt(0);
byteBuffer.putInt(0);

//skip
byteBuffer.putInt(0);

// mDivX
byteBuffer.putInt(xRegions[0]);
byteBuffer.putInt(xRegions[1]);

// mDivY
byteBuffer.putInt(yRegions[0]);
byteBuffer.putInt(yRegions[1]);

// mColors
for (int i = 0; i < colorSize; i++) {
    byteBuffer.putInt(NO_COLOR);
}

return byteBuffer.array(); 

create-a-ninepatch-ninepatchdrawable-in-runtime

You can also find awesome classes on stackoverflow. You can dynamically create a point nine picture, stretch the picture and slap the face. At the beginning, it was said that android can't dynamically specify the picture stretching area like ios.

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) {
= (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) {
 

Keywords: Android Design Pattern

Added by vikaspa on Wed, 15 Dec 2021 00:24:51 +0200