1. 程式人生 > >SwipeRefreshLayout完美實現ListView網路資料上拉載入以及下拉重新整理

SwipeRefreshLayout完美實現ListView網路資料上拉載入以及下拉重新整理

最近才有時間將上拉載入與下拉重新整理進行總結,SwipeRefreshLayout是Android -V4包下自帶的一個重新整理控制元件,但是不足的是它只有下拉重新整理的功能,沒有需要的上拉載入功能,所以我自己添加了上拉載入的功能。

一、首先簡單介紹一下SwipeRefreshLayout的。

這個是我在網上找的一個官網的介紹,在豎直滑動時想要重新整理頁面可以用SwipeRefreshLayout來實現。其實它也就是我們在使用列表的時候需要的上拉載入與下拉重新整理的實現,它通過設定OnRefreshListener來監聽介面的滑動從而實現重新整理。也可以通過一些方法來設定SwipeRefreshLayout是否可以重新整理。如:setRefreshing(true),展開重新整理動畫;setRefreshing(false),取消重新整理動畫;setEnable(true)下拉重新整理將不可用。注意的是,它只可以有一個子控制元件,一般使用的是ListView。

常用到的方法有以下幾個:

  • isRefreshing()

    • 判斷當前的狀態是否是重新整理狀態。
  • setColorSchemeResources(int... colorResIds)

    • 設定下拉進度條的顏色主題,引數為可變引數,並且是資源id,可以設定多種不同的顏色,每轉一圈就顯示一種顏色。
  • setOnRefreshListener(SwipeRefreshLayout.OnRefreshListener listener)

    • 設定監聽,需要重寫onRefresh()方法,頂部下拉時會呼叫這個方法,在裡面實現請求資料的邏輯,設定下拉進度條消失等等。
  • setProgressBackgroundColorSchemeResource(int colorRes)

    • 設定下拉進度條的背景顏色,預設白色。
  • setRefreshing(boolean refreshing)

    • 設定重新整理狀態,true表示正在重新整理,false表示取消重新整理。
二、SwipeRefreshLayout上拉載入的具體實現

1、首先我們寫一個類RefreshView繼承自SwipeRefreshLayout,在它的onLayout方法中獲取ListView佈局,如下:

