RecyclerView下拉重新整理 上拉載入 原理Demo

Cushion_ZM發表於2017-02-27

RecycleView作為Android5.0谷歌對ListView的升級版,其強大之處是:比ListView更為輕量,使用得當的話,完全可以替代ListView/GridView。本節主要說明新增重新整理頭的原理。首先我們先來熟悉一下RecycleView的簡單使用吧。


recycleView的用法:

1.因為RecycleView在V7包裡面有引入,所以我們要匯入正確的v7包(字尾帶有recycle的v7包);
 //設定佈局管理器
        rvMain.setLayoutManager(new LinearLayoutManager(this));
        //設定adapter
        rvMain.setAdapter(new HomeAdapter());
        //設定Item增加、移除動畫
        rvMain.setItemAnimator(new DefaultItemAnimator());
        //新增分割線
        rvMain.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL));
2.該分割線是系統預設的,你可以在theme.xml中找到該屬性的使用情況

注意:這裡的佈局管理器有三種,

rvMain.setLayoutManager(new LinearLayoutManager(this));
//rvMain.setLayoutManager(new GridLayoutManager(this,4));
//rvMain.setLayoutManager(new StaggeredGridLayoutManager(3,StaggeredGridLayoutManager.VERTICAL));

大家可以分別設定一下看看效果,自己去嘗試一下總比我在這裡演示要我,問題也不難。簡單繁瑣的事情我這裡就略過了。

但是我要告訴大家的是如果你僅僅是改變佈局管理器而不去改寫分割線的話效果是明顯不美觀的。大家可以參考鴻洋大神重寫分割線

的博文,很詳細。

recycleViewAdapter的方法概要:

玩過Android開發的小夥伴都應該知道ListView這玩意吧,但是到了RecyclerView這裡好像就不像ListView一樣提供一個

addHeaderView的方法。那麼我們如何實現這個效果呢?

下拉載入跟多原理:
首先我們要了解RecycleViewAdapter的以下方法
@Override
public  RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

    if (TYPE_REFRESH_HEADER==viewType){
       return new LViewHolder(mRefreshHeader.getHeaderView());

    }else{
        return new MyViewHolder(LayoutInflater.from(mContext).inflate(R.layout.item_rvmain, parent,
                false));
    }

}

@Override
public void onBindViewHolder( RecyclerView.ViewHolder holder,int position) {
    if (!isRefreshHeader(position)){
        ((MyViewHolder)holder).tv.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(mContext, String.valueOf("aa"),Toast.LENGTH_SHORT).show();
                Intent intent = new Intent(mContext ,CoordinatorActivity.class);
                mContext.startActivity(intent);
            }
        });
        ((MyViewHolder)holder).tv.setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View v) {
                Toast.makeText(mContext, String.valueOf("aa")+"---LONG",Toast.LENGTH_SHORT).show();
                return true;
            }
        });
    }

}

	@Override
	public int getItemCount() {
    		return 20;
	}

	@Override
	public int getItemViewType(int position) {
    	if (isRefreshHeader(position)) {
    	    return TYPE_REFRESH_HEADER;
   	 }
    	return TYPE_NORMAL;
}
這裡我們主要關心getItemViewType和OnCreatViewHolder這兩個方法,我們可以看到getItemViewType可以根據item的position
返回一個Type,然後我們在OnCreatViewHolder這個方法就可以根據Type這個型別來返回一個特定的Viewholder。換句話說就是我們
可以自己定義每一一個Item 樣式。這不相當於我們有了重新整理頭麼,關鍵是我們如何動態去控制這個Item(重新整理頭)的高度?我想是這樣的:重寫RecycleView的OnEventTouch方法然後動態控制重新整理頭的高度。先上效果給大家看看


1.我們自定義一個View作為重新整理頭,即RecycleView的第一個Item。這個自定義的View必須對外提供設定高度,狀態,動態滑動效果等等方法,而且初始化高度為0。
主要實現一個介面提供下滿方法
public interface IRefreshHeader {
    int STATE_NORMAL = 0;
    int STATE_RELEASE_TO_REFRESH = 1;
    int STATE_REFRESHING = 2;
    int STATE_DONE = 3;

