RecyclerView系列(五)下拉刷新的RecyclerView封裝

RecyclerView的下拉刷新需要自己封裝,其實是RecyclerView添加頭部之后的處理邏輯的編輯。所以我們繼承自添加頭部和底部的RecyclerView開始封裝。

一、添加頭部

處理下拉刷新吁恍,同時考慮刷新列表的不同風格樣式,確保每個項目都能用银受, 所以我們不能直接添加View践盼,需要利用輔助類鸦采。

   public void addRefreshViewCreator(RefreshViewCreator refreshCreator) {
     this.mRefreshCreator = refreshCreator;
    addRefreshView();
    }

其中輔助類提供給外界的方法

public abstract class RefreshViewCreator {

/**
 * 獲取下拉刷新的View
 *
 * @param context 上下文
 * @param parent  RecyclerView
 */
public abstract View getRefreshView(Context context, ViewGroup parent);

/**
 * 正在下拉
 * @param currentDragHeight   當前拖動的高度
 * @param refreshViewHeight  總的刷新高度
 * @param currentRefreshStatus 當前狀態(tài)
 */
public abstract void onPull(int currentDragHeight, int refreshViewHeight, int currentRefreshStatus);

/**
 * 正在刷新中
 */
public abstract void onRefreshing();

/**
 * 停止刷新
 */
public abstract void onStopRefresh();
}

添加頭部的方法:

  /**
 * 添加頭部的刷新View
 */
private void addRefreshView() {
    RecyclerView.Adapter adapter = getAdapter();
    if (adapter != null && mRefreshCreator != null) {
        // 添加頭部的刷新View
        View refreshView = mRefreshCreator.getRefreshView(getContext(), this);
        if (refreshView != null) {
            addHeaderView(refreshView);
            this.mRefreshView = refreshView;
        }
    }
    }

二宾巍、重寫dispatchTouchEvent()方法

  @Override
  public boolean dispatchTouchEvent(MotionEvent ev) {
    switch (ev.getAction()) {
        case MotionEvent.ACTION_DOWN:
            // 記錄手指按下的位置 ,之所以寫在dispatchTouchEvent那是因為如果我們處理了條目點擊事件,
            // 那么就不會進入onTouchEvent里面渔伯,所以只能在這里獲取
            mFingerDownY = (int) ev.getRawY();
            break;

        case MotionEvent.ACTION_UP:
            if (mCurrentDrag) {
                restoreRefreshView();
            }
            break;
    }
    return super.dispatchTouchEvent(ev);
}

restoreRefreshView()方法

/**
 * 重置當前刷新狀態(tài)狀態(tài)
 */
private void restoreRefreshView() {
    int currentTopMargin = ((MarginLayoutParams) mRefreshView.getLayoutParams()).topMargin;
    int finalTopMargin = -mRefreshViewHeight + 1;
    if (mCurrentRefreshStatus == REFRESH_STATUS_LOOSEN_REFRESHING) {
        finalTopMargin = 0;
        mCurrentRefreshStatus = REFRESH_STATUS_REFRESHING;
        if (mRefreshCreator != null) {
            mRefreshCreator.onRefreshing();
        }
        if (mListener != null) {
            mListener.onRefresh();
        }
    }

    int distance = currentTopMargin - finalTopMargin;

    // 回彈到指定位置
    ValueAnimator animator = ObjectAnimator.ofFloat(currentTopMargin, finalTopMargin).setDuration(distance);
    animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            float currentTopMargin = (float) animation.getAnimatedValue();
            setRefreshViewMarginTop((int) currentTopMargin);
        }
    });
    animator.start();
    mCurrentDrag = false;
}

