Android——ListView的上拉分頁(yè)加載

前言

ListView的上拉分頁(yè)加載在很多app中都能看到昨凡,例如微信朋友圈铐然、QQ空間蔬崩、新聞列表等,因?yàn)閿?shù)據(jù)量很大搀暑,一次加載所有的數(shù)據(jù)將嚴(yán)重造成資源浪費(fèi)且影響用戶體驗(yàn)沥阳。所以如果每次只加載一部分的數(shù)據(jù),需要時(shí)再加載下一部分?jǐn)?shù)據(jù)自点,那么就可以很大程度上節(jié)約資源桐罕。

具體實(shí)現(xiàn)

(前排提示:熟悉使用ListView而只想了解分頁(yè)加載的朋友可直接空降第五部分)

一、數(shù)據(jù)源

  • 這里我的數(shù)據(jù)源來(lái)自 易源接口 的奇聞異事API
  • 通過(guò)谷歌自帶的HttpURLConnection獲取數(shù)據(jù)桂敛,這里我將數(shù)據(jù)獲取封裝成GetJsonObject方法功炮,返回值為JSONObject對(duì)象,代碼如下:
public class GetJsonObject {
    public static JSONObject getJsonObject(String url) {
        JSONObject mJsonObject = null;

        try {
            URL mURL = new URL(url);
            HttpURLConnection mHttpURLConnection = (HttpURLConnection) mURL.openConnection();
            mHttpURLConnection.setRequestMethod("GET");
            mHttpURLConnection.setReadTimeout(5000);
            mHttpURLConnection.setDoInput(true);

            StringBuffer mStringBuffer = new StringBuffer();
            String str;
            BufferedReader mBufferedReader = new BufferedReader(new InputStreamReader(mHttpURLConnection.getInputStream()));
            while ((str = mBufferedReader.readLine()) != null) {
                mStringBuffer.append(str);
            }

            //用數(shù)據(jù)源StringBuffer創(chuàng)建JSONObject對(duì)象方便后面解析json
            mJsonObject = new JSONObject(String.valueOf(mStringBuffer));
        } catch (JSONException | IOException e) {
            e.printStackTrace();
        }
        return mJsonObject;
    }
}
  • 解析json
    1术唬、創(chuàng)建Bean對(duì)象薪伏,用于數(shù)據(jù)封裝
public class NewsBean {
    public String news_ImageView;
    public String news_TextView;
}

2、創(chuàng)建用于存儲(chǔ)數(shù)據(jù)的List<NewsBean>

private List<NewsBean> mList = new ArrayList<>();

3粗仓、用谷歌自帶的JSONObject解析json并將數(shù)據(jù)填充到List<NewsBean>中

try {
        JSONObject mJSONObject = GetJsonObject.getJsonObject(url).getJSONObject("showapi_res_body");
        JSONArray mJSONArray = mJSONObject.getJSONArray("newslist");

        for (int i = 0; i < mJSONArray.length(); i++) {
            JSONObject mJSONObject1 = mJSONArray.getJSONObject(i);
            NewsBean mNewsBean = new NewsBean();
            mNewsBean.news_ImageView = mJSONObject1.getString("picUrl");
            mNewsBean.news_TextView = mJSONObject1.getString("title");
            mList.add(mNewsBean);
        }
    } catch (JSONException e) {
        e.printStackTrace();
}

二嫁怀、創(chuàng)建ListView的適配器

  • 新建一個(gè)MyListAdapter類(lèi)繼承BaseAdapter ,代碼如下:
public class MyListAdapter extends BaseAdapter {

    private List<NewsBean> mNewsBeanList;
    private LayoutInflater mLayoutInflater;
    private Context mContext;

    public void bindData(Context mContext, List<NewsBean> mNewsBeanList){
        this.mContext = mContext;
        this.mLayoutInflater = LayoutInflater.from(mContext);
        this.mNewsBeanList = mNewsBeanList;
    }

    @Override
    public int getCount() {
        return mNewsBeanList.size();
    }

    @Override
    public Object getItem(int position) {
        return mNewsBeanList.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder mViewHolder;
        if (convertView == null){
            mViewHolder = new ViewHolder();
            convertView = mLayoutInflater.inflate(R.layout.list_item, null);
            mViewHolder.mImageView = (ImageView) convertView.findViewById(R.id.image_item);
            mViewHolder.mTextView = (TextView) convertView.findViewById(R.id.text_item);
            convertView.setTag(mViewHolder);
        }else {
            mViewHolder = (ViewHolder) convertView.getTag();
        }

        NewsBean mNewsBean = mNewsBeanList.get(position);
        //使用了Glide框架加載圖片
        Glide.with(mContext).load(mNewsBean.news_ImageView).placeholder(R.mipmap.ic_launcher).into(mViewHolder.mImageView);
        mViewHolder.mTextView.setText(mNewsBean.news_TextView);

        return convertView;
    }

