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 上拉加載更多及下拉刷新功能的實現方法,希望對大家有所幫助,如果大家有任何疑問歡迎給我留言,小編會及時回復大家的,在此也非常感謝大家對武林網網址的支持!
新聞熱點
疑難解答