Thinking triggered by a startup diagram--Explore.9 Diagram principles and application scenarios

Introduction

Little u, there is a white screen when our app starts, which is unsightly, you can upload the entire startup image, here is the resource image

changed

Well, not bad, hey, why does this Xiaomi fold display two logos?

ah? This. . . I'll take a look

This is the case, the activity startup image and the startup background icon are displayed together, but the startup image does not fit the narrow and long screen of fold, and the lower part is transparent because there is no background, so two icons are displayed, one Startup icon, a startup picture icon, please see the schematic diagram

schematic diagram.png

How to solve it?

There are several ways, 1. Let the startup image stretch to cover part of the area, but it will be deformed, which is not elegant, 2. Set a white background for the imageview to make it opaque, 3. Use android studio. 9 picture making tools, let the blank part stretch, the content does not stretch, very elegant.

picture.png

Ok, then you try the .9 picture first

.9 Introduction

I know that the friends who read this article must know more or less about the .9 picture, just give a brief introduction. The 9th picture can be set to stretch one part of the picture and other parts not stretch, and you can also control the content displayed in the picture This is the area of ​​​​the .9 picture. The introduction is simple enough. If you want to see a more detailed description, you can move to Google's official description. Generally speaking, this is what it means.

This is the official documentation developer.android.com/studio/writ…

picture.png

picture.png

picture.png

This is an example of the .9 figure in three stretched situations, portrait, landscape, and both.

.9 Figure making

Many articles on the Internet say that the easiest way to do it is to use the as built-in tool, but many of the articles on the Internet are very early and are old version tools. Let's take a look at what this tool is now.

picture.pngIt doesn't seem to be very simple. What are these things used for? It's really confusing to see for the first time. Here's an introduction to this tool.

  • Zoom : Adjusts the zoom level of the graph in the drawing area.

  • Patch scale : Adjust the scale of the image in the preview area.

  • Show lock : Renders visually when the mouse hovers over a non-drawable area of ​​the graph.

  • Show patches:预览绘制区域中的可拉伸图块(粉色为可拉伸图块),如上面的图 2 所示。

  • Show content:突出显示预览图像中的内容区域(紫色为允许绘制内容的区域),如图 2 所示。

  • Show bad patches:在拉伸时可能会在图形中产生伪影的图块区域周围添加红色边框,如图 2 所示。如果您消除所有不良图块,已拉伸图像的视觉连贯性将得以保持。 怎么生成.9图不是重点,网上文章不要太多,最主要就是绘制左边和上面的黑线,左边黑线控制的是哪里可以被上下拉伸,上面黑线控制哪里可以被水平拉伸,可以有多条,右边和下面的黑线控制了哪里可以放内容,不在这个区域的内容不会被显示。

当然 还有很多办法生成.9图 单独工具或者在线工具都行 看个人喜好了。

.9图原理

上面的介绍都很大众化 那么为啥.9图这么神奇呢?它是什么原理呢,这个好像没什么人说过,这里也简单阐述下。

主要是四条黑线 分为两组 左边和上面的黑线 负责判定图片哪个部分可以被拉伸 右边和下面的黑线 负责确定图片内部展示内容的区域 比如这个图是个聊天气泡 内容是一堆文字

大概就是下面这个图的样子 分成了九个区域

picture.png

我们把他们编号成1-9,这几个区域对应情况如下

  1. 1 3 7 9 号区域,不在两条黑线区域内,不会被拉伸
  2. 4 6 号区域,只在左侧黑线范围内,可以被上下拉伸
  3. 2 8 号区域,只在上面黑线区域内,会被左右拉伸
  4. 5号区域,同时在上和左区域内,会被上下左右拉伸

回到刚才的问题 为啥处理之后就能控制拉伸和内容了?

首先我们发现处理之后的图片后缀还是原来的 证明没有变成其他格式 但是名字里加上了.9

那猜测是不是在文件里加上了一些额外信息 用.9作为标识 图片系统处理拉伸的时候就去读这些信息

那么 到底加了什么信息呢 又是怎么判断和使用的呢 下面一一道来

首先 我们给图片加了什么信息

我们看看官方怎么描述.9图的

NinePatchDrawable 图形是一种可拉伸的位图,可用作视图的背景。Android 会自动调整图形的大小以适应视图的内容。NinePatch 图形是标准 PNG 图片,包含一个额外的 1 像素边框。必须使用 9.png 扩展名将其保存在项目的 res/drawable/ 目录下。

可以看到,.9图本质上还是png图片,但是加了1像素边框,且名字里加了个.9。

让我们来看看什么是png图片,以及它的数据构成

The PNG format provides a portable, legally unencumbered, well-compressed, well-specified standard for lossless bitmapped image files.

A PNG file consists of a PNG signature followed by a series of chunks. This chapter defines the signature and the basic properties of chunks.

png的签名块后面跟了两个数据块critical chunk和ancillary chunks,其中critical chunk包含关键数据,也是每个图片必须有的,ancillary chunks包含一些辅助信息,png如果不识别这些辅助块,可以忽略它打到向下兼容的目的。

签名块就是一个八个字节的十六进制码,用来标识图片。

重点来看看数据块的布局

名称 字节数 说明
长度(Length) 4字节 指定数据块中数据区域的长度,长度不可超过(231-1)个字节
数据块类型码(Chunk Type Code) 4字节 数据块类型码由ASCII字母(A-Z和a-z)组成的"数据块符号"
数据块数据(Chunk Data) 可变长度 存储数据块类型码指定的数据
循环冗余检测(CRC) 4字节 存储用来检测是否文件传输有误的循环冗余码

看到这个可以猜测,我们添加的黑色边框就是往辅助块里面加了内容,在展示的时候识别添加的信息,达到控制哪些地方伸缩的目的。

