RecyclerView使用(三)——下拉刷新,上拉加载
2019-07-23 本文已影响0人
Sraindy
一、准备工作
1、item布局
2、footerView底部布局
3、通过Adapter
4、在acivity中使用
5、显示如下:
ezgif.com-resize.gif
二、代码实现——通用部分
1、item布局 item_recycler.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="80dp"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:gravity="center"
android:orientation="horizontal">
<TextView
android:id="@+id/tvName"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:textSize="16sp" />
<TextView
android:id="@+id/tvValue"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="10dp"
android:gravity="center_vertical"
android:textSize="16sp" />
</LinearLayout>
</LinearLayout>
2、footerview布局 layout_recycler_footer.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="70dp"
android:orientation="vertical"
android:padding="10dp">
<!-- 加载时显示的布局 -->
<LinearLayout
android:id="@+id/ll_footer_loading"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="horizontal"
android:visibility="gone">
<ProgressBar
android:id="@+id/pb_loading"
android:layout_width="34dp"
android:layout_height="34dp"
android:indeterminateDrawable="@drawable/progressbar_recycler_footer" />
<TextView
android:id="@+id/tv_loading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:text="正在加载..."
android:textColor="@color/color_recycler_footer_txt"
android:textSize="16sp" />
</LinearLayout>
<!--错误时显示的布局-->
<LinearLayout
android:id="@+id/ll_footer_error"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="true"
android:gravity="center"
android:visibility="gone"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="网络错误,点击重新加载"
android:textColor="@color/color_recycler_footer_txt"
android:textSize="16sp" />
</LinearLayout>
<!--全部加载完毕显示的布局-->
<LinearLayout
android:id="@+id/ll_footer_all_loaded"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:gravity="center"
android:orientation="horizontal"
android:visibility="gone">
<View
android:layout_width="0dp"
android:layout_height="1dp"
android:layout_weight="1"
android:alpha="0.5"
android:background="@color/color_recycler_footer_txt"
android:gravity="start" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="15dp"
android:text="全部加载完毕"
android:textColor="@color/color_recycler_footer_txt"
android:textSize="16sp" />
<View
android:layout_width="0dp"
android:layout_height="1dp"
android:layout_weight="1"
android:alpha="0.5"
android:background="@color/color_recycler_footer_txt"
android:gravity="end" />
</LinearLayout>
</LinearLayout>
3、progressbar的外观 drawable/progressbar_recycler_footer.xml
<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:fromDegrees="0"
android:pivotX="50%"
android:pivotY="50%"
android:toDegrees="720">
<shape
android:innerRadius="10dp"
android:shape="ring"
android:thickness="3dp"
android:useLevel="false">
<gradient
android:centerColor="@color/color_lib_main_center"
android:endColor="@color/color_lib_main_end"
android:startColor="@color/color_lib_main_start"
android:type="sweep" />
</shape>
</rotate>
4、values/colors.xml
<!-- jar库中主题颜色 中的中间色 用于渐变 -->
<color name="color_lib_main_start">#fff</color>
<color name="color_lib_main_center">#5d8afc</color>
<color name="color_lib_main_end">#5d8afc</color>
<!-- recycler footer 字体颜色 -->
<color name="color_recycler_footer_txt">#5d8afc</color>
5、BaseViewHolder
/**
* 基础ViewHolder
*/
public abstract class BaseViewHolder extends RecyclerView.ViewHolder {
public BaseViewHolder(View itemView) {
super(itemView);
}
}
6、FooterViewHolder
/**
* 底部ViewHolder
*/
public class FooterViewHolder extends RecyclerView.ViewHolder {
public LinearLayout mFooterLoading = null;
public LinearLayout mFooterError = null;
public LinearLayout mAllLoaded = null;
public FooterViewHolder(View itemView) {
super(itemView);
mFooterLoading = itemView.findViewById(R.id.ll_footer_loading);
mFooterError = itemView.findViewById(R.id.ll_footer_error);
mAllLoaded = itemView.findViewById(R.id.ll_footer_all_loaded);
}
}
7、 ABaseRecyclerAdapter
/**
* 带有底部的 基础RecyclerAdapter
*/
public abstract class ABaseRecyclerAdapter<T> extends RecyclerView.Adapter {
private Context mContext = null;
private List<T> mDataList = null; //数据源
/**
* item的布局
*/
private int itemLayoutId = 0;
/*** 普通的item */
private final int TYPE_ITEM = 0;
/*** 底部 */
private final int TYPE_FOOTER = 1;
private FooterViewHolder mFooterViewHolder = null;
private View mFooterView = null;
/**
* 加载状态
*/
private int loadState = 1;
private OnReloadClickListener mReloadClick = null;
/**
* 重新加载
*
* @param mReloadClick
*/
public void setmReloadClick(OnReloadClickListener mReloadClick) {
this.mReloadClick = mReloadClick;
}
/**
* 底部上拉加载的view有四种状态:
* <p>
* 初始时:隐藏(避免recyclerView的item不满一页时,上拉加载的view一直显示)
* <p>
* 第一次滑动后:显示(显示信息为正在加载)
* <p>
* 网络错误时:显示(显示信息为网络错误,点击重新加载)
* <p>
* 全部都已加载完时:显示(显示信息为已全部加载完毕)
*/
public static final int STATE_FIRST = 1; //初始状态
public static final int STATE_SHOW = 2; //展示正在加载
public static final int STATE_ERROR_NET = 3; //网络错误
public static final int STATE_ALL_LOADED = 4; //已经全部加载完毕
public ABaseRecyclerAdapter(Context mContext, List<T> mDataList, int itemLayoutId) {
this.mContext = mContext;
this.mDataList = mDataList;
this.itemLayoutId = itemLayoutId;
}
/**
* 获取总数
* 因为底部需要提示内容,所以在所有对象数量上再加1
*
* @return
*/
@Override
public int getItemCount() {
if (null == mDataList || mDataList.size() == 0) {
return 0;
} else {
return mDataList.size() + 1;
}
}
/**
* 获取每个Item中的viewType
* recycleView调用流程:getItemViewType先于onCreateViewHolder。
*
* @param position
* @return
*/
@Override
public int getItemViewType(int position) {
if (position + 1 == getItemCount()) {
return TYPE_FOOTER;
} else {
return TYPE_ITEM;
}
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
RecyclerView.ViewHolder viewHolderRes = null;
if (viewType == TYPE_FOOTER) { //底部footerView
if (null == mFooterViewHolder) {
mFooterView = getItemViewByLayoutId(parent, R.layout.layout_recycler_footer);
mFooterViewHolder = new FooterViewHolder(mFooterView);
viewHolderRes = mFooterViewHolder;
} else {
viewHolderRes = mFooterViewHolder;
}
} else if (viewType == TYPE_ITEM) { //普通Item
View view = getItemViewByLayoutId(parent, itemLayoutId);
viewHolderRes = getBaseViewHolder(view);
}
return viewHolderRes;
}
/**
* 根据layoutId获取view
*
* @param parent
* @param layoutId
* @return
*/
public View getItemViewByLayoutId(ViewGroup parent, int layoutId) {
return LayoutInflater.from(parent.getContext()).inflate(layoutId, parent, false);
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (holder instanceof BaseViewHolder) {
convertViewHolder((BaseViewHolder) holder, mDataList.get(position), position);
} else if (holder instanceof FooterViewHolder) {
FooterViewHolder tHolder = (FooterViewHolder) holder;
switch (loadState) {
case STATE_FIRST: { //初始状态
mFooterView.setVisibility(View.GONE);
tHolder.mFooterLoading.setVisibility(View.GONE);
tHolder.mFooterError.setVisibility(View.GONE);
tHolder.mAllLoaded.setVisibility(View.GONE);
break;
}
case STATE_SHOW: { //展示正在加载
mFooterView.setVisibility(View.VISIBLE);
tHolder.mFooterLoading.setVisibility(View.VISIBLE);
tHolder.mFooterError.setVisibility(View.GONE);
tHolder.mAllLoaded.setVisibility(View.GONE);
break;
}
case STATE_ERROR_NET: { //网络出错
mFooterView.setVisibility(View.VISIBLE);
tHolder.mFooterLoading.setVisibility(View.GONE);
tHolder.mFooterError.setVisibility(View.VISIBLE);
tHolder.mAllLoaded.setVisibility(View.GONE);
tHolder.mFooterError.setOnClickListener(v -> {
if (null != mReloadClick) {
mReloadClick.onClick();
}
});
break;
}
case STATE_ALL_LOADED: { //已经全部加载
mFooterView.setVisibility(View.VISIBLE);
tHolder.mFooterLoading.setVisibility(View.GONE);
tHolder.mFooterError.setVisibility(View.GONE);
tHolder.mAllLoaded.setVisibility(View.VISIBLE);
break;
}
default:
break;
}
}
}
protected abstract void convertViewHolder(BaseViewHolder holder, T data, int position);
public abstract BaseViewHolder getBaseViewHolder(View itemView);
/**
* 更新加载状态
* STATE_FIRST = 1; //初始状态
* STATE_SHOW = 2; //展示正在加载
* STATE_ERROR_NET = 3; //网络错误
* STATE_ALL_LOADED = 4; //已经全部加载完毕
*
* @param loadState
*/
public void setLoadState(int loadState) {
this.loadState = loadState;
notifyDataSetChanged();
}
/**
* 加载失败,重新加载
*/
interface OnReloadClickListener {
void onClick();
}
}
代码实现——具体使用
1、ItemRecyclerModel
public class ItemRecyclerModel {
private String mName = "";
private String mValue = "";
public String getmName() {
return mName;
}
public void setmName(String mName) {
this.mName = mName;
}
public String getmValue() {
return mValue;
}
public void setmValue(String mValue) {
this.mValue = mValue;
}
@Override
public String toString() {
return "ItemRecyclerModel{" +
"mName='" + mName + '\'' +
", mValue='" + mValue + '\'' +
'}';
}
}
2、ItemRecyclerViewAdapter
public class ItemRecyclerViewAdapter extends ABaseRecyclerAdapter<ItemRecyclerModel> {
public ItemRecyclerViewAdapter(Context mContext, List<ItemRecyclerModel> mDataList, int itemLayoutId) {
super(mContext, mDataList, itemLayoutId);
}
@Override
protected void convertViewHolder(BaseViewHolder holder, ItemRecyclerModel data, int position) {
ItemRecyclerViewHolder tHolder = (ItemRecyclerViewHolder) holder;
if (null != tHolder) {
tHolder.mTvName.setText(data.getmName());
tHolder.mTvValue.setText(data.getmValue());
}
}
@Override
public BaseViewHolder getBaseViewHolder(View itemView) {
return new ItemRecyclerViewHolder(itemView);
}
}
3、主布局 activity_recycler_test.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/btn_add_one"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="加一" />
<Button
android:id="@+id/btn_subtract_one"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:text="减一" />
</LinearLayout>
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/srl"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="@+id/rv"
android:layout_width="match_parent"
android:layout_height="match_parent">
</android.support.v7.widget.RecyclerView>
</android.support.v4.widget.SwipeRefreshLayout>
</LinearLayout>
4、RecyclerTestActivity
public class RecyclerTestActivity extends AppCompatActivity {
private Button btnAdd;
private Button btnSubtract;
private SwipeRefreshLayout mSwipeRefreshLayout;
private RecyclerView mRecyclerView;
private List<ItemRecyclerModel> mDataList = new ArrayList<>();
private ItemRecyclerViewAdapter mItemAdapter;
//避免item不满一页时,下拉刷新触发上拉加载事件
private boolean isUp = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_recycler_test);
initData();
initWidget();
}
private void initData() {
ItemRecyclerModel model = new ItemRecyclerModel();
model.setmName("第一个数");
model.setmValue("1");
mDataList.add(model);
}
private void initWidget() {
btnAdd = findViewById(R.id.btn_add_one);
btnSubtract = findViewById(R.id.btn_subtract_one);
btnAdd.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
addItem();
mItemAdapter.setLoadState(STATE_FIRST);
}
});
btnSubtract.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mItemAdapter.setLoadState(STATE_ALL_LOADED);
}
});
mSwipeRefreshLayout = findViewById(R.id.srl);
mRecyclerView = findViewById(R.id.rv);
mItemAdapter = new ItemRecyclerViewAdapter(this, mDataList, R.layout.item_recycler);
LinearLayoutManager llManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(llManager);
mRecyclerView.setAdapter(mItemAdapter);
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
private int lastVisibleItemPosition = 0;
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if ((newState == SCROLL_STATE_IDLE) && (lastVisibleItemPosition + 1 == mItemAdapter.getItemCount()) && isUp) {
Log.i("zss", "到达底部,可再次加载更多数据");
mItemAdapter.setLoadState(STATE_SHOW);
}
}
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
lastVisibleItemPosition = ((LinearLayoutManager) recyclerView.getLayoutManager()).findLastVisibleItemPosition();
}
});
// 监听RecyclerView的滑动事件,判断是否是手指向上滑动,避免item不满一页时,下拉刷新触发上拉加载事件。
mRecyclerView.setOnTouchListener(new View.OnTouchListener() {
float oldy;
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
oldy = event.getY();
break;
case MotionEvent.ACTION_UP:
if (oldy - event.getY() > ViewConfiguration.get(RecyclerTestActivity.this).getScaledTouchSlop()) {
isUp = true;
} else {
isUp = false;
}
}
return false;
}
});
}
private void addItem() {
ItemRecyclerModel model = new ItemRecyclerModel();
model.setmName("第 " + (mDataList.size() + 1) + " 个数");
model.setmValue((mDataList.size() + 1) + "");
mDataList.add(model);
}
}