前言
在很早以前在本人的博客 https://www.cnblogs.com/guanxinjing/p/10299889.html 提供了用RecyclerView实现上拉加载数据的思维,但是用这个方式实现后,每一个上拉加载页面几乎都需要重写大量重复代码。所以进行了重新的封装,以方便使用并且减少重复代码量。如果刚点击这个博客建议看看之前的博客,之前的博客实现思路比较,清晰讲解部分也比较多。
封装后的RecyclerView
使用时请注意,这里以内部类的形式封装了适配器,请使用这里封装的适配器配合一起使用。
package net.wt.gate.ui.widget; import android.content.Context; import android.graphics.Color; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.view.Gravity; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; import android.view.ViewGroup; import android.widget.TextView; import androidx.annotation.ColorInt; import androidx.annotation.IntDef; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.StringRes; import androidx.recyclerview.widget.RecyclerView; import java.lang.annotation.Retention; import java.lang.annotation.Target; import static java.lang.annotation.ElementType.PARAMETER; import static java.lang.annotation.RetentionPolicy.SOURCE; public class UpLoadingRecyclerView extends RecyclerView { private boolean isScroll; private float downY; private int mHeightPixels; private OnUpScrollListener mOnUpScrollListener; private OnUpPullLoadListener mOnUpPullLoadListener; public UpLoadingRecyclerView(@NonNull Context context) { super(context); DisplayMetrics displayMetrics = getResources().getDisplayMetrics(); mHeightPixels = displayMetrics.heightPixels; } public UpLoadingRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs) { super(context, attrs); DisplayMetrics displayMetrics = getResources().getDisplayMetrics(); mHeightPixels = displayMetrics.heightPixels; } public UpLoadingRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); DisplayMetrics displayMetrics = getResources().getDisplayMetrics(); mHeightPixels = displayMetrics.heightPixels; } @Override public boolean dispatchTouchEvent(MotionEvent ev) { return super.dispatchTouchEvent(ev); } /** * 重写拦截的目的是只拦截移动事件,不拦截其他触摸事件 * * @param e * @return */ @Override public boolean onInterceptTouchEvent(MotionEvent e) { super.onInterceptTouchEvent(e);//一定要super.onInterceptTouchEvent(e);不要让RecyclerView的其他事件分发受到影响,否则会出现不滚动的问题 switch (e.getAction()) { case MotionEvent.ACTION_DOWN: isScroll = false;//按下不拦截 downY = e.getY(); break; case MotionEvent.ACTION_MOVE: if (Math.abs(downY - e.getY()) >= ViewConfiguration.get( getContext()).getScaledTouchSlop()) {//判断是否产生了最小滑动 isScroll = true;//移动了拦截触摸事件 } else { isScroll = false; } break; case MotionEvent.ACTION_UP: isScroll = false;//松开不拦截 break; } return isScroll; } @Override public boolean onTouchEvent(MotionEvent e) { switch (e.getAction()) { case MotionEvent.ACTION_DOWN: return true; case MotionEvent.ACTION_MOVE: break; case MotionEvent.ACTION_UP: if (downY - e.getY() > mHeightPixels / 5) {//判断是不是从下往上滑动了屏幕的三分之一 if (mOnUpScrollListener != null) { mOnUpScrollListener.onScroll(); } if (getAdapter() != null && mOnUpPullLoadListener != null && getAdapter() instanceof Adapter) { UpLoadingRecyclerView.Adapter adapter = (UpLoadingRecyclerView.Adapter) getAdapter(); if (adapter.isFooterPullUpLoading() && adapter.isFooterItemPosition()) { mOnUpPullLoadListener.OnUpPullLoad(); } } } break; } return super.onTouchEvent(e); } public void setOnUpScrollListener(OnUpScrollListener listener) { this.mOnUpScrollListener = listener; } public void setOnUpPullLoadListener(OnUpPullLoadListener onUpPullLoadListener) { this.mOnUpPullLoadListener = onUpPullLoadListener; } public interface OnUpScrollListener { void onScroll(); } public interface OnUpPullLoadListener { void OnUpPullLoad(); } public static abstract class Adapter<VH extends RecyclerView.ViewHolder> extends RecyclerView.Adapter { private static final int FOOTER_VIEW = -101; private int currentFooterStatus = FooterStatus.FOOTER_NO_MORE_DATA; //当前页角item状态 private int scrollPosition; //滚动位置 public abstract RecyclerView.ViewHolder onCreateItemViewHolder(@NonNull ViewGroup parent, int viewType); public abstract RecyclerView.ViewHolder onCreateFooterViewHolder(@NonNull ViewGroup parent); public abstract void onBindItemViewHolder(@NonNull RecyclerView.ViewHolder holder, int position); public abstract void onBindFooterViewHolder(@NonNull RecyclerView.ViewHolder holder, @FooterStatus int currentFooterStatus); public abstract int getItemViewCount(); @NonNull @Override public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { if (viewType != FOOTER_VIEW) { return onCreateItemViewHolder(parent, viewType); } return onCreateFooterViewHolder(parent); } @Override public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { scrollPosition = position; if (getItemViewType(position) != FOOTER_VIEW) { onBindItemViewHolder(holder, position); return; } onBindFooterViewHolder(holder, currentFooterStatus); } /** * 是否是最后位置 * * @return */ public boolean isFooterItemPosition() { return scrollPosition == getItemCount() - 1; } @Override public int getItemViewType(int position) { if (getItemCount() != 0 && position == getItemCount() - 1) { return FOOTER_VIEW; } return super.getItemViewType(position); } @Override public int getItemCount() { return getItemViewCount() == 0 ? 0 : getItemViewCount() + 1; } /** * 修改页脚状态为加载中 */ public void setFooterLoading() { currentFooterStatus = FooterStatus.FOOTER_LOADING; notifyDataSetChanged(); } /** * 修改页脚状态为网络异常 */ public void setFooterNetWorkError() { currentFooterStatus = FooterStatus.FOOTER_ERROR; notifyDataSetChanged(); } /** * 修改页脚状态为没有更多数据 */ public void setFooterNoMoreData() { currentFooterStatus = FooterStatus.FOOTER_NO_MORE_DATA; notifyDataSetChanged(); } /** * 修改页脚状态为上拉加载数据 */ public void setFooterPullUpLoading() { currentFooterStatus = FooterStatus.FOOTER_PULL_UP_LOADING; notifyDataSetChanged(); } public boolean isFooterLoading() { return currentFooterStatus == FooterStatus.FOOTER_LOADING; } public boolean isFooterNetWorkError() { return currentFooterStatus == FooterStatus.FOOTER_ERROR; } public boolean isFooterNoMoreData() { return currentFooterStatus == FooterStatus.FOOTER_NO_MORE_DATA; } public boolean isFooterPullUpLoading() { return currentFooterStatus == FooterStatus.FOOTER_PULL_UP_LOADING; } /** * 创建使用简单页尾 * @param parent * @param viewType * @param textSize * @param textColor * @return */ public RecyclerView.ViewHolder createSimpleFooterView(@NonNull ViewGroup parent, int viewType, float textSize, @ColorInt int textColor) { if (viewType == Adapter.FOOTER_VIEW) { TextView textView = new TextView(parent.getContext()); textView.setTextSize(dip2px(parent.getContext(), 14)); textView.setTextColor(Color.parseColor("#999999")); textView.setGravity(Gravity.CENTER); ViewGroup.LayoutParams layoutParams = textView.getLayoutParams(); layoutParams.height = dip2px(parent.getContext(), 30); layoutParams.width = LayoutParams.MATCH_PARENT; textView.setLayoutParams(layoutParams); return new SimpleFooterViewHolder(textView); } return null; } /** * 绑定简单页尾的ViewHolder * @param holder * @param position * @param pullUpLoadingText * @param loadingText * @param errorText * @param notMoreDataText */ public void bindSimpleFooterViewHolder(@NonNull RecyclerView.ViewHolder holder, int position, @StringRes int pullUpLoadingText, @StringRes int loadingText, @StringRes int errorText, @StringRes int notMoreDataText) { if (holder instanceof SimpleFooterViewHolder) { switch (currentFooterStatus) { case FooterStatus.FOOTER_PULL_UP_LOADING: ((SimpleFooterViewHolder) holder).footerTextView.setText(pullUpLoadingText); break; case FooterStatus.FOOTER_LOADING: ((SimpleFooterViewHolder) holder).footerTextView.setText(loadingText); break; case FooterStatus.FOOTER_ERROR: ((SimpleFooterViewHolder) holder).footerTextView.setText(errorText); break; case FooterStatus.FOOTER_NO_MORE_DATA: ((SimpleFooterViewHolder) holder).footerTextView.setText(notMoreDataText); break; default: break; } } } public static class SimpleFooterViewHolder extends RecyclerView.ViewHolder { TextView footerTextView; public SimpleFooterViewHolder(@NonNull View itemView) { super(itemView); footerTextView = (TextView) itemView; } } } @Retention(SOURCE) @Target({PARAMETER}) @IntDef(value = {FooterStatus.FOOTER_PULL_UP_LOADING, FooterStatus.FOOTER_LOADING, FooterStatus.FOOTER_ERROR, FooterStatus.FOOTER_NO_MORE_DATA}) public @interface FooterStatus { int FOOTER_PULL_UP_LOADING = 0; //提示上拉加载更多 int FOOTER_LOADING = 1; //加载中 int FOOTER_ERROR = 2; //网络异常 int FOOTER_NO_MORE_DATA = 3; //没有更多内容 } /** * 根据手机分辨率从DP转成PX * * @param context * @param dpValue * @return */ private static int dip2px(Context context, float dpValue) { float scale = context.getResources().getDisplayMetrics().density; return (int) (dpValue * scale + 0.5f); } }