RecyclerView 已经出来很久了,但是在项目中之前都使用的是ListView,最近新的项目上了都大量的使用了RecycleView.尤其是瀑布流的下拉刷新,网上吧啦吧啦没有合适的自己总结了一哈。
先贴图上来看看:
使用RecyclerView实现上拉加载更多和下拉刷新的功能我自己有两种方式:
1.使用系统自带的Android.support.v4.widget.SwipeRefreshLayout这个控价来实现。
2.自定义的里面带有RecyleView的控件。
使用RecycleView很不好添加头部,之前在使用listview当中自己可以添加header和bootm,但是RecycleView好像不是那么的好操作。对于第一种使用系统自带的Android.support.v4.widget.SwipeRefreshLayout来实现的,也很好用,但是产品一般不要这种下拉刷新,为了让自己显得牛逼,他一般会搞一个自己的带有动画,这就比较扯淡了。。。所以就只能用方法2了。
大致说一哈方法2的实现方式,父布局为ViewGroup,里面添加View第一个为控件为header第二个控件为RecycleView,至于最底部的下拉加载更多试图,通过RecycleViw的Adapter来添加。
有了思路就搞起来:
package com.krain.srecyclerview.fruitview;import android.content.Context;import android.graphics.drawable.AnimationDrawable;import android.util.AttributeSet;import android.view.LayoutInflater;import android.view.View;import android.widget.FrameLayout;import android.widget.ImageView;import com.krain.srecyclerview.R;/*** Created by dafuShao on 2016/9/9 0009.**/public class ElizabethView extends FrameLayout {private ImageView imageView;private AnimationDrawable animationDrawable;public ElizabethView(Context context) {super(context);initview(context);}public ElizabethView(Context context, AttributeSet attrs) {super(context, attrs);initview(context);}public ElizabethView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);initview(context);}private void initview(Context context){View view= LayoutInflater.from(context).inflate(R.layout.elizabeth_item,null);imageView=(ImageView) view.findViewById(R.id.elizabeth_im);animationDrawable= (AnimationDrawable) imageView.getBackground();addView(view);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);}//开始动画public void startAnim(){animationDrawable.start();}//停止动画public void stopAnim(){animationDrawable.stop();}}
这是头部控价很简单里面有一个小的臭蛋眼睛左右挤一哈的效果就不贴图了。
下面是自定义的包含RecyclerView的控件代码如下:
package com.krain.srecyclerview.srecyclerview;import android.content.Context;import android.os.Handler;import android.os.Message;import android.support.v4.view.MotionEventCompat;import android.support.v7.widget.DefaultItemAnimator;import android.support.v7.widget.GridLayoutManager;import android.support.v7.widget.LinearLayoutManager;import android.support.v7.widget.RecyclerView;import android.support.v7.widget.StaggeredGridLayoutManager;import android.util.AttributeSet;import android.view.LayoutInflater;import android.view.MotionEvent;import android.view.View;import android.view.ViewGroup;import android.widget.LinearLayout;import android.widget.ProgressBar;import android.widget.Scroller;import android.widget.TextView;import com.krain.srecyclerview.R;import com.krain.srecyclerview.fruitview.ElizabethView;public class SRecyclerView extends ViewGroup {Context context;RecyclerView mRecyclerView;ElizabethView mHeaderView;TextView mFootViewTips;//footview的文字显示AdapterWrapper mAdapter;boolean mIsTop = true;//是否滑动到了最顶部RecyclerView.LayoutManager mLayoutManager;int mLastVisibleItem;int mFirstVisibleItem;OnRecyclerStatusChangeListener mRecyclerChangeListener;int mStatus;//当前状态int mHeadviewHeight;//headview的高度Scroller mScroller;int mFristScollerY;//最开始的getscrollyboolean mHasFooter;//是否有上拉加载的功能boolean mShowFootVisible;//是否显示FOOTERview在mHasFooter为true的时候生效boolean mHasRefresh = true;//是否支持下拉刷新private final int DEFAULT_MIN_PAGEINDEX = 1;//默认最小的页数int mMaxPage = DEFAULT_MIN_PAGEINDEX;//分页的总页数int mCurrentPage = DEFAULT_MIN_PAGEINDEX;//当前的页数,从1开始private final int STATUS_NORMAL = 0, STATUS_REFRESH = 1, STATUS_LOAD = 2;private final int MSG_LOAD_COMPLETE = 1, MSG_REFRESH_COMPLETE = 0;//handle的常量private final int DELAY_LOAD_COMPLETE = 1000, DELAY_REFRESH_COMPLETE = 1000;//加载完成延时回收的时间public SRecyclerView(Context context) {super(context);init(context);}public SRecyclerView(Context context, AttributeSet attrs) {super(context, attrs);init(context);}public SRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init(context);}/*** 设置最大页数** @param maxPage*/public void setMaxPage(int maxPage) {this.mMaxPage = maxPage;}/*** 是否支持上拉加载** @param hasLoadmore*/public void setLoadmore(boolean hasLoadmore) {mHasFooter = hasLoadmore;}public void setRecyclerViewLayoutManage(RecyclerView.LayoutManager mLayoutManage){this.mLayoutManager=mLayoutManage;}/*** 关闭下拉刷新功能*/public void disableRefresh() {mHasRefresh = false;}public void setAdapter(BaseRecyclerViewAdapter adapter) {int height = 0;if (mMaxPage == DEFAULT_MIN_PAGEINDEX) {mHasFooter = false;}mAdapter = new AdapterWrapper(context, adapter);mRecyclerView.setAdapter(mAdapter);}private int getViewHeight(View view) {int measure = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);view.measure(measure, measure);return view.getMeasuredHeight();}/*** 获取viewgroup里面的recyclerview** @return*/public RecyclerView getRecyclerView() {return mRecyclerView;}public void setOnRecyclerChangeListener(OnRecyclerStatusChangeListener listener) {mRecyclerChangeListener = listener;}/*** 设置RecyclerView添加、删除Item的动画** @param animator*/public void setItemAnimator(RecyclerView.ItemAnimator animator) {mRecyclerView.setItemAnimator(animator);}public void notifyDataSetChanged() {mStatus = STATUS_NORMAL;//重新set数据代表loadmore结束了,此时恢复成普通状态mAdapter.notifyDataSetChanged();}public void notifyDataInsert(int positionStart, int itemCount) {mStatus = STATUS_NORMAL;//重新set数据代表loadmore结束了,此时恢复成普通状态mAdapter.notifyItemRangeInserted(positionStart, itemCount);}public void notifyDataRemove(int position) {mStatus = STATUS_NORMAL;//重新set数据代表loadmore结束了,此时恢复成普通状态mAdapter.notifyItemRemoved(position);}/*** 初始化操作** @param context*/void init(Context context) {this.context = context;mScroller = new Scroller(context);// if (mLayoutManager!=null){// addChildView(context,mLayoutManager);// }else{// addChildView(context, new LinearLayoutManager(context));// }addChildView(context);}@Overridepublic void computeScroll() {if (mScroller.computeScrollOffset()) {scrollTo(mScroller.getCurrX(), mScroller.getCurrY());postInvalidate();}}/*** 增加子View** @param context*/void addChildView(Context context, RecyclerView.LayoutManager mLayoutManager) {ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);mHeaderView = new ElizabethView(context);mRecyclerView = new RecyclerView(context);addView(mHeaderView);addView(mRecyclerView);//mLayoutManager = new LinearLayoutManager(context);// mLayoutManager = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);mRecyclerView.setLayoutManager(mLayoutManager);// 设置Item增加、移除默认动画mRecyclerView.setItemAnimator(new DefaultItemAnimator());mRecyclerView.addOnScrollListener(onScrollListener);mRecyclerView.setHasFixedSize(true);mRecyclerView.setLayoutParams(params);}void addChildView(Context contex) {ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);mHeaderView = new ElizabethView(context);mRecyclerView = new RecyclerView(context);addView(mHeaderView);addView(mRecyclerView);//mLayoutManager = new LinearLayoutManager(context);mLayoutManager = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);mRecyclerView.setLayoutManager(mLayoutManager);// 设置Item增加、移除默认动画mRecyclerView.setItemAnimator(new DefaultItemAnimator());mRecyclerView.addOnScrollListener(onScrollListener);mRecyclerView.setHasFixedSize(true);mRecyclerView.setLayoutParams(params);}/*** 屏蔽Recyclerview的触摸事件(下拉刷新的时候)*/float lastY;@Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) {final int action = MotionEventCompat.getActionMasked(ev);switch (action) {case MotionEvent.ACTION_DOWN:lastY = ev.getRawY();break;case MotionEvent.ACTION_UP:case MotionEvent.ACTION_CANCEL:return false;case MotionEvent.ACTION_MOVE:if (mHasRefresh && mIsTop && ev.getRawY() > lastY)return true;break;}return false;}float offsetY = 0;@Overridepublic boolean onTouchEvent(MotionEvent event) {switch (event.getAction()) {case MotionEvent.ACTION_MOVE:offsetY = Math.abs(event.getRawY() - lastY); //Y差值绝对值if (offsetY > 0)scrollToOffset(offsetY);else {mIsTop = false;}break;case MotionEvent.ACTION_UP:if (getScrollY() <= 0) {doRefresh();mHeaderView.stopAnim();mHeaderView.startAnim();} else complete();break;}return super.onTouchEvent(event);}/*** 滚动这个view到手滑动的位置** @param offsetY Y轴偏移量*/void scrollToOffset(float offsetY) {//假如正在刷新并且现在的scrolly和初始值一样的时候,代表准备下拉开始刷新,并执行一次onlyif (getScrollY() == mFristScollerY && mRecyclerChangeListener != null)mRecyclerChangeListener.startRefresh();int value = Math.round(offsetY / 2.0F);value = mFristScollerY - value;scrollTo(0, value);}/*** 执行刷新操作,移动到header刚出来的位置*/void doRefresh() {mStatus = STATUS_REFRESH;int currentY = getScrollY();mScroller.startScroll(0, currentY, 0, (mFristScollerY - mHeadviewHeight) - currentY);invalidate();if (mRecyclerChangeListener != null) mRecyclerChangeListener.onRefresh();handler.sendEmptyMessageDelayed(MSG_REFRESH_COMPLETE, DELAY_REFRESH_COMPLETE);}Handler handler = new Handler() {@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);if (msg.what == MSG_LOAD_COMPLETE) {View footview = mAdapter.getFootView();if (footview != null)mRecyclerView.smoothScrollBy(0, -footview.getMeasuredHeight());} else if (msg.what == MSG_REFRESH_COMPLETE)complete();}};/*** header返回原处完全隐藏*/public void complete() {mCurrentPage = DEFAULT_MIN_PAGEINDEX;//完成之后当前的page恢复默认值if (mFootViewTips != null)mFootViewTips.setText(context.getString(R.string.loading));//更改foot提示为正在加载中if (mRecyclerChangeListener != null) mRecyclerChangeListener.refreshComplete();mStatus = STATUS_NORMAL;int currentY = getScrollY();mScroller.startScroll(0, currentY, 0, mFristScollerY - currentY);mHeaderView.stopAnim();invalidate();}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {int width = MeasureSpec.getSize(widthMeasureSpec);int height = 0;for (int i = 0; i < getChildCount(); i++) {View child = getChildAt(i);measureChild(child, widthMeasureSpec, heightMeasureSpec);height += child.getMeasuredHeight();}setMeasuredDimension(width, height);}@Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) {int left = getPaddingLeft();int top = getPaddingTop();for (int i = 0; i < getChildCount(); i++) {View child = getChildAt(i);if (i == 0) {//当是header的时候居中显示int headerLeft = getMeasuredWidth() / 2 - child.getMeasuredWidth() / 2;child.layout(headerLeft, top, headerLeft + child.getMeasuredWidth(), top + child.getMeasuredHeight());} elsechild.layout(left, top, left + child.getMeasuredWidth(), top + child.getMeasuredHeight());top += child.getMeasuredHeight();}mHeadviewHeight = getPaddingTop() + mHeaderView.getMeasuredHeight();scrollTo(0, mHeadviewHeight);//移动到header下方以显示recyleviewmFristScollerY = getScrollY();}/*** RecyclerView的滑动监听事件*/RecyclerView.OnScrollListener onScrollListener = new RecyclerView.OnScrollListener() {@Overridepublic void onScrollStateChanged(RecyclerView recyclerView, int newState) {super.onScrollStateChanged(recyclerView, newState);if (newState == RecyclerView.SCROLL_STATE_IDLE) {//滑动到了顶部if (mFirstVisibleItem == 0) {mIsTop = true;} else {mIsTop = false;if (mStatus != STATUS_LOAD && mShowFootVisible && mLastVisibleItem + 1 == mAdapter.getItemCount()) {if (mCurrentPage == mMaxPage) {//当前页面是最后一页的时候mFootViewTips = (TextView) mAdapter.getFootView().findViewById(R.id.footer_tips);mFootViewTips.setText(context.getString(R.string.last_page_tips));handler.sendEmptyMessageDelayed(MSG_LOAD_COMPLETE, DELAY_LOAD_COMPLETE);} else {mStatus = STATUS_LOAD;if (mRecyclerChangeListener != null) {mRecyclerChangeListener.onLoadMore();mCurrentPage++;}}}}}}@Overridepublic void onScrolled(RecyclerView recyclerView, int dx, int dy) {super.onScrolled(recyclerView, dx, dy);if (mLayoutManager instanceof LinearLayoutManager) {mLastVisibleItem = ((LinearLayoutManager) mLayoutManager).findLastVisibleItemPosition();mFirstVisibleItem = ((LinearLayoutManager) mLayoutManager).findFirstCompletelyVisibleItemPosition();setFootviewVisible();} else if (mLayoutManager instanceof GridLayoutManager) {mLastVisibleItem = ((GridLayoutManager) mLayoutManager).findLastVisibleItemPosition();mFirstVisibleItem = ((GridLayoutManager) mLayoutManager).findFirstCompletelyVisibleItemPosition();setFootviewVisible();} else if (mLayoutManager instanceof StaggeredGridLayoutManager) {//因为StaggeredGridLayoutManager的特殊性可能导致最后显示的item存在多个,所以这里取到的是一个数组//得到这个数组后再取到数组中position值最大的那个就是最后显示的position值了int[] lastPositions = new int[((StaggeredGridLayoutManager) mLayoutManager).getSpanCount()];((StaggeredGridLayoutManager) mLayoutManager).findLastVisibleItemPositions(lastPositions);mLastVisibleItem = findMax(lastPositions);mFirstVisibleItem = ((StaggeredGridLayoutManager) mLayoutManager).findFirstVisibleItemPositions(lastPositions)[0];setFootviewVisible();}}};void setFootviewVisible() {//当设置了拥有上拉加载功能但是第一页的条目不足以盛满Recyclerview的时候隐藏footerif (mHasFooter && mFirstVisibleItem == 0) {/*** 这里加上一个mShowFootVisible在上拉加载功能启用的情况下生效,从来控制item数量不足铺满* recyclerview高度的时刻隐藏footview,在item数量超过view高度的情况下显示*/if (mLastVisibleItem + 1 == mAdapter.getItemCount()) {mShowFootVisible = false;} else mShowFootVisible = true;notifyDataSetChanged();}}private int findMax(int[] positions) {int max = positions[0];for (int value : positions) {if (value > max) {max = value;}}return max;}private class AdapterWrapper extends RecyclerView.Adapter {private static final int TYPE_ITEM = 0;private static final int TYPE_FOOTER = 1;private RecyclerView.Adapter mAdapter;private Context mContext;View footer;public AdapterWrapper(Context context, RecyclerView.Adapter wrappedAdapter) {this.mContext = context;this.mAdapter = wrappedAdapter;}@Overridepublic RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {RecyclerView.ViewHolder holder = null;switch (viewType) {case TYPE_ITEM:holder = mAdapter.onCreateViewHolder(parent, viewType);break;case TYPE_FOOTER:footer = LayoutInflater.from(mContext).inflate(R.layout.lib_recyle_footview, null);LinearLayout linearLayout= (LinearLayout) footer.findViewById(R.id.loading_layout);StaggeredGridLayoutManager.LayoutParams layoutParams =new StaggeredGridLayoutManager.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.WRAP_CONTENT);layoutParams.setFullSpan(true);linearLayout.setLayoutParams(layoutParams);holder = new FooterViewHolder(footer);break;}return holder;}public View getFootView() {return footer;}@Overridepublic void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {if (!mHasFooter || position + 1 != getItemCount()) {mAdapter.onBindViewHolder(holder, position);}}@Overridepublic int getItemCount() {return mShowFootVisible ? mAdapter.getItemCount() + 1 : mAdapter.getItemCount();}@Overridepublic int getItemViewType(int position) {if (mShowFootVisible && position + 1 == getItemCount()) {return TYPE_FOOTER;} else {return TYPE_ITEM;}}private class FooterViewHolder extends RecyclerView.ViewHolder {public ProgressBar progressBar;public TextView tvLoading;public LinearLayout llyLoading;public FooterViewHolder(View itemView) {super(itemView);progressBar = (ProgressBar) itemView.findViewById(R.id.progress_loading);tvLoading = (TextView) itemView.findViewById(R.id.footer_tips);llyLoading = (LinearLayout) itemView.findViewById(R.id.loading_layout);}}}}
最后还有一个就是Adapter的代码:
package com.krain.srecyclerview.srecyclerview;import android.support.v7.widget.RecyclerView;import android.view.View;import android.view.ViewGroup;public abstract class BaseRecyclerViewAdapter<VH extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<VH> {private OnItemClickLisener mItemListener;@Overridepublic VH onCreateViewHolder(ViewGroup parent, int viewType) {View view = getItemView(viewType, parent);VH vh = getViewHolder(view);view.setOnClickListener(new OnRecyclerAdapterclickListener(vh, viewType));view.setOnLongClickListener(new OnRecyclerAdapterclickListener(vh, viewType));return vh;}public abstract VH getViewHolder(View itemView);/*** 返回item的view** @return*/public abstract View getItemView(int viewType, ViewGroup parent);/*** 返回Adapter每个itemn的数据 可选*/public Object getItem(int position) {return null;}/*** item点击事件接口** @param mItemListener*/public void setOnItemListener(OnItemClickLisener mItemListener) {this.mItemListener = mItemListener;}@Overridepublic abstract void onBindViewHolder(VH holder, int position);@Overridepublic abstract int getItemCount();class OnRecyclerAdapterclickListener implements View.OnClickListener, View.OnLongClickListener {VH viewholder;int viewType;public OnRecyclerAdapterclickListener(VH viewholder, int viewType) {this.viewholder = viewholder;this.viewType = viewType;}@Overridepublic void onClick(View v) {if (mItemListener != null && viewholder.getAdapterPosition() != RecyclerView.NO_POSITION) {mItemListener.onItemClick(viewholder.getAdapterPosition(), viewType, viewholder, v);}}@Overridepublic boolean onLongClick(View v) {if (mItemListener != null && viewholder.getAdapterPosition() != RecyclerView.NO_POSITION) {mItemListener.onItemLongClick(viewholder.getAdapterPosition(), viewType, viewholder, v);}return false;}}}
需要注意一哈:
如果想改变Reciview的布局方式在这个修改
void addChildView(Context contex) {ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);mHeaderView = new ElizabethView(context);mRecyclerView = new RecyclerView(context);addView(mHeaderView);addView(mRecyclerView);//mLayoutManager = new LinearLayoutManager(context);mLayoutManager = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);mRecyclerView.setLayoutManager(mLayoutManager);// 设置Item增加、移除默认动画mRecyclerView.setItemAnimator(new DefaultItemAnimator());mRecyclerView.addOnScrollListener(onScrollListener);mRecyclerView.setHasFixedSize(true);mRecyclerView.setLayoutParams(params);}
mLayoutManager = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);这一行就是了改成对应的就可以了,这儿没有封装过几天封装一个出来。
还需要注意一哈就是在滑动判断是不是最后一行,如果是瀑布流就要注意哈判断的方式和其他的不一样,
if (mLayoutManager instanceof StaggeredGridLayoutManager) {//因为StaggeredGridLayoutManager的特殊性可能导致最后显示的item存在多个,所以这里取到的是一个数组//得到这个数组后再取到数组中position值最大的那个就是最后显示的position值了int[] lastPositions = new int[((StaggeredGridLayoutManager) mLayoutManager).getSpanCount()];((StaggeredGridLayoutManager) mLayoutManager).findLastVisibleItemPositions(lastPositions);mLastVisibleItem = findMax(lastPositions);mFirstVisibleItem = ((StaggeredGridLayoutManager) mLayoutManager).findFirstVisibleItemPositions(lastPositions)[0];setFootviewVisible();}
因为StaggeredGridLayoutManager的特殊性可能导致最后显示的item存在多个,所以这里取到的是一个数组,得到这个数组后再取到数组中position值最大的那个就是最后显示的position值了再去设置最后一行加载更多的显示。
以上所述是小编给大家介绍的Android RecyclerView 上拉加载更多及下拉刷新功能的实现方法,希望对大家有所帮助,如果大家有任何疑问欢迎给我留言,小编会及时回复大家的,在此也非常感谢大家对武林网网址的支持!
新闻热点
疑难解答