三顶霞、重寫onTouchEvent()方法

  @Override
  public boolean onTouchEvent(MotionEvent e) {
    switch (e.getAction()) {
        case MotionEvent.ACTION_MOVE:
            // 如果是在最頂部才處理,否則不需要處理
            if (canScrollUp() || mCurrentRefreshStatus == REFRESH_STATUS_REFRESHING
                    || mRefreshView == null || mRefreshCreator == null) {
                // 如果沒有到達最頂端,也就是說還可以向上滾動就什么都不處理
                return super.onTouchEvent(e);
            }

            // 解決下拉刷新自動滾動問題
            if (mCurrentDrag) {
                scrollToPosition(0);
            }

            // 獲取手指觸摸拖拽的距離
            int distanceY = (int) ((e.getRawY() - mFingerDownY) * mDragIndex);
            // 如果是已經(jīng)到達頭部选浑,并且不斷的向下拉蓝厌,那么不斷的改變refreshView的marginTop的值
            if (distanceY > 0) {
                int marginTop = distanceY - mRefreshViewHeight;
                setRefreshViewMarginTop(marginTop);
                updateRefreshStatus(distanceY);
                mCurrentDrag = true;
                return false;
            }
            break;
    }

    return super.onTouchEvent(e);
}

/**
 * 設置刷新View的marginTop
 */
private void setRefreshViewMarginTop(int marginTop) {
    MarginLayoutParams params = (MarginLayoutParams) mRefreshView.getLayoutParams();
    if (marginTop < -mRefreshViewHeight + 1) {
        marginTop = -mRefreshViewHeight + 1;
    }
    params.topMargin = marginTop;
    mRefreshView.setLayoutParams(params);
}

 /**
 * 更新刷新的狀態(tài)
 */
private void updateRefreshStatus(int distanceY) {
    if (distanceY <= 0) {
        mCurrentRefreshStatus = REFRESH_STATUS_NORMAL;
    } else if (distanceY < mRefreshViewHeight) {
        mCurrentRefreshStatus = REFRESH_STATUS_PULL_DOWN_REFRESH;
    } else {
        mCurrentRefreshStatus = REFRESH_STATUS_LOOSEN_REFRESHING;
    }

    if (mRefreshCreator != null) {
        mRefreshCreator.onPull(distanceY, mRefreshViewHeight, mCurrentRefreshStatus);
    }
}

四、重寫onLayout方法

  @Override
  protected void onLayout(boolean changed, int l, int t, int r, int b) {
    super.onLayout(changed, l, t, r, b);
    //if (changed) {
    if (mRefreshView != null && mRefreshViewHeight <= 0) {
        // 獲取頭部刷新View的高度
        mRefreshViewHeight = mRefreshView.getMeasuredHeight();
        if (mRefreshViewHeight > 0) {
            // 隱藏頭部刷新的View  marginTop  多留出1px防止無法判斷是不是滾動到頭部問題
            setRefreshViewMarginTop(-mRefreshViewHeight + 1);
        }
    }
    // }
}

五古徒、處理刷新回調

 /**
 * @return Whether it is possible for the child view of this layout to
 * scroll up. Override this if the child view is a custom view.
 * 判斷是不是滾動到了最頂部拓提,這個是從SwipeRefreshLayout里面copy過來的源代碼
 */
private boolean canScrollUp() {
    if (android.os.Build.VERSION.SDK_INT < 14) {
        return ViewCompat.canScrollVertically(this, -1) || this.getScrollY() > 0;
    } else {
        return ViewCompat.canScrollVertically(this, -1);
    }
}

/**
 * 停止刷新
 */
public void onStopRefresh() {
    mCurrentRefreshStatus = REFRESH_STATUS_NORMAL;
    restoreRefreshView();
    if (mRefreshCreator != null) {
        mRefreshCreator.onStopRefresh();
    }
}

// 處理刷新回調監(jiān)聽
private OnRefreshListener mListener;

public void setOnRefreshListener(OnRefreshListener listener) {
    this.mListener = listener;
}

public interface OnRefreshListener {
    void onRefresh();
}

六、具體使用

實現(xiàn)輔助類

