1. 程式人生 > >Android 點九圖機制講解及在聊天氣泡中的應用

Android 點九圖機制講解及在聊天氣泡中的應用

## 點九圖簡介 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,解決方