[48→100]列表展示頁面模型:ListNetFragment

問題場(chǎng)景

上篇文章塘偎,我們討論了一個(gè)常規(guī)的Android頁面模型:NetFragment疗涉,完成了網(wǎng)絡(luò)加載的一系列邏輯拿霉,那么更常見的一類網(wǎng)絡(luò)數(shù)據(jù)加載頁面是列表數(shù)據(jù)展示頁。比如簡(jiǎn)書的專題展示頁:

列表展示頁

對(duì)于這類頁面咱扣,變化的部分往往只有以下幾個(gè):

  1. 加載后臺(tái)數(shù)據(jù)绽淘。
  2. 和數(shù)據(jù)的綁定的界面是什么。
  3. 數(shù)據(jù)和界面怎么綁定闹伪。

不需要的變化的邏輯部分為:

  1. 頁面滑到頂部時(shí)沪铭,下拉刷新整體數(shù)據(jù)壮池;
  2. 頁面滑到底部時(shí),上拉加載下一頁數(shù)據(jù)杀怠。
  3. 數(shù)據(jù)都加載完了椰憋,再次上拉界面,執(zhí)行數(shù)據(jù)加載完成的接口赔退。

怎么實(shí)現(xiàn)呢橙依?

一、設(shè)計(jì)供上層實(shí)現(xiàn)的抽象接口:

/**
 * 加載list數(shù)據(jù)
 *
 * @param startIndex 起始數(shù)據(jù)位置
 * @param pageSize   預(yù)期加載多少個(gè)數(shù)據(jù)
 * @return
 */
protected abstract ListNetResultInfo<O> onDoInBackgroundSafely(int startIndex, int pageSize);

/**
 * 獲取對(duì)應(yīng)Item布局的id
 * @return
 */
public abstract int getItemLayoutId();

/**
 * 將view和數(shù)據(jù)綁定
 * @param position
 * @param view
 * @param parent
 * @return
 */
public abstract View bindView(int position, View view, ViewGroup parent);

/**
 * 獲取item布局中一個(gè)textview的id (這是因?yàn)榈讓硬捎肁rrayAdapter硕旗,所以初始化時(shí)需要用到一個(gè)textview的id)
 * @return
 */
public abstract int getItemTextViewResourceId();

二窗骑、 選用上拉、下拉控件

這里選擇使用開源組件BGARefreshLayout
同樣采用了約定id名的方式來進(jìn)行集成漆枚,

  • ListView界面 R.id.net_listview

所以界面如下:

<?xml version="1.0" encoding="utf-8"?>
<!-- 下拉刷新创译、上拉加載下一頁樣例 -->
<cn.bingoogolapple.refreshlayout.BGARefreshLayout xmlns:tools="http://schemas.android.com/tools"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/net_result"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ListView
        android:id="@+id/net_list"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginLeft="@dimen/width_720_1280_20"
        android:layout_marginRight="@dimen/width_720_1280_20"
        android:clipToPadding="false"
        android:divider="#EEEEEE"
        android:dividerHeight="@dimen/height_720_1280_26"
        android:paddingTop="@dimen/height_720_1280_25"></ListView>
</cn.bingoogolapple.refreshlayout.BGARefreshLayout>

相關(guān)的配置代碼如下:

protected void configRefreshLayout() {
    mRefreshLayout = (BGARefreshLayout) mViewResult;
    if (mRefreshLayout == null) {
        return;
    }
    mListView = (ListView) mViewResult.findViewById(R.id.net_list);

    // 設(shè)置下拉刷新和上拉加載更多的風(fēng)格     參數(shù)1:應(yīng)用程序上下文,參數(shù)2:是否具有上拉加載更多功能
    BGARefreshViewHolder refreshViewHolder = new BGANormalRefreshViewHolder(getActivity(), true);
    // 設(shè)置下拉刷新和上拉加載更多的風(fēng)格
    mRefreshLayout.setRefreshViewHolder(refreshViewHolder);
    mRefreshLayout.setDelegate(new BGARefreshLayout.BGARefreshLayoutDelegate() {
        @Override
        public void onBGARefreshLayoutBeginRefreshing(BGARefreshLayout refreshLayout) {
            useSecondModel = false;
            loadNetData(useSecondModel);
        }

        @Override
        public boolean onBGARefreshLayoutBeginLoadingMore(BGARefreshLayout refreshLayout) {
            if (isLoadedAllNetData){
                onLoadAllNetData();
                return false;
            }
            loadNextPageNetData();
            return true;
        }

    });
  }

