Android 點九圖機制講解及在聊天氣泡中的應用
阿新 • • 發佈:2019-09-20
## 點九圖簡介
Android為了使用同一張圖作為不同數量文字的背景,設計了一種可以指定區域拉伸的圖片格式“.9.png”,這種圖片格式就是點九圖。
注意:這種圖片格式只能被使用於Android開發。在ios開發中,可以在程式碼中指定某個點進行拉伸,而在Android中不行,所以在Android中想要達到這個效果,只能使用點九圖(**下文會啪啪打臉,其實是可以的,只是很少人這樣使用,相容性不知道怎麼樣,[點選跳轉](#create-a-ninepatch-ninepatchdrawable-in-runtime)**)
![](https://img2018.cnblogs.com/blog/1001668/201909/1001668-20190920122130766-1087525161.jpg)
### 點九圖實質
點九圖的本質實際上是在圖片的四周各增加了1px的畫素,並使用純黑(#FF000000)的線進行標記,其它的與原圖沒有任何區別。可以參考以下圖片:
![](https://img2018.cnblogs.com/blog/1001668/201909/1001668-20190920122132002-1371443004.jpg)
![](https://img2018.cnblogs.com/blog/1001668/201909/1001668-20190920122134519-1765319393.jpg)
標記位置 | 含義
---|---
左-黑點 | 縱向拉伸區域
上-黑點 | 橫向拉伸區域
右-黑線 | 縱向顯示區域
下-黑線 | 橫向顯示區域
---
## 點九圖在 Android 中的應用
點九圖在 Android 中主要有三種應用方式
1. 直接放在 res 目錄中的 drawable 或者 mipmap 目錄中
2. 放在 assert 目錄中
3. 從網路下載
第一種方式是我們最常用的,直接呼叫 `setBackgroundResource` 或者 `setImageResource` 方法,這樣的話圖片及可以做到自動拉伸。
而對於第二種或者第三種方式,如果我們直接去載入 .9.png,你會發現圖片或者圖片背景根本無法拉伸。**納尼,這是為甚麼呢**。下面,且聽老衲慢慢道來。
Android 並不是直接使用點九圖,而是在編譯時將其轉換為另外一種格式,這種格式是將其四周的黑色畫素儲存至Bitmap類中的一個名為 `mNinePatchChunk` 的 byte[] 中,並抹除掉四周的這一個畫素的寬度;接著在使用時,如果 Bitmap 的這個 `mNinePatchChunk` 不為空,且為 9patch chunk,則將其構造為 `NinePatchDrawable`,否則將會被構造為 BitmapDrawable,最終設定給 view。
因此,在 Android 中,我們如果想動態使用網路下載的點九圖,一般需要經過以下步驟:
1. 使用 sdk 目錄下的 aapt 工具將點九圖轉化為 png 圖片
2. 解析圖片的時候,判斷是否含有 NinePatchChunk,有的話,轉化為 NinePatchDrawable
```
public static void setNineImagePatch(View view, File file, String url) {
if (file.exists()) {
Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath());
byte[] chunk = bitmap.getNinePatchChunk();
if (NinePatch.isNinePatchChunk(chunk)) {
NinePatchDrawable patchy = new NinePatchDrawable(view.getResources(), bitmap, chunk, new Rect(), null);
view.setBackground(patchy);
}
}
}
```
### 點九圖上傳伺服器流程
![](https://img2018.cnblogs.com/blog/1001668/201909/1001668-20190920122136178-439079606.jpg)
---
## aapt 轉換命令
單個圖片檔案轉換
```
./aapt s -i xxx.9.png -o xxx.png
```
批量轉換
```
# 批量轉換
./aapt c -S inputDir -C outputDir
# inputDir 為原始.9圖資料夾,outputDir 為輸出資料夾
```
執行成功例項
```
jundeMacBook-Pro:一期氣泡 junxu$ ./aapt c -S /Users/junxu/Desktop/一期氣泡/氣泡需求整理 -C /Users/junxu/Desktop/一期氣泡/output
Crunching PNG Files in source dir: /Users/junxu/Desktop/一期氣泡/氣泡需求整理
To destination dir: /Users/junxu/Desktop/一期氣泡/output
```
**注意:**
若不是標準的點九圖,在轉換的過程會報錯,這時候請設計重新提供新的點九圖
---
## 實際開發當中遇到的問題
### 小屏手機適配問題
剛開始,我們的切圖是按照 2 倍圖切的,這樣在小螢幕手機上會手機氣泡高度過大的問題。
![](https://img2018.cnblogs.com/blog/1001668/201909/1001668-20190920122144454-572877595.jpg)
原因分析:
該現象的本質是點九圖圖片的高度大於單行文字訊息的高度。
**解決方案一(暫時不可取):**
1. 我嘗試去壓縮點九圖,但最終再部分手機上面顯示錯亂,不知道是不是壓縮點九圖的方法錯了。
**解決方案二**
對於低解析度的手機和高分辨的手機分別下發不同的圖片 url,我們嘗試過得方案是當 `density < 2` 的時候,採用一倍圖圖片,`density >= 2` 採用二倍圖圖片。
**解決方案三**
可能有人會有這樣的疑問呢,為什麼要採用一倍圖,兩倍圖的解決方案呢?直接讓 UI 設計師給一套圖,點九圖圖片的高度適中不就解決了。是啊,我們也是這樣想得,但他們說對於有一些裝飾的點九圖,如果縮小高度,一些裝飾圖案他們不太好切。比如下面圖片中的星星。
![](https://img2018.cnblogs.com/blog/1001668/201909/1001668-20190920122132002-1371443004.jpg)
**小結**
說到底,方案二,方案三其實都是折中的一種方案,如果直接能夠做到點九圖縮放,那就完美解決了。而 Android 中 res 目錄中的 drawable 或者 mipmap 的點九圖確實能做到,去看了相關的程式碼,目前也沒有發現什麼好的解決方案,如果你有好的解決方案話,歡迎留言交流。
### 點九圖的 padding 在部分手機上面失效
這個是部分 Android 手機的 bug,解決方