自从谷歌推出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
以下是详细代码:
/**
* Created by
* 公司:
* 描述:
*/
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
*/
public 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中根布局文件的高度属性,从而完成拉伸和收缩的效果
/**
*
*
* 描述:
*/
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来完成的。
/**
*
* 公司:
* 描述:
*/
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
* 公司:
* 描述:
*/
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, getHeaderMessageViewHeight(), 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
* 公司
* 描述:
*/
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