三墙基、基于NetFragment封裝相應(yīng)的邏輯

  1. 記錄加載數(shù)據(jù)的個(gè)數(shù)和位置软族;
  2. 透過ArrayAdapter將數(shù)據(jù)和Listview綁定。

最后完成的類文件如下:

package lib;

import android.content.Context;
import android.os.Bundle;
import android.os.Parcelable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.ArrayAdapter;
import android.widget.ListView;

import com.freetek.deepsea.R;

import java.util.ArrayList;
import java.util.List;

import cn.bingoogolapple.refreshlayout.BGAMoocStyleRefreshViewHolder;
import cn.bingoogolapple.refreshlayout.BGANormalRefreshViewHolder;
import cn.bingoogolapple.refreshlayout.BGARefreshLayout;
import cn.bingoogolapple.refreshlayout.BGARefreshViewHolder;
import panda.android.lib.base.model.ListNetResultInfo;
import panda.android.lib.base.util.Log;


/**
 * 加載網(wǎng)絡(luò)List數(shù)據(jù)的通用模型残制。
 *
 * @author shitianci
 */
public abstract class ListNetFragment<O> extends NetFragment<ListNetResultInfo<O>> {

    private static final String TAG = panda.android.lib.base.ui.fragment.ListNetFragment.class.getSimpleName();
    private int mStartIndex = 0; //開始數(shù)據(jù)
    private int mPageSize = 10;  //起始頁的數(shù)據(jù)
    private ArrayList<O> mAllDataList = new ArrayList<O>();
    private boolean isLoadedAllNetData = false;
    protected Parcelable mViewResultState = null;
    private boolean useSecondModel = false;
    private BGARefreshLayout mRefreshLayout;
    private DataAdapter dataAdapter;
    private ListView mListView;

    /**
     * -------------------------
     * START: 最重要的流程方法
     * -------------------------
     */

    /**
     * 加載list數(shù)據(jù)
     *
     * @param startIndex 起始數(shù)據(jù)項(xiàng)
     * @param pageSize   預(yù)期加載多少項(xiàng)
     * @return
     */
    protected abstract ListNetResultInfo<O> onDoInBackgroundSafely(int startIndex, int pageSize);


    /**
     * 獲取item布局中一個(gè)textview的id
     * @return
     */
    public abstract int getItemTextViewResourceId();

    /**
     * 獲取對(duì)應(yīng)Item布局的id
     * @return
     */
    public abstract int getItemLayoutId();

    /**
     * 將view和數(shù)據(jù)綁定
     * @param position
     * @param view
     * @param parent
     * @return
     */
    public abstract View bindView(int position, View view, ViewGroup parent);