    class ViewHolder{
        public ImageView mImageView;
        public TextView mTextView;
    }
}

三借浊、加載數(shù)據(jù)

  • 由于網(wǎng)絡(luò)請(qǐng)求是耗時(shí)操作塘淑,事實(shí)上請(qǐng)求和解析都是在異步任務(wù)AsyncTask的doInBackground中方法完成的,而給ListView設(shè)置適配器則是在onPostExecute方法中完成的蚂斤。這里我在MainActivity中新建了一個(gè)內(nèi)部類(lèi)MyAsyncTask繼承AsyncTask存捺,代碼如下:
class MyAsyncTask extends AsyncTask<String, Void, List<NewsBean>> {

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
        }

        @Override
        protected List<NewsBean> doInBackground(String... params) {

            try {
                JSONObject mJSONObject = GetJsonObject.getJsonObject(params[0]).getJSONObject("showapi_res_body");
                JSONArray mJSONArray = mJSONObject.getJSONArray("newslist");

                for (int i = 0; i < mJSONArray.length(); i++) {
                    JSONObject mJSONObject1 = mJSONArray.getJSONObject(i);
                    NewsBean mNewsBean = new NewsBean();
                    mNewsBean.news_ImageView = mJSONObject1.getString("picUrl");
                    mNewsBean.news_TextView = mJSONObject1.getString("title");
                    mNewsBean.news_Url = mJSONObject1.getString("url");
                    mList.add(mNewsBean);
                }
            } catch (JSONException e) {
                e.printStackTrace();
            }

            return mList;
        }

        @Override
        protected void onPostExecute(List<NewsBean> strings) {
            super.onPostExecute(strings);
            mMyListAdapter.bindData(MainActivity.this, strings);
            //只有當(dāng)加載入第一頁(yè)時(shí)才設(shè)置一次適配器
            if (page == 1) {
                mListView.setAdapter(mMyListAdapter);
            }
            mMyListAdapter.notifyDataSetChanged();
            page++;

        }

        @Override
        protected void onProgressUpdate(Void... values) {
            super.onProgressUpdate(values);
        }
    }
  • 調(diào)用 MyAsyncTask的execute()方法加載數(shù)據(jù)


    Paste_Image.png
  • 效果如圖所示


    Paste_Image.png

五、分頁(yè)加載

  • 準(zhǔn)備了那么多終于到了本文的重點(diǎn)曙蒸,其實(shí)分頁(yè)加載的關(guān)鍵在于什么時(shí)候需要加載下一頁(yè)捌治?
    1、判斷ListView什么時(shí)候滑動(dòng)到了底部纽窟?方法是給ListView設(shè)置滾動(dòng)監(jiān)聽(tīng)具滴,并重寫(xiě)onScroll()方法,代碼如下:
mListView.setOnScrollListener(new AbsListView.OnScrollListener() {

            @Override
            public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
                isLast = (totalItemCount == firstVisibleItem + visibleItemCount);     //判斷是否滑到底部
            }
        });

其中
-isLast是個(gè)boolean變量师倔,用于判斷ListView是否滑動(dòng)到了底部
-totalItemCount 表示ListView總的的item數(shù)量
-firstVisibleItem 表示手機(jī)屏幕所顯示的第一個(gè)item是ListView中的第幾個(gè)item
-visibleItemCount表示手機(jī)屏幕可以看見(jiàn)的item的數(shù)量
所以當(dāng)totalItemCount == firstVisibleItem + visibleItemCount時(shí),就是滑動(dòng)到了底部
2、然而有時(shí)ListView是滑動(dòng)到了底部趋艘,但是我們的手指并沒(méi)有離開(kāi)屏幕疲恢,此時(shí)我們不想繼續(xù)看下面的內(nèi)容而往上滑動(dòng)了,這時(shí)就沒(méi)必要加載分頁(yè)了瓷胧,于是我們還需要判斷滾動(dòng)狀態(tài)是否為停止?jié)L動(dòng)了


Paste_Image.png

所以需要再重寫(xiě)onScrollStateChanged()方法显拳,于是完整的判斷代碼為:

