recyclerview 下拉刷新数据、滑动到最后一个item时加载更多。
只是为了实现功能,基本是对人家代码的修改。个人笔记。
借鉴了其它大神的
自定义RecyclerView实现下拉刷新和上拉加载
张鸿洋的
Android 优雅的为RecyclerView添加HeaderView和FooterView
compile 'com.android.support:recyclerview-v7:23.4.0'
recyclerView 有三种数据显示格式,这里遇到个问题,在第一、三模式下,item没有充满,header也实质上是item,网上的说的原因是:
View.inflate(mContext, R.layout.item_layout, null);
必须要换成下面的构造方式把parent带进去:
LayoutInflater.from(mContext).inflate(R.layout.item_layout, parent, false);
但我问题是:
recyclerView 的父控件是ConstraintLayout
//线性 类listview
recyclerview.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
//类 gridview
recyclerview.setLayoutManager(new GridLayoutManager(this,2));
//瀑布流显示
recyclerview.setLayoutManager(new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL));
这是我对于第一个借鉴的代码修改,处理了一些细节上的问题。
大体的思路:
1.onTouch 来控制下拉刷新,通过layoutManager来得到第一个显示的item,第一个的话下拉时对添加的header设置paddingTop,默认的paddingTop是 - header.getHeight,得到隐藏的效果。通过滑动的距离分三种状态,正在刷新、下拉中、放开刷新。
这样只有在手动滑动的时都会触发。
2.OnScrollListener来控制加载更多,也不用考虑惯性滑动。
3.这里layoutManager,LinearLayoutManager和GridLayoutManager与StaggeredGridLayoutManager是不同的在取显示item时。
4.这里header直接添加多个没有问题,Footer不行,可以再套一层。
public class RefreshRecyclerView extends RecyclerView {
private Context mContext;
// 顶部视图,下拉刷新控件
private LinearLayout headerView;
// 正在刷新状态的进度条
private ProgressBar pb_header_refresh;
// 刷新箭头
private ImageView iv_header_refresh;
// 显示刷新状态
private TextView tv_status;
// 显示最近一次的刷新时间
private TextView tv_time;
// 转到下拉刷新状态时的动画
private RotateAnimation downAnima;
// 转到释放刷新状态时的动画
private RotateAnimation upAnima;
// 尾部视图
private View footerView;
// 尾部(上拉加载控件)的高度
private int footerViewHeight;
//触摸事件中按下的Y坐标,初始值为-1,为防止ACTION_DOWN事件被抢占
private float startY = -1;
// 下拉刷新控件的高度
private int pulldownHeight;
// 刷新状态:下拉刷新
private final int PULL_DOWN_REFRESH = 0;
// 刷新状态:释放刷新
private final int RELEASE_REFRESH = 1;
// 刷新状态:正常刷新
private final int REFRESHING = 2;
// 当前头布局的状态-默认为下拉刷新
private int currState = PULL_DOWN_REFRESH;
// 刷新事件的回调
private RefreshRecyclerView.OnRefreshListener mOnRefreshListener;
// 判断是否是加载更多
private boolean isLoadingMore;
// StaggeredGridLayoutManager
private int[] lastPositions;
//是否需要刷新
private boolean isNeedLoadingMore = true;
private boolean isNeedRefresh = true;
public RefreshRecyclerView(Context context) {
this(context, null);
}
public RefreshRecyclerView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public RefreshRecyclerView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mContext = context;
initHeaderView();
initFooterView();
}
/**
* 是否加载更多
*
* @param enable
*/
public void setPullLoadEnable(boolean enable) {
isNeedLoadingMore = enable;
}
/**
* 是否刷新
*
* @param enable
*/
public void setPullRefreshEnable(boolean enable) {
isNeedRefresh = enable;
}
/**
* 返回尾部布局,供外部调用
*
* @return
*/
public View getFooterView() {
return footerView;
}
/**
* 返回头部布局,供外部调用
*
* @return
*/
public View getHeaderView() {
return headerView;
}
/**
* 通过HeaderAndFooterWrapper对象给RecyclerView添加尾部
*
* @param footerView 尾部视图
* @param headerAndFooterWrapper RecyclerView.Adapter的包装类对象,通过它给RecyclerView添加尾部视图
*/
public void addFooterView(View footerView, RecyHeaderAdapter headerAndFooterWrapper) {
headerAndFooterWrapper.addFooterView(footerView);
}
/**
* 通过HeaderAndFooterWrapper对象给RecyclerView添加头部部
*
* @param headerView 尾部视图
* @param headerAndFooterWrapper RecyclerView.Adapter的包装类对象,通过它给RecyclerView添加头部视图
*/
public void addHeaderView(View headerView, RecyHeaderAdapter headerAndFooterWrapper) {
headerAndFooterWrapper.addHeaderView(headerView);
}
private void initHeaderView() {
headerView = (LinearLayout) View.inflate(mContext, R.layout.layout_recyclerview_header, null);
tv_time = (TextView) headerView.findViewById(R.id.tv_time);
tv_status = (TextView) headerView.findViewById(R.id.tv_status);
iv_header_refresh = (ImageView) headerView.findViewById(R.id.iv_header_refresh);
pb_header_refresh = (ProgressBar) headerView.findViewById(R.id.pb_header_refresh);
headerView.measure(0, 0);
pulldownHeight = headerView.getMeasuredHeight();
headerView.setPadding(0, -pulldownHeight, 0, 0);
//初始化头部布局的动画
initAnimation();
}
/**
* 刷新状态改变时的动画
*/
private void initAnimation() {
// 从下拉刷新状态转换为释放刷新状态
upAnima = new RotateAnimation(0, -180, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
upAnima.setFillAfter(true);
upAnima.setDuration(500);
// 转到下拉刷新的动画
downAnima = new RotateAnimation(-180, -360, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
downAnima.setFillAfter(true);
downAnima.setDuration(500);
}
private void initFooterView() {
footerView = View.inflate(mContext, R.layout.layout_recyclerview_footer, null);
footerView.measure(0, 0);
//得到控件的高
footerViewHeight = footerView.getMeasuredHeight();
//默认隐藏下拉刷新控件
// View.setPadding(0,-控件高,0,0);//完全隐藏
//View.setPadding(0, 0,0,0);//完全显示
footerView.setPadding(0, -footerViewHeight, 0, 0);
// addFooterView(footerView);
// 自己监听自己
this.addOnScrollListener(new MyOnScrollListener());
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
if (!isNeedRefresh) {//不用刷新直接return
return super.onTouchEvent(ev);
}
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN://按下
startY = ev.getY();
// Log.d("ACTION_DOWN.........Y", startY + "");
break;
case MotionEvent.ACTION_MOVE:
//防止ACTION_DOWN事件被抢占,没有执行
if (startY == -1) {
startY = ev.getY();
}
float endY = ev.getY();
float dY = endY - startY;
// Log.d("ACTION_MOVE.......Y", endY + "");
//判断当前是否正在刷新中
if (currState == REFRESHING) {
//如果当前是正在刷新,不执行下拉刷新了,直接break;
break;
}
LayoutManager layoutManager = getLayoutManager();
if (layoutManager instanceof LinearLayoutManager) {
LinearLayoutManager linearLayoutManager = (LinearLayoutManager) layoutManager;
int firstCompletelyVisibleItemPosition = linearLayoutManager.findFirstCompletelyVisibleItemPosition();
int firstVisibleItemPosition = linearLayoutManager.findFirstVisibleItemPosition();
Log.d("firstposition.Complete", firstCompletelyVisibleItemPosition + "....firstVisibleItemPosition..." + firstVisibleItemPosition);
if (firstVisibleItemPosition == 0) {
// 如果是下拉
if (dY > 0) {
int paddingTop = (int) (dY / 3 - pulldownHeight);
if (paddingTop > 0 && currState != RELEASE_REFRESH) {
//完全显示下拉刷新控件,进入松开刷新状态
currState = RELEASE_REFRESH;
refreshViewState();
} else if (paddingTop < 0 && currState != PULL_DOWN_REFRESH) {
//没有完全显示下拉刷新控件,进入下拉刷新状态
currState = PULL_DOWN_REFRESH;
refreshViewState();
}
headerView.setPadding(0, paddingTop, 0, 0);
}
}
} else if (layoutManager instanceof StaggeredGridLayoutManager) {
StaggeredGridLayoutManager staggeredGridLayoutManager = (StaggeredGridLayoutManager) layoutManager;
if (lastPositions == null) {
lastPositions = new int[staggeredGridLayoutManager.getSpanCount()];
}
int[] firstVisibleItemPositions = staggeredGridLayoutManager.findFirstVisibleItemPositions(lastPositions);
Log.d("firstposition.Complete", firstVisibleItemPositions[0] + "");
if (firstVisibleItemPositions[0] == 0) {
// 如果是下拉
if (dY > 0) {
int paddingTop = (int) (dY / 3 - pulldownHeight);
if (paddingTop > 0 && currState != RELEASE_REFRESH) {
//完全显示下拉刷新控件,进入松开刷新状态
currState = RELEASE_REFRESH;
refreshViewState();
} else if (paddingTop < 0 && currState != PULL_DOWN_REFRESH) {
//没有完全显示下拉刷新控件,进入下拉刷新状态
currState = PULL_DOWN_REFRESH;
refreshViewState();
}
headerView.setPadding(0, paddingTop, 0, 0);
}
}
}
break;
case MotionEvent.ACTION_UP:
//5.重新记录值
startY = -1;
if (currState == PULL_DOWN_REFRESH) {
//设置默认隐藏
headerView.setPadding(0, -pulldownHeight, 0, 0);
} else if (currState == RELEASE_REFRESH) {
//当前是释放刷新,进入到正在刷新状态,完全显示
currState = REFRESHING;
refreshViewState();
headerView.setPadding(0, 0, 0, 0);
//调用用户的回调事件,刷新页面数据
if (mOnRefreshListener != null) {
mOnRefreshListener.onPullDownRefresh();
}
}
break;
}
return super.onTouchEvent(ev);
}
/**
* 跳转刷新状态
*/
private void refreshViewState() {
switch (currState) {
// 跳转到下拉刷新
case PULL_DOWN_REFRESH:
iv_header_refresh.startAnimation(downAnima);
tv_status.setText("下拉刷新");
break;
// 跳转到释放刷新
case RELEASE_REFRESH:
iv_header_refresh.startAnimation(upAnima);
tv_status.setText("释放刷新");
break;
// 跳转到正在刷新
case REFRESHING:
iv_header_refresh.clearAnimation();
iv_header_refresh.setVisibility(GONE);
pb_header_refresh.setVisibility(VISIBLE);
tv_status.setText("正在刷新中.....");
break;
}
}
public void setWrapperAdapter(RecyHeaderAdapter wrapperAdapter) {
addHeaderView(getHeaderView(), wrapperAdapter);
addFooterView(getFooterView(), wrapperAdapter);
super.setAdapter(wrapperAdapter);
}
/**
* 定义下拉刷新和上拉加载的接口
*/
public interface OnRefreshListener {
/**
* 当下拉刷新时触发此方法
*/
void onPullDownRefresh();
/**
* 当加载更多的时候回调这个方法
*/
void onLoadingMore();
}
public void setOnRefreshListener(RefreshRecyclerView.OnRefreshListener listener) {
this.mOnRefreshListener = listener;
}
/**
* 当刷新完数据之后,调用此方法,把头文件隐藏,并且状态设置为初始状态
*
* @param isSuccess
*/
public void onFinishRefresh(boolean isSuccess) {
if (isLoadingMore) {
footerView.setPadding(0, -footerViewHeight, 0, 0);
isLoadingMore = false;
} else {
headerView.setPadding(0, -pulldownHeight, 0, 0);
currState = PULL_DOWN_REFRESH;
iv_header_refresh.setVisibility(VISIBLE);
pb_header_refresh.setVisibility(GONE);
tv_status.setText("下拉刷新");
if (isSuccess) {
//设置更新时间
tv_time.setText(getSystemTime());
}
}
}
private String getSystemTime() {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return dateFormat.format(new Date());
}
/**
* recyclerView 滑动监听
* 滑动到最后一个时启动加载更多
*/
private class MyOnScrollListener extends OnScrollListener {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (currState == REFRESHING || isLoadingMore == true || !isNeedLoadingMore) {
//如果当前是正在刷新,或者加载更多,或者不用加载更多,不执行;
return;
}
if (newState == SCROLL_STATE_IDLE || newState == SCROLL_STATE_SETTLING) {
//判断是当前layoutManager是否为LinearLayoutManager
// 只有LinearLayoutManager才有查找第一个和最后一个可见view位置的方法
LayoutManager layoutManager = recyclerView.getLayoutManager();
if (layoutManager instanceof LinearLayoutManager) {
LinearLayoutManager linearLayoutManager = (LinearLayoutManager) layoutManager;
//当停止滚动时或者惯性滚动时,RecyclerView的最后一个显示的条目:getCount()-1
/// 注意是findLastVisibleItemPosition()而不是getLastVisiblePosition
if (linearLayoutManager.findLastVisibleItemPosition() >= getChildCount() - 1) {
isLoadingMore = true;
//把底部加载显示
footerView.setPadding(0, 0, 0, 0);
if (mOnRefreshListener != null) {
mOnRefreshListener.onLoadingMore();
}
}
} else if (layoutManager instanceof StaggeredGridLayoutManager) {
StaggeredGridLayoutManager staggeredGridLayoutManager = (StaggeredGridLayoutManager) layoutManager;
if (lastPositions == null) {
lastPositions = new int[staggeredGridLayoutManager.getSpanCount()];
}
int[] firstVisibleItemPositions = staggeredGridLayoutManager.findLastVisibleItemPositions(lastPositions);
Log.d("firstposition.Complete", firstVisibleItemPositions[0] + "");
//滑动到最后一个
if (firstVisibleItemPositions[0] >= getChildCount() - 1) {
isLoadingMore = true;
//把底部加载显示
footerView.setPadding(0, 0, 0, 0);
if (mOnRefreshListener != null) {
mOnRefreshListener.onLoadingMore();
}
}
}
}
}
}
adapter里注意的问题就是onAttachedToRecyclerView和onViewAttachedToWindow里去处理一下,如果当前的item是header的话,让其充满一行。
public class RecyHeaderAdapter extends RecyclerView.Adapter {
private static final int BASE_ITEM_TYPE_HEADER = 100000;
private static final int BASE_ITEM_TYPE_FOOTER = 200000;
private SparseArrayCompat mHeaderViews = new SparseArrayCompat<>();
private SparseArrayCompat mFootViews = new SparseArrayCompat<>();
private RecyclerView.Adapter mInnerAdapter;
public RecyHeaderAdapter(RecyclerView.Adapter mInnerAdapter) {
this.mInnerAdapter = mInnerAdapter;
}
private boolean isHeaderViewPos(int position) {
return position < getHeadersCount();
}
private boolean isFooterViewPos(int position) {
return position >= getHeadersCount() + getRealItemCount();
}
public void addHeaderView(View view) {
mHeaderViews.put(mHeaderViews.size() + BASE_ITEM_TYPE_HEADER, view);
}
public void addFooterView(View view) {
mFootViews.put(mFootViews.size() + BASE_ITEM_TYPE_FOOTER, view);
}
public int getHeadersCount() {
return mHeaderViews.size();
}
public int getFootersCount() {
return mFootViews.size();
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (mHeaderViews.get(viewType) != null) {
ViewHolder holder = ViewHolder.createViewHolder(parent.getContext(), mHeaderViews.get(viewType));
return holder;
} else if (mFootViews.get(viewType) != null) {
ViewHolder holder = ViewHolder.createViewHolder(parent.getContext(), mFootViews.get(viewType));
return holder;
}
return mInnerAdapter.onCreateViewHolder(parent, viewType);
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (isHeaderViewPos(position)) {
return;
}
if (isFooterViewPos(position)) {
return;
}
mInnerAdapter.onBindViewHolder(holder, position - getHeadersCount());
}
@Override
public int getItemViewType(int position) {
if (isHeaderViewPos(position)) {
return mHeaderViews.keyAt(position);
} else if (isFooterViewPos(position)) {
return mFootViews.keyAt(position - getHeadersCount() - getRealItemCount());
}
return mInnerAdapter.getItemViewType(position - getHeadersCount());
}
public int getRealItemCount() {
if (mInnerAdapter == null) return 0;
return mInnerAdapter.getItemCount();
}
@Override
public int getItemCount() {
return getHeadersCount() + getFootersCount() + getRealItemCount();
}
@Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {//适配GridLayout 的header
WrapperUtils.onAttachedToRecyclerView(mInnerAdapter, recyclerView, new WrapperUtils.SpanSizeCallback()
{
@Override
public int getSpanSize(GridLayoutManager layoutManager, GridLayoutManager.SpanSizeLookup oldLookup, int position)
{
int viewType = getItemViewType(position);
if (mHeaderViews.get(viewType) != null)
{
return layoutManager.getSpanCount();
} else if (mFootViews.get(viewType) != null)
{
return layoutManager.getSpanCount();
}
if (oldLookup != null)
return oldLookup.getSpanSize(position);
return 1;
}
});
// mInnerAdapter.onAttachedToRecyclerView(recyclerView);
// final RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
// if (layoutManager instanceof GridLayoutManager) {
//
// final GridLayoutManager gridLayoutManager = (GridLayoutManager) layoutManager;
// final GridLayoutManager.SpanSizeLookup spanSizeLookup = gridLayoutManager.getSpanSizeLookup();
//
// gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
// @Override
// public int getSpanSize(int position) {
// int viewType = getItemViewType(position);
// if (mHeaderViews.get(viewType) != null) {
// return gridLayoutManager.getSpanCount();
// } else if (mFootViews.get(viewType) != null) {
// return gridLayoutManager.getSpanCount();
// }
// if (spanSizeLookup != null)
// return spanSizeLookup.getSpanSize(position);
// return 1;
// }
// });
// gridLayoutManager.setSpanCount(gridLayoutManager.getSpanCount());
// }
}
@Override
public void onViewAttachedToWindow(RecyclerView.ViewHolder holder) {
mInnerAdapter.onViewAttachedToWindow(holder);
int position = holder.getLayoutPosition();
if (isHeaderViewPos(position) || isFooterViewPos(position))
{
WrapperUtils.setFullSpan(holder);
}
// mInnerAdapter.onViewAttachedToWindow(holder);
// int position = holder.getLayoutPosition();
// if (isHeaderViewPos(position) || isFooterViewPos(position)) {
// ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
//
// if (lp != null
// && lp instanceof StaggeredGridLayoutManager.LayoutParams) {
//
// StaggeredGridLayoutManager.LayoutParams p =
// (StaggeredGridLayoutManager.LayoutParams) lp;
//
// p.setFullSpan(true);
// }
// }
}
}