    void onReset();


    /**
     * 下拉移動
     */
    void onMove(float offSet, float sumOffSet);


    /**
     * 處於可以重新整理的狀態,已經過了指定距離
     */
    void onPrepare();

    /**
     * 下拉鬆開
     */
    boolean onRelease();

    /**
     * 正在重新整理
     */
    void onRefreshing();

    /**
     * 下拉重新整理完成
     */
    void refreshComplete();

    /**
     * 獲取HeaderView
     */
    View getHeaderView();

    /**
     * 獲取Header的顯示高度
     */
    int getVisibleHeight();

}

2.重寫RecycleView的OnTouchEvent方法如下:
 @Override
    public boolean onTouchEvent(MotionEvent e) {
        if (mLastY == -1) {

            mLastY = e.getRawY();
        }
        int action =e.getAction();
        switch (action){
            case  MotionEvent.ACTION_DOWN:
                mLastY = e.getRawY();
                sumOffSet = 0;
                break;
            case  MotionEvent.ACTION_MOVE:
                final float deltaY = (e.getRawY() - mLastY) / DRAG_RATE;
                mLastY = e.getRawY();
                sumOffSet += deltaY;
		//動態設定第一個Item的高度,即重新整理頭
                mRefreshHeader.onMove(deltaY, sumOffSet);

                Log.v("ACTION_MOVE","-------------------"+deltaY);
                if (mRefreshHeader.getVisibleHeight() > 0 && mRefreshing) {
                    return false;
                }

                break;
//            case  MotionEvent.ACTION_UP:
//                break;
//            case  MotionEvent.ACTION_CANCEL:
//                break;
            default://當滑動鬆開時,重新整理頭滑動到重新整理狀態
                if (mRefreshHeader.onRelease()) {
                    if (mRefreshListener != null) {
                        mFootView.setVisibility(GONE);
                        mRefreshing = true;
			//重新整理狀態的回撥,載入資料的過程
                        mRefreshListener.onRefresh();

                    }
                }

        }
        return super.onTouchEvent(e);
    }
3.主頁面主要邏輯
 public void  BasicUse(){
        

        //設定佈局管理器
        rvMain.setLayoutManager(new LinearLayoutManager(this));
        //rvMain.setLayoutManager(new GridLayoutManager(this,4));
        //rvMain.setLayoutManager(new StaggeredGridLayoutManager(3,StaggeredGridLayoutManager.VERTICAL));

        //設定adapter
        rvMain.setAdapter(new LRecyclerViewAdapter(MainActivity.this));
        //設定Item增加、移除動畫
        rvMain.setItemAnimator(new DefaultItemAnimator());
        //新增分割線
//        rvMain.addItemDecoration(new DividerItemDecoration(
//                this, DividerItemDecoration.VERTICAL));
        rvMain.addItemDecoration(new DividerGridItemDecoration(
                MainActivity.this));

        rvMain.setOnRefreshListener(new OnRefreshListener() {
            @Override
            public void onRefresh() {
                new Handler().postDelayed(new Runnable(){
                    public void run() {
                        //這裡作網路資料請求提供的方法,但我沒有處理
                        //當資料重新整理後我們呼叫下面的方法把正在重新整理狀態下的重新整理頭的高度和狀態改變
                        rvMain.refreshComplete(10);
                    }
                }, 1200);

            }
        });
    }
