多圖像合併

用戶圖像一般是一張單圖片,但是羣圖像或者聊天室圖像,以及明星的飯糰圖像,一般是有多張單用戶圖像按照一定規則合成的一張聚合圖像。常見的聚合效果圖如下圖樣式:

 

當然還有五張或是更多張圖像的線性和多邊形聚合的方式,這裏就不一一列舉,那麼這樣的效果是怎麼實現的呢?

討論這個問題的前提是,網絡download圖片的部分不作討論,假定已經熟知單用戶圖像的下載和展示的全過程,本文只討論在這個基礎上,如何實現這種多圖像聚合的效果。

Plan A:雖然知道不是一個好的實現方案,但是一定可以實現的做法。既然可以展示單用戶圖像,再看需要實現的效果圖,無非就是多個單用戶圖像的重疊效果;因此,可以通過多個Imageview的疊加,比如圖3,那麼就四個ImageView,在XML中把佈局間距調整好即可;至於圖4可能會比較複雜點,一個是佈局間距需要慢慢調整,另外一點就是有一層白邊的效果,這個的處理可能需要更多的依賴,比如再加一個背景View

A方案總歸覺得不好,在不知道到底需要把幾個用戶圖像聚合的時候,可能需要寫很多佈局,以適配需要聚合圖像個數導致額差異。那麼有沒有更好的解決方案呢?

答案自然是有的,何不考慮下自定義View + drawBitmap,一起看看Plan B

Plan B:針對Plan A的痛點,可以考慮首先把需要聚合的用戶圖像先download到內存,這時就有聚合圖像數量和該數量的單用戶圖像;預先制定圖像合併規則,按照規則去創建一個特定的圖像合併器Merger,合併器裏面去實現多張Bitmap的重繪,之後生成一張新的Bitmap;最後把這張Bitmap填充到自定義的View裏面。後文將闡述細節。

下載圖片這步的核心是需要寫一個同步方法,保證聚合圖片都下完了才執行後續的聚合圖像方法,可以用一個表示圖片數量的全局變量+synchronized關鍵字來實現。

 

圖像合併規則,會根據不同產品需求而不同,這裏給出一些一些參考規則,後文的繪製就是基於這個規則。圖像聚合分爲兩類場景,一類是線性聚合,每個圖像有白邊陰影效果,每兩個圖像有重疊效果,如效果圖中的圖一和圖四;另外一類是平面聚合,多個圖像呈現平面幾何形狀,三個圖像就是三角形,四個圖像就是正方形等,如效果圖中的圖二和圖三。

說完規則,不急於說合並器Merger,先說下數學。考慮平面聚合裏面的圖二,三張圖片合併成一張圖片,它們三個的位置關係是三張圖片的圓心構成了一個正三角形,如下圖所示:

 

上圖中的R是佈局XML中設定的這個自定義View的寬和高,d是單張用戶圖像的直徑,ABC是三個圓形。考慮繪製Bitmap,需要知道它的leftrighttop,和bottom,所以接下來需要計算出每張圖像的具體位置。圖片A(這裏假定圓心爲A的圖片就叫圖片A)在整個View的相對位置,可以根據幾何知識來計算,圖A的左側位置就是圓心位置減掉半徑,右側是圓心加上半徑,頂部就是View的頂部所以爲0,底部就是圓的直徑,而圓心就是整個View的寬度的一半。

 

同理可以計算出圖B和圖C的位置。

 

說完平面聚合,再說下線性聚合。以效果圖裏面的圖四爲例,四張圖片合併成一張一排有重疊的圖片,它們的圓心在一條直線上,如下圖所示:

 

上圖中,有四張圖片ABCD,圓心也就是ABCD,考慮有白邊效果,內側直徑爲d,也就是真實顯示用戶圖像區域,外側直徑爲D,白邊寬度就是外側直徑減掉內側直徑的一半;而每幅圖像的重疊部分是指外側直徑這個大圓的重合部分,長度爲overlap

合併的時候,首先需要計算出合併後整張圖片的寬和高,高比較直觀,就是外側直徑,寬可以根據幾何知識,是n張圖片的外側直徑減掉(n-1)個重疊部分的長度。

其次是計算出每一張圖片的位置,從圖可以發現規律,每張圖片的左邊都是外側圓直徑減掉重疊部分,右側就是左邊加上外側直徑,頂部就是View的頭所以就是0,底部就是頂部加上外側直徑,所以比較好計算。至於白邊效果,可以通過drawBitmap裏面的設置stroke來達到效果。

 

有了計算公式,接下來就是寫代碼實現一個合併器Merger。定義一個抽象類BitmapMerger,裏面只有一個待實現的抽象方法mergeBitmap,傳入的是下載好的Bitmap數組,返回的是一個合併完成的Bitmap

 

根據合併規則,所以有兩個子類,一個是線性聚合子類,一個是平面聚合子類,在子類裏面去實現具體的聚合方法。以線性聚合子類爲例,在構造函數的時候,傳入聚合需要的參數,如內外側圓的直徑和重疊部分的長度;需要具體實現父類裏的mergeBitmap方法,具體實現:首先創建計算好的合併後Bitmap寬度和高度的Bitmap,之後把需要聚合的Bitmap數組裏面Bitmap取出來,依次繪製,考慮效果需要第一張圖片蓋在第二張圖片上,所以應該是從數組的最後一張開始繪製,最後繪製的是第一張圖片。

 

這裏有一個核心方法,就是drawCircleImageWithStroke方法,它實現了Bitmap的真實繪製過程,本質是利用CanvasdrawCircle方法來實現。具體的代碼如下:

 

在合併圖像的時候,可以利用簡單工廠模式,根據需要合併的規則,去創建不同的合併器BitmapMerger,然後來聚合圖像,補充完善之前遺留的todo

 

還有最後一部分,就是承載這個視圖的自定義View的實現。自定義View的實現比較簡單,這裏就不贅述。說核心的細節點,就是需要合併4張圖片,但是網絡比較慢,需要先展示一張默認圖,等圖片下載完成,合併完成,之後再刷XUI,這裏的觀察者模式也比較常見也不是我說的重點,重點是因爲考慮內存效率,默認圖最好用一張圖展示,而不是也用4張默認圖合併成一張,這樣會浪費資源,這裏可能就會引入的問題就是本來應該展示4張圖合併後的Bitmap的地方只放一張默認圖,會導致默認圖尺寸問題,所以需要寫一個方法,對默認圖做適配處理。

 

這裏提供了一個通過矩陣重新計算默認圖尺寸的方案,僅供參考。

至此,整個實現過程已經講完,還參入部分設計模式,方便擴展,如果需要引入其他的合併規則,可以直接寫一個具體的BitmpaMerger子類,這樣在不破壞整體架構基礎之上,實現需求。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章