前言
在很早以前在本人的博客 https://www.cnblogs.com/guanxinjing/p/10299889.html 提供了用RecyclerView实现上拉加载数据的思维,但是用这个方式实现后,每一个上拉加载页面几乎都需要重写大量重复代码。所以进行了重新的封装,以方便使用并且减少重复代码量。如果刚点击这个博客建议看看之前的博客,之前的博客实现思路比较,清晰讲解部分也比较多。
封装后的RecyclerView
使用时请注意,这里以内部类的形式封装了适配器,请使用这里封装的适配器配合一起使用。
import android.content.Context;
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 onFilterTouchEventForSecurity(MotionEvent event) {
return super.onFilterTouchEventForSecurity(event);
}
@Override
public boolean onTouchEvent(MotionEvent e) {
switch (e.getAction()) {
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_UP:
if (downY - e.getY() > mHeightPixels / 5) {//判断是不是从下往上滑动了屏幕的5分之一
if (mOnUpScrollListener != null) {
mOnUpScrollListener.onScroll();
}
if (getAdapter() != null && mOnUpPullLoadListener != null && getAdapter() instanceof Adapter) {
Adapter adapter = (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 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 ViewHolder onCreateItemViewHolder(@NonNull ViewGroup parent, int viewType);
public abstract ViewHolder onCreateFooterViewHolder(@NonNull ViewGroup parent, int viewType);
public abstract void onBindItemViewHolder(@NonNull ViewHolder holder, int position);
public abstract void onBindFooterViewHolder(@NonNull ViewHolder holder, @FooterStatus int currentFooterStatus);
public abstract int getItemViewCount();
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
if (viewType != FOOTER_VIEW) {
return onCreateItemViewHolder(parent, viewType);
}
return onCreateFooterViewHolder(parent, viewType);
}
@Override
public void onBindViewHolder(@NonNull 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() - 5;
}
@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 SimpleFooterViewHolder createSimpleFooterView(@NonNull ViewGroup parent, int viewType, float textSize, @ColorInt int textColor) {
if (viewType == Adapter.FOOTER_VIEW) {
TextView textView = new TextView(parent.getContext());
if(textSize != 0){
textView.setTextSize(textSize);
}
if (textColor != 0){
textView.setTextColor(textColor);
}
textView.setGravity(Gravity.CENTER);
textView.post(new Runnable() {
@Override
public void run() {
ViewGroup.LayoutParams layoutParams = textView.getLayoutParams();
layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT;
textView.setLayoutParams(layoutParams);
}
});
return new SimpleFooterViewHolder(textView);
}
return null;
}
/**
* 绑定简单页尾的ViewHolder
*
* @param holder
* @param pullUpLoadingText 提示 上拉加载数据
* @param loadingText 提示 数据加载中
* @param errorText 提示 网络异常
* @param notMoreDataText 提示 没有更多数据了
*/
public void bindSimpleFooterViewHolder(@NonNull ViewHolder holder, @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;
}
}
}
/**
* 绑定简单页尾的ViewHolder
*
* @param holder
* @param pullUpLoadingText 提示 上拉加载数据
* @param loadingText 提示 数据加载中
* @param errorText 提示 网络异常
* @param notMoreDataText 提示 没有更多数据了
*/
public void bindSimpleFooterViewHolder(@NonNull ViewHolder holder, String pullUpLoadingText, String loadingText, String errorText, String 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 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; //没有更多内容
}
}
使用例子
Adpater
class HomeVideoAdapter extends UpLoadingRecyclerView.Adapter<RecyclerView.ViewHolder> {
private List<HomeVideoBean> mList = new ArrayList<>();
private int mSelected = 0;
private SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm:ss", Locale.getDefault());
private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault());
private OnClickListener mListener;
public void refreshData(List<HomeVideoBean> list) {
mList.clear();
if (list != null) {
mList.addAll(list);
}
notifyDataSetChanged();
}
public void addData(List<HomeVideoBean> list) {
if (list != null) {
mList.addAll(list);
}
notifyDataSetChanged();
}
@Override
public RecyclerView.ViewHolder onCreateItemViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.ad_item_home_video, parent, false);
HomeVideoViewHolder viewHolder = new HomeVideoViewHolder(view);
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int position = viewHolder.getAdapterPosition();
if (position == RecyclerView.NO_POSITION) {
return;
}
mSelected = position;
notifyDataSetChanged();
if (mListener != null) {
mListener.onClickItem(mList.get(position));
}
}
});
return viewHolder;
}
@Override
public RecyclerView.ViewHolder onCreateFooterViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.ad_ad_layout_pull_up_refresh, parent, false);
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mListener != null) {
mListener.onClickFooter();
}
}
});
return new FooterViewHolder(view);
}
@Override
public void onBindItemViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
HomeVideoViewHolder viewHolder = (HomeVideoViewHolder) holder;
HomeVideoBean bean = mList.get(position);
if (mSelected == position) {
viewHolder.icon.setImageResource(R.drawable.ad_ic_video_selected);
} else {
viewHolder.icon.setImageResource(R.drawable.ad_ic_video_no_selected);
}
viewHolder.content.setText(bean.getContent());
long ts = 0;
try {
ts = dateFormat.parse(bean.getCreateTime()).getTime();
} catch (ParseException e) {
e.printStackTrace();
}
viewHolder.time.setText(timeFormat.format(ts));
viewHolder.videoImage.setImageURI(bean.getFileUrl() + "?vframe/jpg/offset/1");
}
@Override
public void onBindFooterViewHolder(@NonNull RecyclerView.ViewHolder holder, int currentFooterStatus) {
FooterViewHolder footViewHolder = (FooterViewHolder) holder;
switch (currentFooterStatus) {
case UpLoadingRecyclerView.FooterStatus.FOOTER_PULL_UP_LOADING:
footViewHolder.footerText.setText(R.string.ad_pull_up_load_more);
footViewHolder.footerProgress.setVisibility(View.GONE);
break;
case UpLoadingRecyclerView.FooterStatus.FOOTER_LOADING:
footViewHolder.footerText.setText(R.string.ad_loading_more_for_you);
footViewHolder.footerProgress.setVisibility(View.VISIBLE);
break;
case UpLoadingRecyclerView.FooterStatus.FOOTER_ERROR:
footViewHolder.footerText.setText(R.string.ad_network_anomaly_click_reload);
footViewHolder.footerProgress.setVisibility(View.GONE);
break;
case UpLoadingRecyclerView.FooterStatus.FOOTER_NO_MORE_DATA:
footViewHolder.footerText.setText(R.string.ad_Theres_nothing_more);
footViewHolder.footerProgress.setVisibility(View.GONE);
break;
}
}
@Override
public int getItemViewCount() {
return mList.size();
}
public void setListener(OnClickListener listener) {
this.mListener = listener;
}
public interface OnClickListener {
void onClickItem(HomeVideoBean bean);
void onClickFooter();
}
public static class HomeVideoViewHolder extends RecyclerView.ViewHolder {
ImageView icon;
TextView time;
TextView content;
SimpleDraweeView videoImage;
public HomeVideoViewHolder(@NonNull View itemView) {
super(itemView);
icon = itemView.findViewById(R.id.icon);
time = itemView.findViewById(R.id.time);
content = itemView.findViewById(R.id.content);
videoImage = itemView.findViewById(R.id.videoImage);
}
}
public static class FooterViewHolder extends RecyclerView.ViewHolder {
ProgressBar footerProgress;
TextView footerText;
public FooterViewHolder(@NonNull View itemView) {
super(itemView);
footerProgress = itemView.findViewById(R.id.footer_progress);
footerText = itemView.findViewById(R.id.footer_text);
}
}
}
activity的关键部分
//数据来源
mHomeVideoViewModel.getHomeVideoList().observe(this,new Observer<Result<List<HomeVideoBean>>>() {
@Override
public void onChanged (Result < List < HomeVideoBean >> result) {
if (result.getCode() != 200 && result.getCode() != 0) { //服务器加载数据异常
Toast.makeText(HomeVideoActivity.this, "" + result.getMsg(), Toast.LENGTH_SHORT).show();
if (mCurrentPage == 1) { //如果是第一页数据就直接隐藏整个列表
mRecyclerMessageVideo.setVisibility(View.GONE);
} else { //如果是其他页数据,就在页尾显示网络异常
mAdapter.setFooterNetWorkError();
}
return;
}
if (mCurrentPage == 1) { //如果页数等于1,那就是首次数据
if (result.getData() != null && !result.getData().isEmpty()) {
mAdapter.refreshData(result.getData()); //刷新数据
} else {
mRecyclerMessageVideo.setVisibility(View.GONE);
}
} else {
mAdapter.addData(result.getData()); //添加数据
}
if (result.getData().size() < 10) { //判断获取的数据熟练是否满足10个达到一页内容
mAdapter.setFooterNoMoreData(); //如果不满足,就显示没有更多数据了
} else {
mAdapter.setFooterPullUpLoading(); //如果满足,就显示上拉加载更多数据
}
}
});
mRecyclerMessageVideo.setOnUpPullLoadListener(new UpLoadingRecyclerView.OnUpPullLoadListener(){
@Override
public void OnUpPullLoad () { //上拉加载数据
mAdapter.setFooterLoading(); //将尾页显示为加载中
mCurrentPage++;
mHomeVideoViewModel.postHomeVideoList(mRequestTime, mDeviceSn, mCurrentPage, 10); //请求数据
}
});
mAdapter.setListener(new HomeVideoAdapter.OnClickListener(){
@Override
public void onClickItem (HomeVideoBean bean){
//item点击
}
@Override
public void onClickFooter () {
if (mAdapter.isFooterNetWorkError()) { //如果尾页显示网络异常就允许点击,重新加载数据
mHomeVideoViewModel.postHomeVideoList(mRequestTime, mDeviceSn, mCurrentPage, 10);
}
} });
End