RecyclerView實現優雅的瀑布流圖片加載

開篇


這是我在簡書上寫的第一篇博客。非常喜歡簡書的風格,所以想在這里寫下我Android之路所遇到的一些麻煩,和我如何解決這一問題。

<small>之前一直使用ListView和GridView來應用在開發中,但是一直仰慕RecyclerView的大名,故開始踏上了RecyclerView的使用之旅。在使用過程中是遇到了很多的坑,比如說給RecyclerView 加上事件監聽,我直接在RecyclerView上add了一個監聽事件,可是程序運行之后卻發現并沒有事件回調出來。。。后來才知道RecyclerView并沒有提供相應的接口,比如ClickListener和LongClickListener。我不明白為什么谷歌為什么這么做,可能是為了讓他更能專注于他的技能Recycler吧。
??好了接下來是正題,大家都知道開發中我們常常使用RecyclerView實現三種布局管理,分別是LinearLayoutManager、GridLayoutManager和StaggeredGridLayoutManager,前兩種一般是沒什么太大問題,今天我們來說說第三種瀑布流的實現方式。</small>

*這是我們最基礎的用法,添加瀑布流3列垂直分布

StaggeredGridLayoutManager layoutManager = new StaggeredGridLayoutManager(3, StaggeredGridLayoutManager.VERTICAL);
recyclerView.setLayoutManager(layoutManager );
adapter = new MyRecyclerAdapter();
recyclerView.setAdapter(adapter);

這里我根據圖片數量給他設置一些隨機高度,因為我使用的接口并沒有返回圖片的寬高

List<Integer> heights = new ArrayList<>();
for (int i = 0; i < urls.size(); i++) {
  int x=random.nextInt(200) + 200;
  heights.add(x);
}

并在onBindViewHolder中設置itemView的高度

StaggeredGridLayoutManager.LayoutParams layoutParams =
(StaggeredGridLayoutManager.LayoutParams) itemView.getLayoutParams();
layoutParams.height = heights.get(position);
itemView.setLayoutParams(layoutParams);
我放入了一些測試數據,并使用了Picasso加載圖片

<img src="http://upload-images.jianshu.io/upload_images/2826360-0485125ee3747b15.gif?imageMogr2/auto-orient/strip" style="zoom:50%"/>

我們發現被滑出屏幕的圖片又消失被重新加載了

這是什么原因呢

<small>給bindView打上日志,發現itemView一旦劃入可見區域,便會調用onBindViewHolder方法...于是Picasso又給我們的圖片重新加載了一遍</small>

這里我的思路是:

  • 設置ImageView的tag,可以設置tag為position或者url。
  • 在加載圖片的時候先進行判斷是否有tag,沒有tag進行圖片加載
        @Override
        public void onBindViewHolder(MyRecyclerViewHolder holder, final int position) {
            if (holder.itemView.getTag() == null) {
                holder.itemView.setTag(position);
                holder.bind(position);
            }
        }

這樣就不會重復加載了,不知道是否有更加合理的方式,有請告知,感激不盡。

接下來我們的效果是

autoChangeItem.gif

這里的問題可愁了我了……
網上查了好久都沒有發現好的文章能真正幫我解決這個問題
最終放棄尋求別人幫助,還是靠自己解決吧。。。
我決定對RecyclerView的Adapter進行拆解,對他的各種可能需要用到的方法進行日志打印,我相信create和bind方法兩個肯定是打上了日志,最終我發現每次itemView自動交換位置的時候,onCreateViewHolder這個方法就會被調用。哪里有問題點哪里。。。我們來看一下源碼中,對onCreateViewHolder的描述,我截取了最為重要的部分

* @param parent The ViewGroup into which the new View will be added after it is bound to
*               an adapter position.
* @param viewType The view type of the new View.
*
* @return A new ViewHolder that holds a View of the given view type.
* @see #getItemViewType(int)
* @see #onBindViewHolder(ViewHolder, int)

<small>可見RecyclerView真的很靈活,他根據你返回的View類型來決定create一個新的視圖的類型,我想問題出在這里,默認返回的是0,當我從下往上滑到最頂部的時候,復用的View大小是根據下面的itemView的大小,所以高度上出現了不適應,layoutManager會自動調整位置,于是出現了上述情況</small>

我們重寫getItemViewType方法,讓我們的高度成為type的值

@Override
public int getItemViewType(int position) {
     return heights.get(position);
//   return super.getItemViewType(position);
}

OK,我們進行一下測試。


last.gif

至此我們實現了瀑布流圖片加載

??但是還不夠優雅,因為我相信,我在這里使用圖片的高度作為ViewType肯定是有問題的,因我的圖片高度太多不固定的值,有的是重復的,但是大多數是不重復的,這勢必會造成性能問題。所以建議在使用瀑布流的時候,不要用太多不同的高度,適度就行。。。一般是根據圖片的大小來計算。

當然具體情況還是得具體對待……

結束語:我對于網上某些寫的RecyclerView的文章還是要說一句,多一些真誠,少一些CV。
文章中如有什么錯誤,請您諒解并且可以給我留言指出,寫了好久,休息休息。Thanks!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容