上拉載入更多原理:
效果圖:
1.首先我們要想如何確定Adapter最後一個肯定是載入更多的那個控制元件。但是我們如何去確定呢?我想法事這樣的,首先把載入更多這個控制元件設定到
Adapter裡面,然後根據檢測到最後一個位置則新增載入更多這個控制元件,這樣最後一個item永遠是載入更多這個控制元件,但是我們接下來想的就是如何
控制這個控制元件的高度和可見性。這個就是觸發載入更多事件入口?
這裡我們需要搞懂的就是RecycleView的滾動事件了。列表的滾動過程一般分兩種:
(1)手指按下 -->手指拖拽滑動--> 手指停止--> 手指抬起
(2)手指按下--> 手指快速拖拽後抬起 --> 列表繼續滑動 -->手指抬起
而上面這兩種滾動過程所持有的狀態是在RecycleView的OnScrollStateChange(int state)這個方法記錄,而且上面兩種滾動的整個過程都是表現為
3重狀態。分別為 1 -->2 -->0(開始滾動,正在滾動,停止滾動)。只要我們在停止滾動是判斷最後一個可見item是否最後一個就可以了,如果是就
載入。
2.RecycleView中觸發載入如下:
@Override
public void onScrollStateChanged(int state) {
    super.onScrollStateChanged(state);
    currentScrollState = state;
    Log.d("------------", "onScrollStateChanged: onScrollStateChanged--------------"+state);
    if (mLScrollListener != null) {
        mLScrollListener.onScrollStateChanged(state);
    }

    if (mLoadMoreListener != null && mLoadMoreEnabled) {
        if (currentScrollState == RecyclerView.SCROLL_STATE_IDLE) {
            RecyclerView.LayoutManager layoutManager = getLayoutManager();
            int visibleItemCount = layoutManager.getChildCount();
            int totalItemCount = layoutManager.getItemCount();
            if (visibleItemCount > 0
                    && lastVisibleItemPosition == totalItemCount - 1
                    && totalItemCount > visibleItemCount
                    && !mRefreshing) {

                mFootView.setVisibility(View.VISIBLE);
                if (mLoadingData) {
                    return;
                } else {
                    mLoadingData = true;
                    mLoadMoreFooter.onLoading();
                    mLoadMoreListener.onLoadMore();
                }
3.dapter的改寫
 @Override
    public int getItemViewType(int position) {
//        if (isRefreshHeader(position)) {
//            return TYPE_REFRESH_HEADER;
//        }
//        return TYPE_NORMAL;

      //  int adjPosition = position - (getHeaderViewsCount() + 1);
        if (isRefreshHeader(position)) {
            return TYPE_REFRESH_HEADER;
        }
//        if (isHeader(position)) {
//            position = position - 1;
//            return mHeaderTypes.get(position);
//        }
        if (isFooter(position)) {
            return TYPE_FOOTER_VIEW;
        }
//        int adapterCount;
//        if (mInnerAdapter != null) {
//            adapterCount = mInnerAdapter.getItemCount();
//            if (adjPosition < adapterCount) {
//                return mInnerAdapter.getItemViewType(adjPosition);
//            }
//        }
        return TYPE_NORMAL;
    }
@Override
public  RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

    if (TYPE_REFRESH_HEADER==viewType){
       return new LViewHolder(mRefreshHeader.getHeaderView());

    } else if (viewType == TYPE_FOOTER_VIEW) {
        return new LViewHolder(mFooterViews.get(0));
    } else {
        return new MyViewHolder(LayoutInflater.from(mContext).inflate(R.layout.item_rvmain, parent,
                false));
    }
3.主頁面邏輯
rvMain.setOnLoadMoreListener(new OnLoadMoreListener() {
    @Override
    public void onLoadMore() {

        new Handler().postDelayed(new Runnable() {
            public void run() {
                //這裡作網路資料請求提供的方法,但我沒有處理
                //當資料重新整理後我們呼叫下面的方法把正在重新整理狀態下的重新整理頭的高度和狀態改變
                rvMain.setNoMore(true);
            }
        }, 1200);


    }
});
這裡只是簡單的介紹了RecycleView下拉重新整理和上拉載入的基本原理,離真實專案開發用到還有很長一段距離要走呢。接下來我會整理一下這個Demo
然後給大家參考,如果大家有好的關於RecycleView的框架介紹也可以給我參考。

相關文章