RecyclerView實作瀑布流,完美解決留白、錯亂等問題,廢話不多說。
網上很多說解決留白要用:
layoutManager.invalidateSpanAssignments();
是滴 雖然留白解決了,but會出現新的問題,當你往上滑動到頂部的時候,明顯左右item其中一個必然出現向下滑動的視覺效果,很惡心的一個坑,是以說這個方法最好别用,因為隻是從一個坑跳進了另一個坑裡邊,而且這個方法會造成界面的頻繁繪制。解決方法下邊會說到。
防止錯亂,抖動方法如下:
//防止item 交換位置
StaggeredGridLayoutManager layoutManager = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);
layoutManager.setGapStrategy(StaggeredGridLayoutManager.GAP_HANDLING_NONE);
recyclerView.setLayoutManager(layoutManager);
((DefaultItemAnimator) recyclerView.getItemAnimator()).setSupportsChangeAnimations(false);
((SimpleItemAnimator) recyclerView.getItemAnimator()).setSupportsChangeAnimations(false);
recyclerView.getItemAnimator().setChangeDuration(0);
recyclerView.setHasFixedSize(true);
recyclerView.addItemDecoration(new SpaceItemDecoration(2, 20));
防止留白方法如下:
RecyclerView實作瀑布流,比如要用到加載更多的方法,解決辦法就在這裡,加載更多的時候,資料填充進adapter之後的重新整理,要用這個方法
notifyItemRangeInserted(positon,size);
1:其中position就是你添加資料之前,adapter裡邊的資料總量,其實也是開始重新整理的那個下标的位置
2:size就是加載更多擷取到的第二頁的資料的長度
3:用這個方法之前,要先把擷取到的資料通過addAll()方法先注入到adapter的資料集合中,之後可以調用這個方法
類似這樣:
preDataNum = this.data.size();
this.data.addAll(data);
notifyItemRangeInserted(preDataNum,data.size());
另:
notifyItemInserted(preDataNum);
notifyItemRangeChanged(preDataNum,data.size());
notifyDataSetChanged();
notifyItemRangeInserted(preDataNum,data.size());
notifyItemChanged(preDataNum -1);
雖然提供的局部重新整理的防範很多,但是在這裡,親測 隻有notifyItemRangeInserted(preDataNum,data.size());這個辦法靠譜
其次就是設定每個item的寬高,目前demo還在完善中,寬高的設定,目前是通過重寫imageview實作的
public class DynamicHeightImageView extends ImageView {
private double mHeightRatio;
public DynamicHeightImageView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public DynamicHeightImageView(Context context) {
super(context);
}
public void setHeightRatio(double ratio) {
if (ratio != mHeightRatio) {
mHeightRatio = ratio;
requestLayout();
}
}
public double getHeightRatio() {
return mHeightRatio;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (mHeightRatio > 0.0) {
// set the image views size
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = (int) (width * mHeightRatio);
setMeasuredDimension(width, height);
}
else {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
}
因為這裡我給每個item的寬度設定的match_parent,瀑布流是兩列的,這裡我隻用設定每個item的比率就行了。
使用方法:
在onBindViewHolder中設定
holder.image.setHeightRatio(ratio);//設定高寬比
多加點吧,RecyclerView實作加載更多,重寫RecyclerView
public class StaggerRecyclerView extends RecyclerView {
private OnLoadMoreListener onLoadMoreListener;
private boolean isLoadingMore = false;
private static final int TOLAST = 2;
public StaggerRecyclerView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
this.addOnScrollListener(new OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
StaggeredGridLayoutManager layoutManager = null ;
if(recyclerView.getLayoutManager() instanceof StaggeredGridLayoutManager){
layoutManager = (StaggeredGridLayoutManager) recyclerView.getLayoutManager();
}else{
return;
}
//下面這句話,就是上邊提到的,不能用
//layoutManager.invalidateSpanAssignments();
int[] positions = null;
int[] into = layoutManager.findLastCompletelyVisibleItemPositions(positions);
int[] firstInto = layoutManager.findFirstVisibleItemPositions(positions);
int lastPositon = Math.max(into[0],into[1]);
int firstPositon = Math.max(firstInto[0],firstInto[1]);
if(!isLoadingMore && dy>0 && layoutManager.getItemCount()-lastPositon<=TOLAST){
//load more
isLoadingMore = true;
if(onLoadMoreListener!=null){
onLoadMoreListener.onLoadMore();
}
}
}
});
}
public void setOnLoadMoreListener(OnLoadMoreListener onLoadMoreListener) {
this.onLoadMoreListener = onLoadMoreListener;
}
public void setLoadingMoreComplete(){
isLoadingMore = false;
}
public interface OnLoadMoreListener{
void onLoadMore();
}
}
上個效果圖吧,

demo還在完善中,後續會發出來。