public class DefaultRefreshCreator extends RefreshViewCreator {
// 加載數(shù)據(jù)的ImageView
private View mRefreshIv;

@Override
public View getRefreshView(Context context, ViewGroup parent) {
    View refreshView = LayoutInflater.from(context).inflate(R.layout.layout_refresh_header_view, parent, false);
    mRefreshIv = refreshView.findViewById(R.id.refresh_iv);
    return refreshView;
}

@Override
public void onPull(int currentDragHeight, int refreshViewHeight, int currentRefreshStatus) {
    float rotate = ((float) currentDragHeight) / refreshViewHeight;
    // 不斷下拉的過程中旋轉圖片
    mRefreshIv.setRotation(rotate * 360);
}

@Override
public void onRefreshing() {
    // 刷新的時候不斷旋轉
    RotateAnimation animation = new RotateAnimation(0, 720,
            Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
    animation.setRepeatCount(-1);
    animation.setDuration(1000);
    mRefreshIv.startAnimation(animation);
}

@Override
public void onStopRefresh() {
    // 停止加載的時候清除動畫
    mRefreshIv.setRotation(0);
    mRefreshIv.clearAnimation();
}
}

設置

  mRecyclerView.addRefreshViewCreator(new DefaultRefreshCreator());
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末隧膘,一起剝皮案震驚了整個濱河市代态,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌疹吃,老刑警劉巖蹦疑,帶你破解...
    沈念sama閱讀 212,816評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異萨驶,居然都是意外死亡歉摧,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,729評論 3 385
  • 文/潘曉璐 我一進店門腔呜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來叁温,“玉大人,你說我怎么就攤上這事核畴∪眩” “怎么了?”我有些...
    開封第一講書人閱讀 158,300評論 0 348
  • 文/不壞的土叔 我叫張陵膛檀,是天一觀的道長锰镀。 經(jīng)常有香客問我,道長咖刃,這世上最難降的妖魔是什么泳炉? 我笑而不...
    開封第一講書人閱讀 56,780評論 1 285
  • 正文 為了忘掉前任,我火速辦了婚禮嚎杨,結果婚禮上花鹅,老公的妹妹穿的比我還像新娘。我一直安慰自己枫浙,他們只是感情好刨肃,可當我...
    茶點故事閱讀 65,890評論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著箩帚,像睡著了一般真友。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上紧帕,一...
    開封第一講書人閱讀 50,084評論 1 291
  • 那天盔然,我揣著相機與錄音桅打,去河邊找鬼。 笑死愈案,一個胖子當著我的面吹牛挺尾,可吹牛的內容都是我干的。 我是一名探鬼主播站绪,決...
    沈念sama閱讀 39,151評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼遭铺,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了恢准?” 一聲冷哼從身側響起掂僵,我...
    開封第一講書人閱讀 37,912評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎顷歌,沒想到半個月后锰蓬,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,355評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡眯漩,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,666評論 2 327
  • 正文 我和宋清朗相戀三年芹扭,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片赦抖。...
    茶點故事閱讀 38,809評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡舱卡,死狀恐怖,靈堂內的尸體忽然破棺而出队萤,到底是詐尸還是另有隱情轮锥,我是刑警寧澤,帶...
    沈念sama閱讀 34,504評論 4 334
  • 正文 年R本政府宣布要尔,位于F島的核電站舍杜,受9級特大地震影響,放射性物質發(fā)生泄漏赵辕。R本人自食惡果不足惜既绩,卻給世界環(huán)境...
    茶點故事閱讀 40,150評論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望还惠。 院中可真熱鬧饲握,春花似錦、人聲如沸蚕键。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽锣光。三九已至笆怠,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間嫉晶,已是汗流浹背骑疆。 一陣腳步聲響...
    開封第一講書人閱讀 32,121評論 1 267
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留替废,地道東北人箍铭。 一個月前我還...
    沈念sama閱讀 46,628評論 2 362
  • 正文 我出身青樓,卻偏偏與公主長得像椎镣,于是被迫代替她去往敵國和親诈火。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,724評論 2 351

推薦閱讀更多精彩內容