    /**
     * -------------------------
     * END
     * -------------------------
     */

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View rootView = super.onCreateView(inflater, container, savedInstanceState);
        if (mViewResult instanceof BGARefreshLayout) {
            configRefreshLayout();
        } else {
            Log.e(TAG, "沒有找到BGARefreshLayout");
        }
        return rootView;
    }


    protected void configRefreshLayout() {
        mRefreshLayout = (BGARefreshLayout) mViewResult;
        if (mRefreshLayout == null) {
            return;
        }
        mListView = (ListView) mViewResult.findViewById(R.id.net_list);

        // 設(shè)置下拉刷新和上拉加載更多的風(fēng)格     參數(shù)1:應(yīng)用程序上下文互订,參數(shù)2:是否具有上拉加載更多功能
        BGARefreshViewHolder refreshViewHolder = new BGANormalRefreshViewHolder(getActivity(), true);
        // 設(shè)置下拉刷新和上拉加載更多的風(fēng)格
        mRefreshLayout.setRefreshViewHolder(refreshViewHolder);
        mRefreshLayout.setDelegate(new BGARefreshLayout.BGARefreshLayoutDelegate() {
            @Override
            public void onBGARefreshLayoutBeginRefreshing(BGARefreshLayout refreshLayout) {
                useSecondModel = false;
                loadNetData(useSecondModel);
            }

            @Override
            public boolean onBGARefreshLayoutBeginLoadingMore(BGARefreshLayout refreshLayout) {
                if (isLoadedAllNetData){
                    onLoadAllNetData();
                    return false;
                }
                loadNextPageNetData();
                return true;
            }

        });
    }

    /**
     * 顯示list數(shù)據(jù)
     *
     * @param list 最近一次交互獲取的數(shù)據(jù)
     */
    private void displayResult(List<O> list){
        Log.d(TAG, "displayResult: " + list.toString());
        if (dataAdapter == null) {
            dataAdapter = new DataAdapter(getActivity());
            mListView.setAdapter(dataAdapter);
        }
        android.util.Log.d(TAG, "displayResult: " + list.toString());
        for (int i = 0; i < list.size(); i++) {
            dataAdapter.add(list.get(i));
        }
        dataAdapter.notifyDataSetChanged();
    }

    //不同的訂單的適配器
    public class DataAdapter extends ArrayAdapter<O> {

        public DataAdapter(Context context) {
            super(context, getItemLayoutId(), getItemTextViewResourceId());
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            return bindView(position, super.getView(position, convertView, parent), parent);
        }


    }

    public O getItem(int position) {
        if(dataAdapter == null){
            Log.d(TAG, "dataAdapter 沒有初始化");
            return  null;
        }
        return dataAdapter.getItem(position);
    }


    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        mRefreshLayout.beginRefreshing();
    }

    @Override
    public void onPreloadNetData(boolean useSecondModel) {
        super.onPreloadNetData(useSecondModel);
        if (!useSecondModel){
            mStartIndex = 0;
            mAllDataList.clear();
            isLoadedAllNetData = false;
        }
        mRefreshLayout.setVisibility(View.VISIBLE);
        //保存位置狀態(tài)
        if (mViewResult != null) {
            if (mViewResult instanceof AbsListView) {
                mViewResultState = ((AbsListView) mViewResult.findViewById(R.id.net_list)).onSaveInstanceState();
                Log.d(TAG, "onSaveInstanceState, mViewResultState = " + mViewResultState);
            }
        }
    }

    @Override
    public void onPostloadNetData(boolean useSecondModel) {
        super.onPostloadNetData(useSecondModel);
        if(useSecondModel){
            mRefreshLayout.endLoadingMore();
        }else{
            mRefreshLayout.endRefreshing();
        }
    }

    /**
     * 加載下一頁數(shù)據(jù)
     */
    public void loadNextPageNetData() {
        Log.d(TAG, "loadNextPageNetData, isLoadedAllNetData = " + isLoadedAllNetData);
        if (mStartIndex != mAllDataList.size()) {
            Log.w(TAG, "mStartIndex = " + mStartIndex);
            Log.w(TAG, "mAllDataList.size() = " + mAllDataList.size());
            mStartIndex = mAllDataList.size();
        }
        useSecondModel = true;
        super.loadNetData(useSecondModel);
    }


    /**
     * 所有數(shù)據(jù)加載完畢
     */
    public void onLoadAllNetData() {
        Log.d(TAG, "所有數(shù)據(jù)加載完畢");
    }

    @Override
    protected ListNetResultInfo<O> onDoInBackgroundSafely() {
        try {
            Thread.sleep(1000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return onDoInBackgroundSafely(mStartIndex, mPageSize);
    }

    final protected void onDisplayResult(ListNetResultInfo<O> result) {
        if (result.getList() == null || result.getList().size() == 0) {
            showOnlyView(R.id.net_no_result, useSecondModel);
            return;
        }
        if (result.getList().size() < mPageSize) {
            Log.w(TAG, "onLoadAllNetData");
            isLoadedAllNetData = true;
            onLoadAllNetData();
        }
        List<O> list = result.getList();
        for (O o : list) {
            mAllDataList.add(o);
        }
        mStartIndex += list.size();
        displayResult(list);
        //恢復(fù)原來的位置狀態(tài)
        if (mViewResult != null) {
            if (mViewResult instanceof AbsListView) {
                Log.d(TAG, "onRestoreInstanceState, mViewResultState = " + mViewResultState);
                ((AbsListView) mViewResult).onRestoreInstanceState(mViewResultState);
            }
        }
    }


    /**
     * 獲取每一頁數(shù)據(jù)的大小
     *
     * @return
     */
    public int getPageSize() {
        return mPageSize;
    }

    /**
     * 設(shè)置每一頁數(shù)據(jù)的大小
     *
     * @param mPageSize
     */
    public void setPageSize(int mPageSize) {
        this.mPageSize = mPageSize;
    }

    /**
     * 獲取下一次加載的其實(shí)位置
     *
     * @return
     */
    public int getStartIndex() {
        return mStartIndex;
    }

    /**
     * 設(shè)置下一次加載的其實(shí)位置
     *
     * @param mStartIndex
     */
    public void setStartIndex(int mStartIndex) {
        this.mStartIndex = mStartIndex;
    }

    /**
     * @return the mAllDataList
     */
    public ArrayList<O> getAllDataList() {
        return mAllDataList;
    }

    /**
     * @param mAllDataList the mAllDataList to set
     */
    public void setAllDataList(ArrayList<O> mAllDataList) {
        this.mAllDataList = mAllDataList;
    }

}

上層怎么使用呢?

  1. 界面里面增加BGARefreshLayout和ListView痘拆;
<?xml version="1.0" encoding="utf-8"?>
<!-- 下拉刷新仰禽、上拉加載下一頁樣例 -->
<cn.bingoogolapple.refreshlayout.BGARefreshLayout xmlns:tools="http://schemas.android.com/tools"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/net_result"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ListView
        android:id="@+id/net_list"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginLeft="@dimen/width_720_1280_20"
        android:layout_marginRight="@dimen/width_720_1280_20"
        android:clipToPadding="false"
        android:divider="#EEEEEE"
        android:dividerHeight="@dimen/height_720_1280_26"
        android:paddingTop="@dimen/height_720_1280_25"></ListView>
</cn.bingoogolapple.refreshlayout.BGARefreshLayout>
  1. 頁面的Fragment繼承ListNetFragment,實(shí)現(xiàn)四個(gè)抽象方法即可纺蛆。

小小吐槽一下~

BGARefreshLayout這個(gè)組件有一個(gè)open的bug——正在刷新或加載更多時(shí)吐葵,用戶上下滑動(dòng)不會(huì)讓下拉刷新視圖和加載更多視圖跟著滑動(dòng),效果有點(diǎn)挫桥氏,需要優(yōu)化温峭。

Panda
2016-06-12

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市字支,隨后出現(xiàn)的幾起案子凤藏,更是在濱河造成了極大的恐慌,老刑警劉巖堕伪,帶你破解...
    沈念sama閱讀 211,639評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件揖庄,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡欠雌,警方通過查閱死者的電腦和手機(jī)蹄梢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,277評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來富俄,“玉大人禁炒,你說我怎么就攤上這事而咆。” “怎么了幕袱?”我有些...
    開封第一講書人閱讀 157,221評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵暴备,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我们豌,道長(zhǎng)馍驯,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,474評(píng)論 1 283
  • 正文 為了忘掉前任玛痊,我火速辦了婚禮汰瘫,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘擂煞。我一直安慰自己混弥,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,570評(píng)論 6 386
  • 文/花漫 我一把揭開白布对省。 她就那樣靜靜地躺著蝗拿,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蒿涎。 梳的紋絲不亂的頭發(fā)上哀托,一...
    開封第一講書人閱讀 49,816評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音劳秋,去河邊找鬼仓手。 笑死,一個(gè)胖子當(dāng)著我的面吹牛玻淑,可吹牛的內(nèi)容都是我干的嗽冒。 我是一名探鬼主播,決...
    沈念sama閱讀 38,957評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼补履,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼添坊!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起箫锤,我...
    開封第一講書人閱讀 37,718評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤贬蛙,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后谚攒,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體阳准,經(jīng)...
    沈念sama閱讀 44,176評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,511評(píng)論 2 327
  • 正文 我和宋清朗相戀三年五鲫,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了溺职。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,646評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡位喂,死狀恐怖浪耘,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情塑崖,我是刑警寧澤七冲,帶...
    沈念sama閱讀 34,322評(píng)論 4 330
  • 正文 年R本政府宣布,位于F島的核電站规婆,受9級(jí)特大地震影響澜躺,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜抒蚜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,934評(píng)論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望嗡髓。 院中可真熱鬧操漠,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,755評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至声功,卻和暖如春烦却,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背先巴。 一陣腳步聲響...
    開封第一講書人閱讀 31,987評(píng)論 1 266
  • 我被黑心中介騙來泰國(guó)打工其爵, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人伸蚯。 一個(gè)月前我還...
    沈念sama閱讀 46,358評(píng)論 2 360
  • 正文 我出身青樓摩渺,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親剂邮。 傳聞我的和親對(duì)象是個(gè)殘疾皇子摇幻,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,514評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容