這是一個使用RecyclerView實現(xiàn)瀑布流跨嘉,并帶上下拉刷新和上拉加載功能的Demo。做Demo之前看了很多網(wǎng)友們實現(xiàn)瀑布流踩的坑吃嘿,所以這個Demo把那些常見的坑都填上了祠乃,目前沒發(fā)現(xiàn)有什么問題。
關(guān)于下拉刷新和上拉加載兑燥,說實話我每次做這個功能都很頭疼亮瓷,因為一直沒有找到一個好的方式或者說好的庫,能讓我只拿著一個框架就去適配所有需要下拉刷新和上拉加載功能的ViewGroup降瞳,并且可以自己實現(xiàn)Header和Footer嘱支。今天自己鼓搗了很久瀑布流的Header和Footer,然而光是Footer我就花費了不少功夫,最后在尋找好的Header實現(xiàn)方式的過程中發(fā)現(xiàn)了一個能滿足我全部需求的庫——# SmartRefreshLayout挣饥,講真除师,我以前就見過這個庫,但是那時候覺得這個庫花里胡哨的就沒仔細看下去扔枫,今天定睛一看馍盟,,我的媽呀茧吊,神器呀贞岭,我以前咋就把它錯過了呢!從此我要抱著這個庫不放手了搓侄。
廢話了這么多瞄桨,下邊來看看我的實現(xiàn)吧。
先貼完整代碼
首先是Activity文件
package com.adminstrator.guaguakaapplication;
import android.os.Handler;
import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.StaggeredGridLayoutManager;
import com.adminstrator.guaguakaapplication.widget.StaggeredDividerItemDecoration;
import com.scwang.smartrefresh.layout.SmartRefreshLayout;
import com.scwang.smartrefresh.layout.api.RefreshLayout;
import com.scwang.smartrefresh.layout.listener.OnLoadMoreListener;
import com.scwang.smartrefresh.layout.listener.OnRefreshListener;
import java.util.ArrayList;
public class WaterFallActivity extends AppCompatActivity {
private ArrayList<Integer> imageIds = new ArrayList<>();
private int[] ids = {R.drawable.p1,R.drawable.p2,R.drawable.p3,R.drawable.p4,R.drawable.p5,R.drawable.p6,R.drawable.p7,
R.drawable.p8,R.drawable.p9,R.drawable.p10,R.drawable.p11,R.drawable.p12,R.drawable.p13,R.drawable.p14,};
private RecyclerView rv_waterfall;
private DemoAdapter adapter;
private SmartRefreshLayout refreshlayout;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_water_fall);
refreshlayout = findViewById(R.id.refreshlayout);
rv_waterfall = findViewById(R.id.rv_waterfall);
rv_waterfall.setHasFixedSize(true);
rv_waterfall.setItemAnimator(null);
//垂直方向的2列
final StaggeredGridLayoutManager layoutManager = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);
//防止Item切換
layoutManager.setGapStrategy(StaggeredGridLayoutManager.GAP_HANDLING_NONE);
rv_waterfall.setLayoutManager(layoutManager);
final int spanCount = 2;
rv_waterfall.addItemDecoration(new StaggeredDividerItemDecoration(this,10,spanCount));
//解決底部滾動到頂部時讶踪,頂部item上方偶爾會出現(xiàn)一大片間隔的問題
rv_waterfall.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
int[] first = new int[spanCount];
layoutManager.findFirstCompletelyVisibleItemPositions(first);
if (newState == RecyclerView.SCROLL_STATE_IDLE && (first[0] == 1 || first[1] == 1)) {
layoutManager.invalidateSpanAssignments();
}
}
});
//設(shè)置Adapter
for(int i = 0 ; i < ids.length;i++){
imageIds.add(ids[i]);
}
adapter = new DemoAdapter();
rv_waterfall.setAdapter(adapter);
adapter.replaceAll(imageIds);
//設(shè)置下拉刷新和上拉加載監(jiān)聽
refreshlayout.setOnRefreshListener(new OnRefreshListener() {
@Override
public void onRefresh(@NonNull final RefreshLayout refreshLayout) {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
adapter.replaceAll(getData());
refreshLayout.finishRefresh();
}
},2000);
}
});
refreshlayout.setOnLoadMoreListener(new OnLoadMoreListener() {
@Override
public void onLoadMore(@NonNull final RefreshLayout refreshLayout) {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
adapter.addData(adapter.getItemCount(),getData());
refreshLayout.finishLoadMore();
}
},2000);
}
});
}
private ArrayList<Integer> getData() {
ArrayList<Integer> list = new ArrayList<>();
for(int i = 0 ; i < 6;i++){
list.add(ids[i]);
}
return list;
}
}
Activity的布局文件,使用了SmartRefreshLayout芯侥。Header和Footer我都是用了一個360度旋轉(zhuǎn)的Loading圖片。這種Header和Footer的添加方式可以讓你隨意定制自己想要的任何樣式乳讥,及其方便柱查。SmartRefreshLayout有更豐富的功能,建議沒了解過的童鞋去看看云石。
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.adminstrator.guaguakaapplication.WaterFallActivity">
<com.scwang.smartrefresh.layout.SmartRefreshLayout
android:id="@+id/refreshlayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!--下拉刷新用的Header-->
<RelativeLayout
android:id="@+id/rl_header_refresh"
android:layout_width="match_parent"
android:layout_height="60dp"
>
<ProgressBar
android:id="@+id/progress_loading_dialog"
android:layout_width="30dp"
android:layout_height="30dp"
android:background="@null"
android:indeterminateDrawable="@drawable/loading_anim"
android:indeterminateBehavior="repeat"
android:layout_centerInParent="true"
/>
</RelativeLayout>
<android.support.v7.widget.RecyclerView
android:id="@+id/rv_waterfall"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="10dp"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:layout_marginTop="10dp" />
<!--上拉加載用的Footer-->
<RelativeLayout
android:id="@+id/rl_footer_refresh"
android:layout_width="match_parent"
android:layout_height="60dp"
>
<ProgressBar
android:id="@+id/progress_loading_dialog2"
android:layout_width="30dp"
android:layout_height="30dp"
android:background="@null"
android:indeterminateDrawable="@drawable/loading_anim"
android:indeterminateBehavior="repeat"
android:layout_centerInParent="true"
/>
</RelativeLayout>
</com.scwang.smartrefresh.layout.SmartRefreshLayout>
</android.support.constraint.ConstraintLayout>
loading_anim也貼出來
<?xml version="1.0" encoding="utf-8"?>
<animated-rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/loading"
android:fromDegrees="0.0"
android:pivotX="50.0%"
android:pivotY="50.0%"
android:toDegrees="360.0"
/>
接下來是DemoAdapter唉工,實現(xiàn)瀑布流高低錯落效果的關(guān)鍵就在里邊,需要在onBindViewHolder的時候去為item設(shè)定高度汹忠。
package com.adminstrator.guaguakaapplication;
import android.app.Activity;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.StaggeredGridLayoutManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import com.nostra13.universalimageloader.core.ImageLoader;
import java.util.ArrayList;
import java.util.Random;
/**
* Created by Administrator on 2019/7/31.
*/
public class DemoAdapter extends RecyclerView.Adapter<DemoAdapter.BaseViewHolder> {
private ArrayList<Integer> dataList = new ArrayList<>();
public void replaceAll(ArrayList<Integer> list) {
dataList.clear();
if (list != null && list.size() > 0) {
dataList.addAll(list);
}
notifyDataSetChanged();
}
/**
* 插入數(shù)據(jù)使用notifyItemInserted淋硝,如果要使用插入動畫雹熬,必須使用notifyItemInserted
* 才會有效果。即便不需要使用插入動畫谣膳,也建議使用notifyItemInserted方式添加數(shù)據(jù)竿报,
* 不然容易出現(xiàn)閃動和間距錯亂的問題
* */
public void addData(int position,ArrayList<Integer> list) {
dataList.addAll(position,list);
notifyItemInserted(position);
}
//移除數(shù)據(jù)使用notifyItemRemoved
public void removeData(int position) {
dataList.remove(position);
notifyItemRemoved(position);
}
@Override
public DemoAdapter.BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new OneViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_rv_water_fall, parent, false));
}
@Override
public void onBindViewHolder(DemoAdapter.BaseViewHolder holder, int position) {
holder.setData(dataList.get(position),position);
}
@Override
public int getItemCount() {
return dataList != null ? dataList.size() : 0;
}
public class BaseViewHolder extends RecyclerView.ViewHolder {
public BaseViewHolder(View itemView) {
super(itemView);
}
void setData(Object data,int position) {
}
}
private class OneViewHolder extends BaseViewHolder {
private ImageView ivImage;
public OneViewHolder(View view) {
super(view);
ivImage = (ImageView) view.findViewById(R.id.iv_item_water_fall);
}
@Override
void setData(Object data,int position) {
if (data != null) {
int id = (int) data;
ivImage.setImageResource(id);
//需要Item高度不同才能出現(xiàn)瀑布流的效果,此處簡單粗暴地設(shè)置一下高度
if (position % 2 == 0) {
ivImage.setLayoutParams(new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 250));
} else {
ivImage.setLayoutParams(new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 350));
}
}
}
}
}
以上已經(jīng)實現(xiàn)了瀑布流效果继谚,為了美觀一般情況下還需要在Item間設(shè)置間隔烈菌。也就上邊Activity里用到的StaggeredDividerItemDecoration。
package com.adminstrator.guaguakaapplication.widget;
import android.content.Context;
import android.graphics.Rect;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.StaggeredGridLayoutManager;
import android.util.TypedValue;
import android.view.View;
public class StaggeredDividerItemDecoration extends RecyclerView.ItemDecoration {
private Context context;
private float interval;
private int spanCount;
/**
* @param interval item的間距
* @param spanCount 列數(shù)
* */
public StaggeredDividerItemDecoration(Context context, float interval, int spanCount) {
this.context = context;
this.interval = interval;
this.spanCount = spanCount;
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
StaggeredGridLayoutManager.LayoutParams params = (StaggeredGridLayoutManager.LayoutParams) view.getLayoutParams();
// 獲取item在span中的下標(biāo)
int spanIndex = params.getSpanIndex();
int interval = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
this.interval, context.getResources().getDisplayMetrics());
// 中間間隔
/**
* 這個判斷適用于瀑布流只有兩列的情況花履,如果有多列僧界,那么再增加spanIndex % spanCount == 的判斷并做處理就好了
* 此處的left和right都為interval / 2的原因是為了讓左邊item和右邊item同寬
* */
if (spanIndex % spanCount == 0) {
outRect.right = interval / 2;
} else {
outRect.left = interval / 2;
}
// 下方間隔
outRect.bottom = interval;
}
}
我效果圖里的圓角圖片使用了SWImageView,如果有需要臭挽,在Gradle中引入
implementation 'com.angel:SWImageView:1.0.0'
我使用的SmartRefreshLayout版本
implementation 'com.scwang.smartrefresh:SmartRefreshLayout:1.1.0-alpha-32'
以上
謝謝大家的督促捂襟,我把項目放Github上了,地址:https://github.com/ChunmingWu/StudyProject/tree/scratchDemo