瀑布流也是個常用的顯示控件了,但是在使用時經常遇到一些問題,比如滑動回頂部后出現空隙、item在滑動時亂跳等問題。
下面就來說說我怎么實現的瀑布流,并且怎么處理上面所說的這些問題的。
我使用了原生控件RecyclerView+StaggeredGridLayoutManager來實現的瀑布流,沒有用第三方開源框架。下面以2列的瀑布流為例子開始講解。
因為使用了StaggeredGridLayoutManager實現瀑布流,但是在設置后發現圖片在滑動加載過程中高度會發生變化,在網上搜索了很多資料后,總結解決辦法是在onBindViewHolder中綁定View時,給ImageView設置寬高,就能解決這個問題。
先看一下最終實現效果:
提前說明下,我使用的是Glide3,讀者們可以自行修改為Glide4。
1.實現瀑布流
先說說實現思路:
- 寫布局文件,分別有2個布局文件,Activity的布局文件和Adapter的布局文件
- 寫適配器,瀑布流的適配器里需要設置ImageView的寬高。
- 寫RecyclerView,給RecyclerView設置StaggeredGridLayoutManager并設置適配器。
- 添加數據測試效果,根據效果反饋進行修改
第一步:寫布局文件
Activity的布局文件只有一個RecyclerView就不貼了,貼一下Adapter的布局文件:
adapter_item_card.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/card"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:background="@android:color/holo_green_dark"
android:orientation="vertical">
<ImageView
android:id="@+id/card_image"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:src="@mipmap/ic_launcher" />
<TextView
android:id="@+id/card_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="8dp"
android:layout_marginBottom="2dp"
tools:text="hello" />
</LinearLayout>
第二步:寫適配器
適配器中包含數據Card的集合,Card類包含如下幾個屬性:
private String title;
private String img_url;
private int width;
private int height;
在適配器中主要就是將數據綁定到view上,最關鍵的步驟是根據圖片的寬高算出圖片的寬高比,然后根據寬高比選擇正方形顯示,還是長方形顯示,最后通過setLayoutParams方法來設置圖片的寬高。
思路如下:
- 計算圖片寬度
- 根據圖片寬高比,確定圖片使用正方形或是4比3的長方形顯示
- 使用setLayoutParams方法設置圖片寬高
- 使用Glide加載圖片并用override重寫圖片寬高
適配器核心代碼如下:
private final double STANDARD_SCALE = 1.1; //當圖片寬高比例大于STANDARD_SCALE時,采用3:4比例,小于時,則采用1:1比例
private final float SCALE = 4 * 1.0f / 3; //圖片縮放比例
private List<Card> cards = new ArrayList<>();
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
Card card = mCards.get(position);
setCardView(holder, card);
}
private void setCardView(ViewHolder holder, Card card) {
//計算圖片寬高
LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) holder.image.getLayoutParams();
//2列的瀑布流,屏幕寬度減去兩列間的間距space所的值再除以2,計算出單列的imageview的寬度,space的值在RecyclerView初始化時傳入
float itemWidth = (ScreenUtil.getScreenWidth(context) - space) / 2;
layoutParams.width = (int) itemWidth;
float width = card.getWidth();
float height = card.getHeight();
float scale = height / width;
if (scale > STANDARD_SCALE) {
//采用3:4顯示
layoutParams.height = (int) (itemWidth * SCALE);
} else {
//采用1:1顯示
layoutParams.height = (int) itemWidth;
}
holder.image.setLayoutParams(layoutParams);
Glide.with(context).load(card.getImg_url()).asBitmap().placeholder(R.mipmap.ic_launcher)
.diskCacheStrategy(DiskCacheStrategy.RESULT).centerCrop().into(holder.image);
holder.title.setText(card.getTitle());
}
寫好適配器后,就可以在MAinActivity中初始化RecyclerView和適配器了,代碼如下:
int space = 20;
StaggeredGridLayoutManager layoutManager = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);
// layoutManager.setGapStrategy(StaggeredGridLayoutManager.GAP_HANDLING_NONE); //設置后瀑布流不顯示了
mRecyclerView.setLayoutManager(layoutManager);
mRecyclerView.setItemAnimator(null);
mRecyclerView.addItemDecoration(new StaggeredItemDecoration(space));//單位px
mAdapter = new StaggeredGridAdapter(space);
mAdapter.setCards(mCards);
mRecyclerView.setAdapter(mAdapter);
在網上看到使用StaggeredGridLayoutManager的setGapStrategy(StaggeredGridLayoutManager.GAP_HANDLING_NONE)設置來處理瀑布流滑動到頂部空白的問題,結果發現添加這句代碼后,整個瀑布流都不顯示了,所以不能這樣處理。
在上面的代碼中我設置了space值,space是指兩列卡片之間的距離,根據需求設置,這里space用在了2個地方分別是:
mRecyclerView.addItemDecoration(new StaggeredItemDecoration(space));
mAdapter = new StaggeredGridAdapter(space);
前者用于設置兩列瀑布流之間的距離,后者是用來計算單列圖片的寬度。StaggeredItemDecoration類的代碼在此。
代碼寫好后,來看看瀑布流效果。
好像有點奇怪的地方,在滑動過程中前面圖片1、2、3的大小發生了變化。我當時也是很疑惑,在網上搜索圖片大小改變的問題原因也沒有找到,好像與RecyclerView的圖片緩存機制有關,有知道的胖友可以告知一下。
最后通過在Glide加載圖片時添加override設置圖片寬高解決了,關于override設置圖片可以看看這篇文章《Glide的override方法和View的setLayoutParams方法設置圖片寬高對比》。
Glide.with(context).load(card.getImg_url()).asBitmap().placeholder(R.mipmap.ic_launcher)
.diskCacheStrategy(DiskCacheStrategy.RESULT).override(layoutParams.width, layoutParams.height).centerCrop().into(holder.image);
解決后的效果如下,可以看到在滑動過程中,圖片大小沒有再變化:
2. 瀑布流頂部出現空隙、item亂跳等問題
照上面的處理已經能解決頂部出現空隙、item亂跳的問題,但是建議在瀑布流更新時采用notifyItemRangeInserted()方法更新,可以避免一些不必要的問題。
if (FIRST_PAGE_LAST_ID.equals(lastId)) {
mAdapter.notifyDataSetChanged();//第一頁更新
} else {
mAdapter.notifyItemRangeInserted(startPosition, count);//第一頁以外使用notifyItemRangeInserted()更新
}
如果對你有幫助的話,點贊、評論、贊賞都是對我的鼓勵,也是支持我寫下去的動力,謝謝!