mListView.setOnScrollListener(new AbsListView.OnScrollListener() {
            @Override
            public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
                isLast = (totalItemCount == firstVisibleItem + visibleItemCount);     //判斷是否滑倒底部

            }

            @Override
            public void onScrollStateChanged(AbsListView view, int scrollState) {
                //當(dāng)滑動(dòng)到底部 且 手指離開(kāi)屏幕時(shí) 確定為需要加載分頁(yè)
                if (isLast && scrollState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE) {
                    new MyAsyncTask().execute(URL_PATH1 + num + URL_PATH2 + page + URL_PATH3 + showapi_timestamp + URL_PATH4);
                }
            }
        });

再返回來(lái)看MyAsyncTask中的onPostExecute()方法


Paste_Image.png

其中

if (page == 1) {
    mListView.setAdapter(mMyListAdapter);
}
mMyListAdapter.notifyDataSetChanged();

保證了適配器不會(huì)被多次設(shè)置且加載分頁(yè)后頁(yè)面不會(huì)移動(dòng)到ListView的第一個(gè)item那邊,而 page++ 則使每次調(diào)用加載分頁(yè)時(shí)傳入的url的頁(yè)碼參數(shù)自增一搓萧。

后話

  • 關(guān)于網(wǎng)絡(luò)請(qǐng)求和解析json格式的數(shù)據(jù)杂数,推薦使用okHttp框架+Gson框架,更佳高效便捷瘸洛。
  • 關(guān)于ListView中加載網(wǎng)絡(luò)圖片揍移,強(qiáng)烈推薦使用Glide框架,原因是因?yàn)樵贚istView上加載圖片時(shí)會(huì)出現(xiàn)錯(cuò)誤的位置加載圖片而有圖片閃動(dòng)現(xiàn)象反肋,還有時(shí)我們需要看的是ListView后面的一點(diǎn)的數(shù)據(jù)那伐,所以滑動(dòng)時(shí)的圖片不需要加載,而只需加載最終我們等待加載的頁(yè)面的圖片石蔗,這些Glide框架都幫我們處理好了罕邀,不需要我們自己去實(shí)現(xiàn)這些代碼。
  • 完整代碼已上傳Github养距,如有疑問(wèn)或表述有誤的地方诉探,歡迎在評(píng)論區(qū)交流。
  • Github:https://github.com/TheCoffeeNoSugar/Novel
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末棍厌,一起剝皮案震驚了整個(gè)濱河市肾胯,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌定铜,老刑警劉巖阳液,帶你破解...
    沈念sama閱讀 217,657評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異揣炕,居然都是意外死亡帘皿,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén)畸陡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)鹰溜,“玉大人,你說(shuō)我怎么就攤上這事丁恭〔芏” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,057評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵牲览,是天一觀的道長(zhǎng)墓陈。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么贡必? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,509評(píng)論 1 293
  • 正文 為了忘掉前任兔港,我火速辦了婚禮,結(jié)果婚禮上仔拟,老公的妹妹穿的比我還像新娘衫樊。我一直安慰自己,他們只是感情好利花,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布科侈。 她就那樣靜靜地躺著,像睡著了一般炒事。 火紅的嫁衣襯著肌膚如雪臀栈。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,443評(píng)論 1 302
  • 那天羡洛,我揣著相機(jī)與錄音挂脑,去河邊找鬼。 笑死欲侮,一個(gè)胖子當(dāng)著我的面吹牛崭闲,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播威蕉,決...
    沈念sama閱讀 40,251評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼刁俭,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了韧涨?” 一聲冷哼從身側(cè)響起牍戚,我...
    開(kāi)封第一講書(shū)人閱讀 39,129評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎虑粥,沒(méi)想到半個(gè)月后如孝,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,561評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡娩贷,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評(píng)論 3 335
  • 正文 我和宋清朗相戀三年第晰,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片彬祖。...
    茶點(diǎn)故事閱讀 39,902評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡茁瘦,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出储笑,到底是詐尸還是另有隱情甜熔,我是刑警寧澤,帶...
    沈念sama閱讀 35,621評(píng)論 5 345
  • 正文 年R本政府宣布突倍,位于F島的核電站腔稀,受9級(jí)特大地震影響盆昙,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜烧颖,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評(píng)論 3 328
  • 文/蒙蒙 一弱左、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧炕淮,春花似錦、人聲如沸跳夭。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,838評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)币叹。三九已至润歉,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間颈抚,已是汗流浹背踩衩。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,971評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留贩汉,地道東北人驱富。 一個(gè)月前我還...
    沈念sama閱讀 48,025評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像匹舞,于是被迫代替她去往敵國(guó)和親褐鸥。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評(píng)論 2 354

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