完全自定義RecyclerView下拉重新整理上拉載入
自從谷歌推出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 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 */ 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中根佈局檔案的高度屬性,從而完成拉伸和收縮的效果
/**
* 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, 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 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();
}
}
最後重要的事說三遍:本專案擴充套件性極高,建議下載原始碼觀看可更清晰的瞭解此專案的結構