让我们看看一张图片被弄成.9之后加了些什么内容。

原图数据如下

original image.png

.9数据如下

.9 figure.png

可以看到,变成.9图片之后,多了5个IDAT块,而且参数里面的长宽都增加了2像素,而且图片大小也增加了不少从80kb增加到了180kb,我们可以猜测到,这几个块里面记录的数据就是我们生成.9图时画的那几条线生成的了。

其次 这些信息在图片发生拉伸时怎么被识别和使用的

这就涉及到android怎么加载一张图片的问题了,当然这些操作都是native层进行的,经过代码跟踪,我们发现有这样一个类 ** NinePatchPeeker.cpp**

我们来看看这个类里面的readChunk方法干了些什么

bool NinePatchPeeker::readChunk(const char tag[], const void* data, size_t length) {

    if (!strcmp("npTc", tag) && length >= sizeof(Res_png_9patch)) {

        Res_png_9patch* patch = (Res_png_9patch*) data;

        size_t patchSize = patch->serializedSize();

        if (length != patchSize) {

            return false;

        }

        // You have to copy the data because it is owned by the png reader

        Res_png_9patch* patchNew = (Res_png_9patch*) malloc(patchSize);

        memcpy(patchNew, patch, patchSize);

        Res_png_9patch::deserialize(patchNew);

        patchNew->fileToDevice();

        free(mPatch);

        mPatch = patchNew;

        mPatchSize = patchSize;

    } else if (!strcmp("npLb", tag) && length == sizeof(int32_t) * 4) {

        mHasInsets = true;

        memcpy(&mOpticalInsets, data, sizeof(int32_t) * 4);

    } else if (!strcmp("npOl", tag) && length == 24) { // 4 int32_ts, 1 float, 1 int32_t sized byte

        mHasInsets = true;

        memcpy(&mOutlineInsets, data, sizeof(int32_t) * 4);

        mOutlineRadius = ((const float*)data)[4];

        mOutlineAlpha = ((const int32_t*)data)[5] & 0xff;

    }

    return true;

}

我们看到这里处理了npTcnpLbnpOl三个数据块,当判断有npTc这个数据块的时候,系统就认为这是.9图片,就会进行下一步处理。

npTc这个数据又是从哪来的呢?

从上面内容我们知道已经添加了一些额外信息,我们发现官方的说明里有一句,要把.9图放在src/drawable目录下,这是因为在编译的时候,aapt会在发现图片名字符合.9图规则的时候,把四周的黑色边框提取出来,放在npTc数据块里面。

接下来我们看看结构体Res_png_9patch里面有什么。

/**

 * 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 colors array 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 alignas(uintptr_t) Res_png_9patch

{

    int8_t wasDeserialized;

    uint8_t numXDivs, numYDivs, numColors;


    uint32_t xDivsOffset, yDivsOffset, colorsOffset;

    int32_t paddingLeft, paddingRight, 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

    };


    ...

      

    inline int32_t* getXDivs() const {

        return reinterpret_cast<int32_t*>(reinterpret_cast<uintptr_t>(this) + xDivsOffset);

    }

    inline int32_t* getYDivs() const {

        return reinterpret_cast<int32_t*>(reinterpret_cast<uintptr_t>(this) + yDivsOffset);

    }

    inline uint32_t* getColors() const {

        return reinterpret_cast<uint32_t*>(reinterpret_cast<uintptr_t>(this) + colorsOffset);

    }

}

从注释我们看出,一张图片被分成了很多个部分,s开头的代表可以拉伸的,f开头的代表不能拉伸,还有一些颜色的数据,他们共同构成了这个结构体,x,y轴上这些可以拉伸和不可拉伸的部分分别放在xdivydivs数组里,同时内容的显示区域也由几个padding来存放,到这里,就把.9图的额外信息都给读出来了。

Now that the additional information has been obtained, when drawing, the system can judge how to stretch a .9 image based on this information. When the drawing is called NinePatchDrawable, it will eventually go to the native layer to process the data.

So far, the process of making .9 and parsing .9 has been analyzed. It is quite surprising that the background of a .9 picture is so complicated. It really is endless learning.

.9 Figure application and a small pit

After reading how to generate and its principle, let's use it, go back to the beginning and solve the problem with our new cool .9 diagram! code like this

picture.pngThe effect is like this

picture.png

Why is our .9 image not stretched as expected, but the content is stretched too? Because of the characteristics of the .9 image, it only supports stretching . If an image itself is larger than the imageview, it will not be stretched, so our .9 image itself is too large, so there is no stretching. That said, it's definitely not valid. Then let's try to reduce the size of the picture again, now it can be stretched, let's see the effect.

picture.png

It can be seen that the icon below is blocked, but the content is too small , so it is not a good idea to use the .9 image as the splash screen background .

So, in what scenarios can it be used? To sum up, a picture, the corners cannot be stretched, and other parts can be stretched as the content increases, like this.

picture.png

To put it simply, it is like chat bubbles. If the main body of a picture is to be scaled, it is not suitable, because the .9 picture will only stretch a specific part, and the main part will maintain the original size.

For example, we do the background image above.

Summarize

  1. The .9 image is not suitable for full-screen display pages. Either it is not too big and there is no stretching effect, or the content is too small and unsightly.
  2. .9 The picture is suitable for some backgrounds with solid color stretching and no deformation, such as chat bubbles and button backgrounds.
  3. In android, the .9 map must be placed in the sr/drawable directory, and the system will generate the corresponding data block before it can be parsed, otherwise it will be generated by itself
  4. Ordinary pictures cannot achieve the effect of .9 picture because there is no additional data.

Guess you like

Origin juejin.im/post/7118282635972968484