[置顶] 完全自定义RecyclerView下拉刷新上拉加载
2017-08-10 17:08
633 查看
自从谷歌推出SwipeRefreshLayout之后越来越多的博客都是使用SwipeRefreshLayout来完成下拉刷新,但是往往产品经理根本不会使用谷歌的样式.每个公司都有一套自己的下拉样式这个时候就需要我们完全自定义RecyclerView的下拉刷新,基本查阅了网上所有的下拉刷新,但是效果都不怎么样.个人感觉我写的这个下拉刷新效果方面绝对的66666,欢迎可以提出一些改进意见:
废话不多说 先上效果图:
RecyclerView 出现以后,Android 里的下拉刷新和加载更多实现起来就非常容易了。当然,现成的库也有很多,只是总会有不一样的需求,而且我们往往只需要最基本的下拉刷新和加载更多功能,而不需要其他多余的功能。我只需要一个最纯粹的下拉刷新和加载更多。所以,自己动手显然是最好的结果了,也算是个小练习
在介绍代码之前我们先来讲一下实现原理:
首先,我们先通过重写RecyclerView的Adapter类装饰器为起实现addHeaderView,addFootView方法来添加头部与尾部。头部的实现是通过动态的修改的头部控件的高度,尾部的实现是通过动态修改它的BottomMargin,因为尾部的是默认显示的,使用Margin好实现,如果想实现下拉刷新和上拉加载功能,那么就必须有拉伸效果,所以就像上面的那样,Header是通过设置height,Footer是通过设置BottomMargin来模拟拉伸效果。那么回弹效果呢?仅仅通过设置高度或者是间隔是达不到模拟回弹效果的,因此,就需要用Scroller来实现模拟回弹效果。
本项目我把它分为主要的五部分HeaderAndFooterWrapper,PullRefreshRecyclerView,RecyclerViewFooter,RecyclerViewHeader,MessageRelativeLayout
下面我们分开来介绍。
HeaderAndFooterWrapper
这个类主要实现了RecyclerView的addHeaderView,addFootView方法,详细资料看参考弘洋大神的Android 优雅的为RecyclerView添加HeaderView和FooterView
以下是详细代码:
RecyclerViewHeader
RecyclerViewHeader继承自LinearLayout用来实现下拉刷新时的界面展示,可以分为三种状态:正常、准备刷新、正在加载。在添加布局文件的时候,指定高度为0,这是为了隐藏header,setState()是设置header的状态,因为header需要根据不同的状态,完成控件隐藏、显示、改变文字等操作,这个方法主要是在PullRefreshRecyclerView里面调用。除此之外,还有setVisiableHeight()和getVisiableHeight(),这两个方法是为了设置和获取Header中根布局文件的高度属性,从而完成拉伸和收缩的效果
说完了Header,我们再看看Footer。Footer是为了完成加载更多功能时候的界面展示,基本思路和Header是一样的,不过Footer的拉伸和显示效果不是通过高度来模拟的,而是通过设置BottomMargin来完成的。
MessageRelativeLayout
该类主要是为了实现在刷新完毕的时候可以显示一个更新了多少条,网络错误等等的提示语:在添加布局文件的时候,指定高度为0,这是为了隐藏提示语,实现思路与头布局类似,如不需要此功能可忽略,不会影响代码的使用
PullRefreshRecyclerView
在了解了Header,Footer和Message之后,我们就要介绍最核心的PullRefreshRecyclerView的代码实现了。便于观看,本代码有部分删减重构
最后重要的事说三遍:本项目扩展性极高,建议下载源码观看可更清晰的了解此项目的结构
源码地址:http://download.csdn.net/download/q714093365/9928217
转载请注明出处:http://blog.csdn.net/q714093365/article/details/77063084
废话不多说 先上效果图:
RecyclerView 出现以后,Android 里的下拉刷新和加载更多实现起来就非常容易了。当然,现成的库也有很多,只是总会有不一样的需求,而且我们往往只需要最基本的下拉刷新和加载更多功能,而不需要其他多余的功能。我只需要一个最纯粹的下拉刷新和加载更多。所以,自己动手显然是最好的结果了,也算是个小练习
在介绍代码之前我们先来讲一下实现原理:
首先,我们先通过重写RecyclerView的Adapter类装饰器为起实现addHeaderView,addFootView方法来添加头部与尾部。头部的实现是通过动态的修改的头部控件的高度,尾部的实现是通过动态修改它的BottomMargin,因为尾部的是默认显示的,使用Margin好实现,如果想实现下拉刷新和上拉加载功能,那么就必须有拉伸效果,所以就像上面的那样,Header是通过设置height,Footer是通过设置BottomMargin来模拟拉伸效果。那么回弹效果呢?仅仅通过设置高度或者是间隔是达不到模拟回弹效果的,因此,就需要用Scroller来实现模拟回弹效果。
本项目我把它分为主要的五部分HeaderAndFooterWrapper,PullRefreshRecyclerView,RecyclerViewFooter,RecyclerViewHeader,MessageRelativeLayout
下面我们分开来介绍。
HeaderAndFooterWrapper
这个类主要实现了RecyclerView的addHeaderView,addFootView方法,详细资料看参考弘洋大神的Android 优雅的为RecyclerView添加HeaderView和FooterView
以下是详细代码:
/** * Created by on 2017/7/5. * 公司:北京华星成汇文化发展有限公司 * 描述: */ public class HeaderAndFooterWrapper extends RecyclerView.Adapter<RecyclerView.ViewHolder> { private static final int BASE_ITEM_TYPE_HEADER = 100000; private static final int BASE_ITEM_TYPE_FOOTER = 200000; //头集合 尾结合 private SparseArrayCompat<View> mHeaderViews = new SparseArrayCompat<>(); private SparseArrayCompat<View> mFootViews = new SparseArrayCompat<>(); private RecyclerView.Adapter mInnerAdapter; /** * 把传进来的adapter赋值给成员变量 * * @param adapter */ public HeaderAndFooterWrapper(RecyclerView.Adapter adapter) { mInnerAdapter = adapter; } private boolean isHeaderViewPos(int position) { return position < getHeadersCount(); } private boolean isFooterViewPos(int position) { return position >= getHeadersCount() + getRealItemCount(); } /** * 添加头部方法 * * @param view */ publ 4000 ic void addHeaderView(View view) { mHeaderViews.put(mHeaderViews.size() + BASE_ITEM_TYPE_HEADER, view); } /** * 添加尾部方法 * * @param view */ public void addFootView(View view) { mFootViews.put(mFootViews.size() + BASE_ITEM_TYPE_FOOTER, view); } /** * 获取头部集合的大小 * * @return */ public int getHeadersCount() { return mHeaderViews.size(); } /** * 获取尾部集合的大小 * * @return */ public int getFootersCount() { return mFootViews.size(); } /** * 获取adapter的大小 * * @return */ private int getRealItemCount() { return mInnerAdapter.getItemCount(); } @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; return new HeaderViewHolder(mHeaderViews.get(viewType)); } else if (mFootViews.get(viewType) != null) { // ViewHolder holder = ViewHolder.createViewHolder(parent.getContext(), mFootViews.get(viewType)); // return holder; return new FootViewHolder(mFootViews.get(viewType)); } return mInnerAdapter.onCreateViewHolder(parent, viewType); } @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()); } @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 getItemCount() { return getHeadersCount() + getFootersCount() + getRealItemCount(); } @Override public void onAttachedToRecyclerView(RecyclerView recyclerView) { mInnerAdapter.onAttachedToRecyclerView(recyclerView); /** * 解决网格布局问题 */ RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager(); if (layoutManager instanceof GridLayoutManager) { final GridLayoutManager gridLayoutManager = (GridLayoutManager) layoutManager; 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(); } else { return 1; } } }); } } // /** // * 解决 StaggeredGridLayoutManager样式的加头部问题,暂时没用 // * @param holder // */ // @Override // public void onViewAttachedToWindow(RecyclerView.ViewHolder 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); // } // } // } private class HeaderViewHolder extends RecyclerView.ViewHolder { HeaderViewHolder(View itemView) { super(itemView); } } private class FootViewHolder extends RecyclerView.ViewHolder { FootViewHolder(View itemView) { super(itemView); } } }
RecyclerViewHeader
RecyclerViewHeader继承自LinearLayout用来实现下拉刷新时的界面展示,可以分为三种状态:正常、准备刷新、正在加载。在添加布局文件的时候,指定高度为0,这是为了隐藏header,setState()是设置header的状态,因为header需要根据不同的状态,完成控件隐藏、显示、改变文字等操作,这个方法主要是在PullRefreshRecyclerView里面调用。除此之外,还有setVisiableHeight()和getVisiableHeight(),这两个方法是为了设置和获取Header中根布局文件的高度属性,从而完成拉伸和收缩的效果
/** * Created by 刘龙 on 2017/7/18. * 公司:北京华星成汇文化发展有限公司 * 描述: */ public class RecyclerViewHeader extends LinearLayout { /** * 动画执行时间 */ private final int ROTATE_ANIM_DURATION = 180; public final static int STATE_NORMAL = 0; public final static int STATE_READY = 1; public final static int STATE_REFRESHING = 2; /** * 当前状态 */ private int mState = STATE_NORMAL; //获取到头布局 private LinearLayout mContainer; //获取到控件 private ImageView mArrowImageView; // private ProgressBar mProgressBar; private TextView mHintTextView; //初始化动画 // private RotateAnimation mRotateUpAnim; // private Animation mRotateDownAnim; private TextView mTitleTextView; private RelativeLayout mRealityContent; public RecyclerViewHeader(Context context) { this(context, null); } public RecyclerViewHeader(Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); } public RecyclerViewHeader(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context); } private void init(Context context) { // 初始情况,设置下拉刷新view高度为0 LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, 0); //获取下拉布局 mContainer = (LinearLayout) LayoutInflater.from(context).inflate(R.layout.pullrefrefh_recyclerview_header, (ViewGroup) getParent(), true); //添加到改容器 addView(mContainer, lp); //显示位置下面 setGravity(Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL); //初始化控件 mRealityContent = (RelativeLayout) mContainer.findViewById(R.id.pullRefresh_reality_content); mArrowImageView = (ImageView) mContainer.findViewById(R.id.pullRefresh_arrow); mHintTextView = (TextView) mContainer.findViewById(R.id.pullRefresh_text); // mProgressBar = (ProgressBar) findViewById(R.id.pullRefresh_progressbar); mTitleTextView = (TextView) mContainer.findViewById(R.id.pullRefresh_title); //初始化动画 // mRotateUpAnim = new RotateAnimation(0.0f, -180.0f, // Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, // 0.5f); // mRotateUpAnim.setDuration(ROTATE_ANIM_DURATION); // mRotateUpAnim.setFillAfter(true); // mRotateDownAnim = new RotateAnimation(-180.0f, 0.0f, // Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, // 0.5f); // mRotateDownAnim.setDuration(ROTATE_ANIM_DURATION); // mRotateDownAnim.setFillAfter(true); } public void setState(int state) { //如果状态相同 直接返回 if (mState == state) return; //如果传进来的是刷新状态 if (state == STATE_REFRESHING) { // 正在加载显示圆圈进度 // mArrowImageView.clearAnimation(); // mArrowImageView.setVisibility(View.INVISIBLE); // mProgressBar.setVisibility(View.VISIBLE); } else { // 显示箭头图片 // mArrowImageView.setVisibility(View.VISIBLE); // mProgressBar.setVisibility(View.INVISIBLE); } switch (state) { case STATE_NORMAL://正常状态 if (mState == STATE_READY) { // mArrowImageView.startAnimation(mRotateDownAnim); } if (mState == STATE_REFRESHING) { // mArrowImageView.clearAnimation(); } mHintTextView.setText("下拉刷新"); break; case STATE_READY://可以刷新状态 if (mState != STATE_READY) { // mArrowImageView.clearAnimation(); // mArrowImageView.startAnimation(mRotateUpAnim); mHintTextView.setText("松开刷新数据"); } break; case STATE_REFRESHING://刷新状态 mHintTextView.setText("正在加载..."); break; default: } mState = state; } /** * 设置显示的图片 * * @param imagePath */ public void setPullImage(String imagePath) { Drawable fromPath = Drawable.createFromPath(imagePath); Bitmap bitmap = BitmapFactory.decodeFile(imagePath); // mArrowImageView.setBackground(fromPath); mArrowImageView.setImageBitmap(bitmap); } /** * 设置显示的文字 * * @param text */ public void setPullContent(String text) { mTitleTextView.setText(text); } /** * 获取本身实际的高度 */ public int getRealityHeight() { return mRealityContent.getHeight(); } /** * 设置隐藏高度 * * @param height */ public void setVisibleHeight(int height) { if (height < 0) { height = 0; } LayoutParams lp = (LayoutParams) mContainer.getLayoutParams(); lp.height = height; mContainer.setLayoutParams(lp); } /** * 获取隐藏的高度 * * @return */ public int getVisibleHeight() { return mContainer.getLayoutParams().height; } }
说完了Header,我们再看看Footer。Footer是为了完成加载更多功能时候的界面展示,基本思路和Header是一样的,不过Footer的拉伸和显示效果不是通过高度来模拟的,而是通过设置BottomMargin来完成的。
/** * Created by 刘龙 on 2017/8/9. * 公司:北京华星成汇文化发展有限公司 * 描述: */ public class RecyclerViewFooter extends LinearLayout { public final static int STATE_NORMAL = 0; public final static int STATE_READY = 1; public final static int STATE_LOADING = 2; private Context context; private View contentView; private View progressBar; private TextView hintView; public RecyclerViewFooter(Context context) { this(context, null); } public RecyclerViewFooter(Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); } public RecyclerViewFooter(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); this.context = context; initView(); } private void initView() { LinearLayout moreView = (LinearLayout) LayoutInflater.from(context).inflate(R.layout.pullrefrefh_recyclerview_footer, null); addView(moreView); moreView.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)); contentView = moreView.findViewById(R.id.pullrefrefh_footer_content); progressBar = moreView.findViewById(R.id.pullrefrefh_footer_ProgressBar); hintView = (TextView) moreView.findViewById(R.id.pullrefrefh_footer_hint_TextView); } /** * 设置状态 * * @param state */ public void setState(int state) { hintView.setVisibility(View.INVISIBLE); progressBar.setVisibility(View.INVISIBLE); hintView.setVisibility(View.INVISIBLE); if (state == STATE_READY) { hintView.setVisibility(View.VISIBLE); hintView.setText("松开载入更多"); } else if (state == STATE_LOADING) { progressBar.setVisibility(View.VISIBLE); } else { hintView.setVisibility(View.VISIBLE); hintView.setText("查看更多"); } } /** * 设置距离下边的BottomMargin * * @param height */ public void setBottomMargin(int height) { if (height < 0) return; LayoutParams lp = (LayoutParams) contentView.getLayoutParams(); lp.bottomMargin = height; contentView.setLayoutParams(lp); } /** * 获取BottomMargin * * @return */ public int getBottomMargin() { LayoutParams lp = (LayoutParams) contentView.getLayoutParams(); return lp.bottomMargin; } /** * hide footer when disable pull load more */ public void hide() { LayoutParams lp = (LayoutParams) contentView.getLayoutParams(); lp.height = 0; contentView.setLayoutParams(lp); } /** * show footer */ public void show() { LayoutParams lp = (LayoutParams) contentView.getLayoutParams(); lp.height = LayoutParams.WRAP_CONTENT; contentView.setLayoutParams(lp); } }
MessageRelativeLayout
该类主要是为了实现在刷新完毕的时候可以显示一个更新了多少条,网络错误等等的提示语:在添加布局文件的时候,指定高度为0,这是为了隐藏提示语,实现思路与头布局类似,如不需要此功能可忽略,不会影响代码的使用
/** * Created by on 2017/7/18. * 公司:北京华星成汇文化发展有限公司 * 描述: */ public class MessageRelativeLayout extends RelativeLayout { //显示消息的控件 private LinearLayout mHeaderMessageView; private TextView mHeaderMessageText; private int mHeaderMessageViewHeight; //滚动类 private Scroller mScroller; public MessageRelativeLayout(Context context) { this(context, null); } public MessageRelativeLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } public MessageRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context); } private void init(Context context) { //滚动类 mScroller = new Scroller(context, new DecelerateInterpolator()); //初始化一个显示消息的textView mHeaderMessageView = (LinearLayout) LayoutInflater.from(context).inflate(R.layout.pullrefresh_header_message, (ViewGroup) getParent(), false); mHeaderMessageView.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0)); mHeaderMessageText = (TextView) mHeaderMessageView.findViewById(R.id.pullRefresh_message); // 初始化 头部高度 mHeaderMessageText.getViewTreeObserver().addOnGlobalLayoutListener( new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { mHeaderMessageViewHeight = mHeaderMessageText.getHeight();//57 getViewTreeObserver().removeOnGlobalLayoutListener(this); } }); } @Override protected void onFinishInflate() { super.onFinishInflate(); //确保添加到后面 addView(mHeaderMessageView, 1); } public void showMessage() { mScroller.startScroll(0, getHeaderMessageViewHeig d399 ht(), 0, 0, PullRefreshRecyclerView.SCROLL_DURATION); invalidate(); } public void hideMessage() { mScroller.startScroll(0, getVisibleHeight(), 0, -getVisibleHeight(), PullRefreshRecyclerView.SCROLL_DURATION); invalidate(); } /** * 设置消息 */ public void setMessage(String message) { mHeaderMessageText.setText(message); } /** * 获取消息总高度 * * @return */ public int getHeaderMessageViewHeight() { return mHeaderMessageViewHeight; } /** * 设置隐藏高度 * * @param height */ private void setVisibleHeight(int height) { if (height < 0) { height = 0; } LayoutParams lp = (LayoutParams) mHeaderMessageView.getLayoutParams(); lp.height = height; mHeaderMessageView.setLayoutParams(lp); } /** * 获取隐藏的高度 * * @return */ public int getVisibleHeight() { return mHeaderMessageView.getLayoutParams().height; } @Override public void computeScroll() { if (mScroller.computeScrollOffset()) { setVisibleHeight(mScroller.getCurrY()); postInvalidate(); } super.computeScroll(); } }
PullRefreshRecyclerView
在了解了Header,Footer和Message之后,我们就要介绍最核心的PullRefreshRecyclerView的代码实现了。便于观看,本代码有部分删减重构
/** * Created by 刘龙 on 2017/7/18. * 公司:北京华星成汇文化发展有限公司 * 描述: */ public class PullRefreshRecyclerView extends RecyclerView { private float mLastY = -1; // save event y /** * 滚动需要的时间 */ public final static int SCROLL_DURATION = 200; /** * 提示消息显示时间 */ public final static int MESSAGE_SHOW_DURATION = 2000; /** * 阻尼效果 */ private final static float OFFSET_RADIO = 1.5f; /** * 上拉加载的距离,默认50px */ private static final int PULL_LOAD_MORE_DELTA = 50; /** * 是否设置为自动加载更多,目前没实现 */ private boolean mEnableAutoLoading = false; /** * 是否可以上拉 默认可以 */ private boolean mEnablePullLoad = true; /** * 是否可以下拉 默认可以 */ private boolean mEnablePullRefresh = true; /** * 是否正在加载 */ private boolean mPullLoading = false; /** * 是否正在刷新 */ private boolean mPullRefreshing = false; /** * 区分上拉和下拉 */ private int mScrollBack; private final static int SCROLLBACK_HEADER = 0; private final static int SCROLLBACK_FOOTER = 1; //滚动类 private Scroller mScroller; //头布局控件 private RecyclerViewHeader mHeaderView; //尾控件 private RecyclerViewFooter mFooterView; //消息提示类 private MessageRelativeLayout mParent; //adapter的装饰类 private HeaderAndFooterWrapper mHeaderAndFooterWrapper; public PullRefreshRecyclerView(Context context) { this(context, null); } public PullRefreshRecyclerView(Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); } public PullRefreshRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(context); } private void init(Context context) { //滚动类 mScroller = new Scroller(context, new DecelerateInterpolator()); //获取到头布局 mHeaderView = new RecyclerViewHeader(context); mHeaderView.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); //获取尾布局 mFooterView = new RecyclerViewFooter(context); mFooterView.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); } private Adapter adapter; @Override public void setAdapter(Adapter adapter) { this.adapter = adapter; mHeaderAndFooterWrapper = new HeaderAndFooterWrapper(adapter); super.setAdapter(mHeaderAndFooterWrapper); //添加头,确保是第一个 mHeaderAndFooterWrapper.addHeaderView(mHeaderView); //添加尾,确保是第最后一个 mHeaderAndFooterWrapper.addFootView(mFooterView); //获取到它的父容器 if (getParent() instanceof MessageRelativeLayout) { mParent = (MessageRelativeLayout) getParent(); } } @Override public boolean onTouchEvent(MotionEvent e) { if (mLastY == -1) { mLastY = e.getRawY(); } switch (e.getAction()) { case MotionEvent.ACTION_DOWN: //按下的时候记录值 mLastY = e.getRawY(); break; case MotionEvent.ACTION_MOVE: float moveY = e.getRawY(); //手指滑动的差值 float distanceY = moveY - mLastY; mLastY = moveY; //第一个条目完全显示 //头部高度大于0 deltaY大于0 向下移动 if ((((LinearLayoutManager) getLayoutManager()).findFirstCompletelyVisibleItemPosition() == 0 || ((LinearLayoutManager) getLayoutManager()).findFirstCompletelyVisibleItemPosition() == 1) && (mHeaderView.getVisibleHeight() > 0 || distanceY > 0)) { // 更新头部高度 updateHeaderHeight(distanceY / OFFSET_RADIO); } else if (isSlideToBottom() && (mFooterView.getBottomMargin() > 0 || distanceY < 0)) { Log.e("PullRefreshRecyclerView","-------111------"+distanceY); //已经到达底部,改变状态或者自动加载 updateFooterHeight(-distanceY / OFFSET_RADIO); }else if (distanceY > 0){ updateFooterHeight(-distanceY / OFFSET_RADIO); } break; default: mLastY = -1; // 复位 if ((((LinearLayoutManager) getLayoutManager()).findFirstCompletelyVisibleItemPosition() == 0 || ((LinearLayoutManager) getLayoutManager()).findFirstCompletelyVisibleItemPosition() == 1)) { // 松手的时候 高度大于 一定值 调用刷新 if (mEnablePullRefresh && mHeaderView.getVisibleHeight() > mHeaderView.getRealityHeight()) { //变为刷新状态 mPullRefreshing = true; mHeaderView.setState(RecyclerViewHeader.STATE_REFRESHING); //回调事件 if (mOnRefreshListener != null) { mOnRefreshListener.onRefresh(); } } resetHeaderHeight(); } else if (isSlideToBottom()) { // invoke load more. if (mEnablePullLoad && mFooterView.getBottomMargin() > PULL_LOAD_MORE_DELTA && !mPullLoading) { mPullLoading = true; mFooterView.setState(RecyclerViewFooter.STATE_LOADING); if (mOnRefreshListener != null) { mOnRefreshListener.onLoadMore(); } } resetFooterHeight(); } else { // resetFooterHeight(); resetHeaderHeight(); } break; } return super.onTouchEvent(e); } /** * 更新尾部加载 * * @param distance */ private void updateFooterHeight(float distance) { int height = mFooterView.getBottomMargin() + (int) distance; Log.e("PullRefreshRecyclerView","-------------"+height); if (mEnablePullLoad && !mPullLoading) { if (height > PULL_LOAD_MORE_DELTA) { //改变状态 mFooterView.setState(RecyclerViewFooter.STATE_READY); } else { mFooterView.setState(RecyclerViewFooter.STATE_NORMAL); } } mFooterView.setBottomMargin(height); } /** * 更新头部刷新 * * @param distance */ private void updateHeaderHeight(float distance) { // 设置头部高度,原先的高度加上 mHeaderView.setVisibleHeight((int) distance + mHeaderView.getVisibleHeight()); // 未处于刷新状态,更新箭头 if (mEnablePullRefresh && !mPullRefreshing) { //下拉高度到达可以刷新的位置 if (mHeaderView.getVisibleHeight() > mHeaderView.getRealityHeight()) { mHeaderView.setState(RecyclerViewHeader.STATE_READY); } else { mHeaderView.setState(RecyclerViewHeader.STATE_NORMAL); } } //移动到顶部 smoothScrollBy(0, 0); } /** * 重置头部高度 */ private void resetHeaderHeight() { int height = mHeaderView.getVisibleHeight(); if (height == 0) // 如果=0 是不可见的 直接返回 return; if (mPullRefreshing && height <= mHeaderView.getRealityHeight()) { return; } int finalHeight = 0; if (mPullRefreshing && height > mHeaderView.getRealityHeight()) { finalHeight = mHeaderView.getRealityHeight(); } if (mParent != null) { if (mHeaderView.getVisibleHeight() == mParent.getHeaderMessageViewHeight()) { finalHeight = mParent.getHeaderMessageViewHeight(); } } mScrollBack = SCROLLBACK_HEADER;//设置标识 mScroller.startScroll(0, height, 0, finalHeight - height, SCROLL_DURATION); // 触发计算滚动 invalidate(); } /** * 重置尾部高度 */ private void resetFooterHeight() { int bottomMargin = mFooterView.getBottomMargin(); if (bottomMargin > 0) { mScrollBack = SCROLLBACK_FOOTER;//设置标识 mScroller.startScroll(0, bottomMargin, 0, -bottomMargin, SCROLL_DURATION); invalidate(); } } /** * 停止刷新 */ public void stopRefresh() { mScrollBack = SCROLLBACK_HEADER;//设置标识 int obligateHeight; if (mParent != null) { obligateHeight = mParent.getHeaderMessageViewHeight(); } else { obligateHeight = 0; } int height = mHeaderView.getVisibleHeight(); if (mPullRefreshing) { //是否复位 mPullRefreshing = false; //显示更新了多少条消息 if (mParent != null) { mParent.showMessage(); } mScroller.startScroll(0, height, 0, obligateHeight - height, SCROLL_DURATION); // 触发计算滚动 invalidate(); //延时执行复位移动 if (mParent != null) { handler.removeCallbacksAndMessages(null); handler.sendEmptyMessageDelayed(1, MESSAGE_SHOW_DURATION); } } } /** * 停止加载 */ public void stopLoadMore() { if (mPullLoading) { mPullLoading = false; mFooterView.setState(RecyclerViewFooter.STATE_NORMAL); } } /** * 消息 */ private Handler handler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); if (mHeaderView.getVisibleHeight() == mParent.getHeaderMessageViewHeight()) { // resetHeaderHeight(); mScroller.startScroll(0, mHeaderView.getVisibleHeight(), 0, -mHeaderView.getVisibleHeight(), SCROLL_DURATION); postInvalidate(); } mParent.hideMessage(); } }; @Override public void computeScroll() { if (mScroller.computeScrollOffset()) { if (mScrollBack == SCROLLBACK_HEADER) { mHeaderView.setVisibleHeight(mScroller.getCurrY()); } else { mFooterView.setBottomMargin(mScroller.getCurrY()); } postInvalidate(); } super.computeScroll(); } private OnRefreshListener mOnRefreshListener; public void setOnRefreshListener(OnRefreshListener onRefreshListener) { mOnRefreshListener = onRefreshListener; } /** * 刷新接口, */ public interface OnRefreshListener { void onRefresh(); void onLoadMore(); } /** * 判断是否到底 * * @return */ private boolean isSlideToBottom() { return computeVerticalScrollExtent() + computeVerticalScrollOffset() >= computeVerticalScrollRange(); } }
最后重要的事说三遍:本项目扩展性极高,建议下载源码观看可更清晰的了解此项目的结构
源码地址:http://download.csdn.net/download/q714093365/9928217
转载请注明出处:http://blog.csdn.net/q714093365/article/details/77063084
相关文章推荐
- 【FastDev4Android框架开发】RecyclerView完全解析之下拉刷新与上拉加载SwipeRefreshLayout(三十一)
- RecyclerView完全解析之下拉刷新与上拉加载SwipeRefreshLayout
- 自定义的RecyclerView, 下拉刷新,加载更多.
- Android-详解RecyclerView+BGARefreshLayout实现自定义下拉刷新、上拉加载和侧滑删除效果
- RecyclerView完全解析之下拉刷新与上拉加载
- [置顶] 结合SwipeRefreshLayout可以上拉加载更多下拉刷新的RecyclerView
- 【FastDev4Android框架开发】RecyclerView完全解析之下拉刷新与上拉加载SwipeRefreshLayout(三十一)
- Android从零开搞系列:自定义View(2)继承RecyclerView实现下拉刷新和加载更多
- 支持下拉刷新和上划加载更多的自定义RecyclerView(仿XListView效果)
- [置顶] Xamarin android 使用RecyclerView结合SwipeRefreshLayout下拉刷新滑到底部加载更多
- 自定义RecyclerView实现下拉刷新和上拉加载
- YRecyclerView自定义下拉刷新上拉加载更多的RecyclerView
- android-- 自定义下拉刷新上拉加载控件(SwipeRefreshLayout + recyclerView)
- 详解RecyclerView+BGARefreshLayout实现自定义下拉刷新、上拉加载和侧滑删除效果
- Android RecyclerView(八)设置自定义 下拉刷新 与 上拉加载数据
- Android仿XListView支持下拉刷新和上划加载更多的自定义RecyclerView
- 自定义RecyclerView实现下拉刷新,加载更多
- Android下拉刷新-SwipeRefreshLayout,RecyclerView完全解析之下拉刷新与上拉加载SwipeRefreshLayout)
- android 自定义下拉刷新上拉加载控件(SwipeRefreshLayout + recyclerView)
- 自定义RecyclerView添加下拉刷新和上拉加载功能