// 獲取ListView,設定ListView的佈局位置
if (mListView == null) {
    // 判斷容器有多少個孩子
if (getChildCount() > 0) {
        // 判斷第一個孩子是不是ListView
if (getChildAt(0) instanceof ListView) { // 建立ListView物件 mListView = (ListView) getChildAt(0); // 設定ListView的滑動監聽 setListViewOnScroll(); } } }

2、在複寫的程式碼下新增如下程式碼,獲取底部載入的佈局

// 填充底部載入佈局
mFooterView = View.inflate(context, R.layout.view_foot, null);
// 表示控制元件移動的最小距離,手移動的距離大於這個距離才能拖動控制元件
mScaledTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
System.out.println("====" + mScaledTouchSlop);

3、需要判斷上拉與下拉。所以需要設定ListView的滑動

 /**
     * 設定ListView的滑動監聽
     */
private void setListViewOnScroll() {

        mListView.setOnScrollListener(new AbsListView.OnScrollListener() {
            @Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
                // 移動過程中判斷時候能下拉載入更多
if (canLoadMore()) {
                    // 載入資料
loadData();
                }
            }

            @Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {

            }
        });
    }


4、當用戶發生行為的時候,對子控制元件進行觸控判斷,判斷是否滿足載入更多的條件,處理載入資料邏輯關係,

 /**
     * 在分發事件的時候處理子控制元件的觸控事件
     *
     * @param ev
* @return
*/
private float mDownY, mUpY;

    @Override
public boolean dispatchTouchEvent(MotionEvent ev) {

        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                // 移動的起點
mDownY = ev.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                // 移動過程中判斷時候能下拉載入更多
if (canLoadMore()) {
                    // 載入資料
loadData();
                }

                break;
            case MotionEvent.ACTION_UP:
                // 移動的終點
mUpY = getY();
                break;
        }
        return super.dispatchTouchEvent(ev);
    }

private boolean canLoadMore() {
        // 1. 是上拉狀態
boolean condition1 = (mDownY - mUpY) >= mScaledTouchSlop;
        if (condition1) {
            System.out.println("是上拉狀態");
        }

        // 2. 當前頁面可見的item是最後一個條目
boolean condition2 = false;
        if (mListView != null && mListView.getAdapter() != null) {
            condition2 = mListView.getLastVisiblePosition() == (mListView.getAdapter().getCount() - 1);
        }

        if (condition2) {
//            i = i++;
Log.d("RefreshView", "是最後一個條目");
        }
        // 3. 正在載入狀態
boolean condition3 = !isLoading;
        if (condition3) {
            System.out.println("不是正在載入狀態");
        }
        return condition1 && condition2 && condition3;
}    

private void loadData() {
        System.out.println("載入資料...");
        if (mOnLoadListener != null) {
            // 設定載入狀態,讓佈局顯示出來
setLoading(true);
            mOnLoadListener.onLoad();
        }

    }



 /**
     * 設定載入狀態,是否載入傳入boolean值進行判斷
     *
     * @param loading
*/
public void setLoading(boolean loading) {
        // 修改當前的狀態
isLoading = loading;
        if (isLoading) {
            // 顯示佈局
mListView.addFooterView(mFooterView);
            DataValue.JIGONG_REFSH_I = i++;
            Log.d("RefreshView", "DataValue.JIGONG_REFSH_I:" + DataValue.JIGONG_REFSH_I);
        } else {
            // 隱藏佈局
mListView.removeFooterView(mFooterView);

            // 重置滑動的座標
mDownY = 0;
            mUpY = 0;
        }
    }

    /**
     * 上拉載入的介面回撥
     */
public interface OnLoadListener {
        void onLoad();
    }

    public void setOnLoadListener(OnLoadListener listener) {
        this.mOnLoadListener = listener;
    }

5、在xml佈局檔案中的使用

 <com.jinyuankeji.yxm.findhuo.findwork.hot_type.hot_type_detail.RefreshView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/swip_jigong">

    <ListView
android:id="@+id/lv"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
    </com.jinyuankeji.yxm.findhuo.findwork.hot_type.hot_type_detail.RefreshView>
將RefreshView作為一個控制元件,其中包含子控制元件ListView

6、在java程式碼中的具體使用

在ListView對應的檔案中,呼叫.setOnLoadListener()的方法對滑動重新整理進行監聽,

定義

RefreshView mSwipePull;進行定義,然後進行佈局繫結
mSwipePull.setOnLoadListener(new RefreshView.OnLoadListener() {
    @Override
public void onLoad() {
        new Handler().postDelayed(new Runnable() {
            @Override
public void run() {

此處為新增重新整理的資料
// requestLode();
              //  if (mNewBean.getRes() == 10002) {
               //     Toast.makeText(FindWorkHotTypeDetailActivity.this, "沒有更多了", Toast.LENGTH_SHORT).show();
               // } else {
               //     Toast.makeText(FindWorkHotTypeDetailActivity.this, "載入了" + mNewBean.getData().size() + "條資料", Toast.LENGTH_SHORT).show();
                //}
                // 載入完資料設定為不載入狀態,將載入進度收起來
mSwipePull.setLoading(false);
            }
        }, 1200);
    }
});
//        mSwipePull.setProgressBackgroundColorSchemeResource(android.R.color.white);
        // 設定下拉進度的主題顏色
//        mSwipePull.setColorSchemeResources(R.color.colorAccent, R.color.colorPrimary, R.color.colorPrimaryDark);

特別記錄一下,如果是post資料的型別,在上傳引數的時候如果需要傳遞page數,可以通過判斷向上拉載入或者下拉重新整理的頭Or腳佈局的出現次數來進行次數i的++,即i++;我是在ListView新增腳佈局的裡面新增的i++;然後在網路解析的引數中將i上傳就行。

public void setLoading(boolean loading) {
    // 修改當前的狀態
isLoading = loading;
    if (isLoading) {
        // 顯示佈局
mListView.addFooterView(mFooterView);
        DataValue.JIGONG_REFSH_I = i++;
        Log.d("RefreshView", "DataValue.JIGONG_REFSH_I:" + DataValue.JIGONG_REFSH_I);
    } else {
        // 隱藏佈局
mListView.removeFooterView(mFooterView);

        // 重置滑動的座標
mDownY = 0;
        mUpY = 0;
    }
}

三、SwipeRefreshLayout下拉重新整理的實現

// 下拉時觸發SwipeRefreshLayout的下拉動畫,動畫完畢之後就會回撥這個方法
swipeRefreshView.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
    @Override
    public void onRefresh() {

        // 開始重新整理,設定當前為重新整理狀態
        //swipeRefreshLayout.setRefreshing(true);
        // 這裡是主執行緒
        // 一些比較耗時的操作,比如聯網獲取資料,需要放到子執行緒去執行
        // TODO 獲取資料
final Random random = new Random();
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                mList.add(0, "我是天才" + random.nextInt(100) + "號");
                mAdapter.notifyDataSetChanged();

                Toast.makeText(MainActivity.this, "重新整理了一條資料", Toast.LENGTH_SHORT).show();

                // 載入完資料設定為不重新整理狀態,將下拉進度收起來
swipeRefreshView.setRefreshing(false);
            }
        }, 1200);

        // System.out.println(Thread.currentThread().getName());
        // 這個不能寫在外邊,不然會直接收起來
        //swipeRefreshLayout.setRefreshing(false);
}
});

如果想要剛進入頁面就是重新整理的狀態,可以設定.setRefreshing(true)

// 不能在onCreate中設定,這個表示當前是重新整理狀態,如果一進來就是重新整理狀態,SwipeRefreshLayout會遮蔽掉下拉事件
//swipeRefreshLayout.setRefreshing(true);

ListView的介面卡設定和往常的一樣就可以。

四、item的點選時候,使用的實體類與最初獲取的資料的實體類是一個,在載入重新整理資料的時候,重新定義一個實體類

寫的不是很詳細,如果有什麼不對的不足之處還請大家多多幫忙指出,大家有什麼疑問歡迎提問,期待與大家一起進步溝通。