自己造輪子:一款實用的Android廣告欄實現過程

發表於2016-01-20

開源界有一句很有名的話叫“不要重複發明輪子”,當然,我今天的觀點不是要反駁這句話,輪子理論給我們的開發帶來了極大的便利,專案中要實現一些功能,便去網上找找,一般推薦使用一些有名的庫,我本身也是這麼做的,但我想說的是,既要會用輪子,也要知道輪子怎麼造,必要的時候,自己也要造輪子(想要找到一個完全滿意的輪子還是不大容易的)。

由來

之前專案裡面都是用的daimajia的AndroidImageSlider,一開始被驚豔的動畫切換效果吸引了,還有各種自定義屬性動畫啥的,感覺很棒,但隨著專案的進展和時間的推移,我慢慢發現它也不是無所不能的,甚至我發現它只是好看,卻不怎麼實用,我列舉一些我發現的問題:

  • 庫中整合了Picasso,Picasso是極其耗記憶體的,這點我在之前的一篇為什麼圖片載入我首先Glide 提到過,當一個頁面既有廣告又有列表(列表中也一般有圖片)會造成頁面嚴重卡頓。
  • 動畫效果太炫,實際專案中一個沒用到,一般用標準模式。
  • 太多的依賴包,現在的許多專案都是基於android4.0以上版本開發的,nineoldandroid已經不需要了
  • 頁面點選事件不直觀,如果有那種像ListView的OnItemClickListener就好了

總而言之,我感覺它現在已經有些臃腫。當然,我也在網上尋找更簡潔實用的替代庫,比如BGABanner-Android,但事實也是殘酷的,不支援網路圖片,還有一個坑等著我跳,看程式碼片段

低於3張圖片會直接拋異常,這叫我怎麼用,需求也不是我能控制的。
當然以上兩個庫的作者我都很喜歡,這兩個庫也非常不錯,不然也不值得我花時間研究。

我對輪子的要求

結合自己的理解,我認為兩個庫中都有可取之處,也有不足之處,我就取長補短,造自己的輪子,我對這個輪子的要求:

  • 無限輪播
  • 自動加手動滑動
  • 簡單的自定義指示器樣式及位置
  • 支援本地圖片及網路圖片
  • 滑動流暢,無卡頓,無閃爍
  • 廣告頁面不限制個數
  • 頁面點選監聽事件
  • 簡單易用,高配置,無明顯bug
    動畫效果我先打算拋棄了,預設的就好,以實用為主。

開始動手,step by step

系統可以滑動翻頁的控制元件就只有ViewPager和ViewFliper網上還有大神實現用RecyclerView實現了類似ViewPager的效果,這裡暫不做過多研究,這裡就選擇使用最多ViewPager作為滑動翻頁控制元件,使用ViewPager+PagerAdapter可以很容易的實現翻頁切換效果,但存在幾個弊端:

  • 不能無限輪迴的翻頁(滑到第一個或者最後一個就不能繼續滑)
  • 切換速度太快,系統預設250毫秒,用做廣告欄切換會存在明顯閃爍
  • 僅支援手動滑動,不支援自動切換
  • 沒有提供直接的類似ListView的OnItemClickListener監聽,使用起來很不方便
    當然還會有其他的坑,遇到了再解決,先解決上面的問題

無限輪播效果

這裡採用網上通用的解決辦法,偽輪播(讓使用者看到輪播的假象,實際上用了很多頁面在不斷重複出現,如果使用者滑動幾十億下是可以滑到頭,實際幾乎不可能有人這麼做)看具體實現程式碼

還需要做一件事情,就是設定當前position為中間的一個較大的值,如果不設定或者設定的比較小,往左滑動容易滑倒頭

改變原生ViewPager切換速度

通過反射拿到ViewPager的滑動器mScroller,改變duration引數,看程式碼:

自動切換實現

這裡可以有多種方式,使用Handler或者Timer都可以的,這裡採用handler實現,isAutoPlay可以控制是否禁止控制元件自動輪播,autoPlayDuration是輪播間隔時間,還需注意觸控時應當停止輪播,放開恢復正常

先附上一張預覽圖:

bannerLayoutDemo.gif

先寫這麼多吧,後續完整程式碼我會上傳到github,如果大家有興趣,我會抽時間寫剩下的內容,這個控制元件的程式碼也是借鑑了很多優秀的開源庫,並結合自己的理解寫的。

炫麗的效果固然吸引人眼球,平凡實用的東西才愈久彌香

完整程式碼已上傳到github:BannerLayoutDemo

新增指示器–繪製指示器樣式

github上也有各種各樣很棒的指示器,可作為獨立控制元件,這裡我先簡單處理直接整合到內部,後期有需求再進行重構,最簡單的指示器是用兩張不同狀態的小圖片做的,但我認為這樣做相對實現是簡單的,但對於修改卻顯得有些麻煩,適配也是問題,簡單修改何必大動干戈呢?

這裡借鑑了daimajia的思路,指示器是用程式碼繪出來的,怎麼繪呢?看程式碼,以選中的狀態為例,繪製一次便可,將drawable存起來使用:

這樣指示器的形狀,大小,顏色可以隨意換了,支援換膚也更容易

新增指示器–位置

大家都知道在xml檔案中使用RelativeLayout父佈局可以控制子佈局的位置,用程式碼怎麼去做呢?首先,BannerLayout是繼承與RelativeLayout的,看程式碼

兩個屬性必須分開新增不能寫成

如何設定指示器margin和padding這裡就不再多說

切換指示器

我之前的想法是這樣的,不用迴圈做,只需知道上一個選中的頁面和即將要跳轉的頁面位置即可,實現思路是這樣的

但實際的執行效果卻可能出現錯亂的現象,不知是哪裡出了問題,目前就採用了迴圈來做,後期可能會改進,這樣做雖然簡單,但效率始終不高

新增頁面點選監聽回撥

用法就像ListView的setOnItemClickListener一樣,來看看程式碼如何實現

使用

遇到的一些坑

上篇講到了BGABanner-Android,部分思想也是參考這個庫的,例如指示器位置的處理方案,但我要說的坑也在這裡,低於3張圖片就會直接拋異常,我試著將異常不丟擲看看,一張或者兩張圖片的時候切換效果慘不忍睹(我猜想和ViewPager的懶載入機制有關),所以作者處理為直接拋異常,但這樣不行啊,需求不可控制啊,必須解決這個問題,不然就像定時炸彈。

問題解決思路:既然輪播是偽的,圖片的張數也可以是偽的,只需要給使用者看起來是那樣就行了,1張也可以是3×1張相同的圖片,2張也可以是2×2張相同圖片,3張及以上沒問題就無需處理,關鍵程式碼

新增網路地址和本地的思路差不多,使用的是Glide來處理載入網路圖片,還有就是新增各種自定義屬性。

github地址BannerLayoutDemo

完整程式碼實現請看原始碼,歡迎fork和star以及提出你寶貴的意見

相關文章