引子

小u啊,我们应用启动的时候有一段白屏,不雅观,你给整个启动图上去,给,这里是资源图片

换好了

嗯,不错不错,咦,这个小米fold怎么显示了两个logo数组的定义

啊?这。。。我来看看

是这样的,activity启数组初始化动图和启动背景图标一起显示了,但是启动图片又不适配fold这数组的定义种狭长的屏幕,而且下半部分由于没有背景,是透明的,所以就显示了两个图标,一个启动图标,一个启动图图片图标,请看示意图

一张启动图引发的思考--探索.9图原理和应用场景

那怎么解决呢?

有几个办法,数组的定义1.让启动图拉伸,覆盖下部分区域,但是会数组c语言有形数组初始化变,不够优雅,2.给imageview设置一个白色背景,让数组的定义它不透明,3.使用android studio的。9图片制作工具,让空白部分拉伸,内容不拉伸,非常优雅。

一张启动图引发的思考--探索.9图原理和应用场景

好,那你先试试.9图片吧

.9简介

我知道看这篇文章的朋友肯定对.9图或多或少有点了解,就简单介绍下就行 .9图可以通过设置来让图片某一部分拉伸 其他部分不拉伸 还可以控制图片里面显示内数组初始化容的区域 这就是.9图的简介 够简了吧 如果想看更详细的说明可以移步谷歌官方的说明 大体上也是这意思

这个是官数组词方的文档developer.android.com/studio/writ…

一张启动图引发的思考--探索.9图原理和应用场景

一张启动图引发的思考--探索.9图原理和应用场景

一张启动图引发的思考--探索.9图原理和应用场景

这是.9图在数组去重方法三种拉伸情况下的例子,纵向,横向,横数组去重方法纵双向。

.数组去重9图制作

网上很多文章说最简单是用as内置工具来做 但是数组排序网上文章好多都是很早的 都是老版本工具 让我们来看看这工具现在啥样

一张启动图引发的思考--探索.9图原理和应用场景
好像也不是很简单啊 这些东西做什么用的第一次看确实会很蒙,这里介绍下这个工具

  • Zoom:调数组去重方法整图形在绘制区域中的缩放级别。

  • Patch scale:调整图像在预览区域中的比例。

  • Show l数组和链表的区别ock:当鼠标悬停在图形的不可绘制区域上时以直观方式呈现。

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

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

  • Show bad patches:在拉伸时可能会在图形中产生伪影的图块区域周围添加红色边框,如图 2 所示。如果您消除所有不良图块,已拉伸图像的视觉连贯性将得以保持。

怎么生成.9图不是重点,网上文章不要太多,最主要就是绘制左边和上面的黑线,左边黑线控制的是哪里可以被上下拉伸,上面黑线控制哪里可以被水平拉伸,可以有多条数组排序,右边和下面的黑线控制了哪里可以放内容,不在这个区域的内容不会被显示。

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

.9图原理

上面的介绍都很数组指针大众化 那么为啥.9图这么神数组去重方法奇呢?它是什么原理呢,这个好像没什么数组和链表的区别人说过,这里也简单阐述下。

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

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

一张启动图引发的思考--探索.9图原理和应用场景

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

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

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

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

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

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

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

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

NinePatchDrawable图形是一种可拉伸的数组c语言数组去重图,可用作视图的背景。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 ima数组ge files.

A PNG file consists of a PNG signature followed by a se数组ries of chunks. This cha数组词pter defi数组c语言nes the signature and the basic properties of chunks.

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

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

重点来看看数据块的布局

名称 字节数 说明
长度(L数组ength) 4字节 指定数据块中数据区域的长度,长度不可超过(231-1)个字节
数据块类型码(Chunk Type Code) 4字节 数据块类型码由数组初始化AS数组词CII字母(A-Z和a-z)组成的”数据块符号数组的定义
数据块数据(Chunk Data) 可变长度 存储数据块类型码指定数组指针的数据
循环冗余检测(CRC) 4字节 存储用来检测是否文件数组c语言传输有误的循环冗余码

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

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

原图数据如下

一张启动图引发的思考--探索.9图原理和应用场景

.9数据如下

一张启动图引发的思考--探索.9图原理和应用场景

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

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

这就涉及到andro数组和链表的区别id怎么加载一张图片的问题了,当然这些操作都是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;
}

我们数组排序看到这里处理了npTcnp数组指针LbnpOl三个数据块,当判断有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图的额外信息数组公式都给读出来了。

既然已经获得了这些额外的信息,那么绘制的时候,系统就可以根据这些信息判断怎么拉伸一数组词张.9图了,绘制调用了NinePatchDrawable,最终也会走到native层去处理这些数据。

到此为止,制作.9和解析.数组排序9的过程就分析完毕了,一张数组去重.9数组去重方法图背后居然这么复杂,还是挺数组指针让人意外的,果然是学无止数组去重方法境啊。

.9图应用以及一个小坑

看完了怎么生成和它的原理 该我们去使用它了 回到开头数组 用我们新做的酷炫.9图解决问题吧! 代码张这样

一张启动图引发的思考--探索.9图原理和应用场景
数组指针果张这样

一张启动图引发的思考--探索.9图原理和应用场景

为什么我们的.9图没有按照预期的拉伸,而是把内容也给拉伸了?因为.9图的特性问题,它只支持拉伸,如果一个图片本身就比imageview数组排序大了,那就不会去拉伸,所以我们的.9图本身太大了,也数组去重方法就不数组指针存在拉伸的说法了,那肯定是无效的。 那我们再来尝试把图片尺寸改小,这下可以拉伸了吧,来看看效果。

一张启动图引发的思考--探索.9图原理和应用场景

可以看数组词到,下面图标被遮挡住了,但是内容也太小了吧所以数组去重方法用.9图来做数组去重方法s数组的定义plash screen背景不是个好主意

那么,到底哪些场景可以用它呢?总结一下就是,一张图片,角落不能拉伸,其他部分可以随着内容的增多随便拉伸的情况,像这样。

一张启动图引发的思考--探索.9图原理和应用场景

简单来说,就是聊天气泡一类的,如果一张图片的主体是内容要放缩的话,是不适合的,因为.9图只会拉伸特定部分,主体部分会数组去重方法维持原大小。

比如我们上面做背景数组的定义图这种情况。

总结

  1. .9图并不适合作为全屏展示页面,要不太大了没有拉伸效果,要不内容太小了不美观数组
  2. .9图适合做一些纯色拉伸不形变的背景,比如数组公式聊天气数组和链表的区别泡和按钮背景这种
  3. 数组词andr数组指针oid中,.9图得放到sr/drawable目录才行,系统才会去生成对应数据块,才可以被解析,否则就要自己去生成
  4. 普通图片由于没有额外的数据,是不能达到.9图这样的效果的