今天給大家講講android開發(fā)中比較常見的listView的下拉加載,其實(shí)也能夠叫做分頁(yè)加載。為何會(huì)有這個(gè)叫法呢?說說我的理解吧!
如上圖所示紅色方框的部份就是底部的加載布局。從1定程度上這樣做是必要的,為了優(yōu)化用戶的體驗(yàn)。你可以想一想,假設(shè)你點(diǎn)開微信的朋友圈的時(shí)候,如果沒有做分頁(yè)加載,那末你需要等待很久的時(shí)間才能夠看到你和朋友發(fā)的1些狀態(tài)。這是由于這個(gè)時(shí)候代碼需要給服務(wù)器發(fā)送要求獲得數(shù)據(jù)并顯現(xiàn)這些數(shù)據(jù)。這個(gè)時(shí)候獲得數(shù)據(jù)就成了最耗時(shí)的問題。為何這么說?如果沒有分頁(yè)加載的功能,那末,你肯定需要獲得全部的數(shù)據(jù),你可以想一想,好幾年的數(shù)據(jù),數(shù)據(jù)量可很多啊!發(fā)要求去獲得這些數(shù)據(jù)并顯現(xiàn)它們是會(huì)很耗時(shí)間的。所以分頁(yè)加載就很好的解決了這個(gè)問題。你先獲得其中的10幾條數(shù)據(jù)并顯現(xiàn)它們。當(dāng)你1直從頭閱讀這10幾條數(shù)據(jù)到結(jié)束的時(shí)候,這個(gè)時(shí)候再去加載更多的數(shù)據(jù)并顯現(xiàn)它們。如上圖所示,這樣才是好的用戶體驗(yàn)。
listView.addFooterView(view_more);// TODO 添加底部記載布局
再往listView的底部添加底部加載布局。最后對(duì)listView設(shè)置滑動(dòng)監(jiān)聽器:listView.setOnScrollListener(this);// TODO listView這是滑動(dòng)監(jiān)聽
并重寫相干方法: public void onScrollStateChanged(AbsListView view, int scrollState) {}
和 public void onScroll(AbsListView view, int firstVisibleItem,int visibleItemCount, int totalItemCount) {}
這兩個(gè)重寫的方法1個(gè)是監(jiān)聽listView的滑動(dòng)狀態(tài)改變,另外一個(gè)是監(jiān)聽listView的滑動(dòng)。listView.removeFooterView(view_more);// TODO 移除底部的加載布局
<?xml version="1.0" encoding="utf⑻"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="80dp"
android:background="#8888" >
<TextView
android:id="@+id/tv_Load"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="正在加載..."
android:textSize="14sp"
android:visibility="visible" />
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyleSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toLeftOf="@id/tv_Load"
android:padding="10dp"
android:visibility="visible" />
</RelativeLayout>
很簡(jiǎn)單的布局,顯示了1個(gè)進(jìn)度條和1個(gè)文本控件。如圖:
package com.example.drop_down_load;
import java.util.ArrayList;
import java.util.List;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
public class MainActivity extends Activity implements OnScrollListener {
private ListView listView;
private int totalCount;// 數(shù)據(jù)總條數(shù)
private List<String> lists = new ArrayList<String>();
private ArrayAdapter<String> adapter;
// 創(chuàng)建handler接收消息并處理消息
private Handler handler = new Handler() {
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case 0:
// 創(chuàng)建adapter
adapter = new ArrayAdapter<String>(MainActivity.this,
android.R.layout.simple_list_item_1, lists);
// 設(shè)置adapter
listView.setAdapter(adapter);
// 添加底部加載布局
listView.addFooterView(view_more);
// 設(shè)置監(jiān)聽
setListeners();
break;
}
};
};
private View view_more;
private ProgressBar pb;
private TextView tvLoad;
private int lastVisibleIndex;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 控件初始化
initViews();
// 初始化數(shù)據(jù)
initData();
}
private void initData() {
// 摹擬網(wǎng)絡(luò)要求獲得數(shù)據(jù),1次獲得15條
new Thread() {
public void run() {
try {
totalCount = 100;// 假定數(shù)據(jù)1共有100條,將來調(diào)接口可以獲得到這個(gè)值
for (int i = 0; i < 15; i++) {
lists.add("數(shù)據(jù)" + (i + 1));
}
// 給handler發(fā)消息更新UI,子線程不可以更新UI
Message message = new Message();
message.what = 0;
handler.sendMessage(message);
} catch (Exception e) {
e.printStackTrace();
}
};
}.start();
}
private void setListeners() {
if (totalCount > 15) {
// listView設(shè)置滑動(dòng)簡(jiǎn)監(jiān)聽
listView.setOnScrollListener(this);
} else {
// 假設(shè)數(shù)據(jù)總數(shù)少于等于15條,直接移除底部的加載布局,不需要再加載更多的數(shù)據(jù)
listView.removeFooterView(view_more);
}
}
private void initViews() {
listView = (ListView) findViewById(R.id.listView);
// 構(gòu)建底部加載布局
view_more = (View) getLayoutInflater()
.inflate(R.layout.view_more, null);
// 進(jìn)度條
pb = (ProgressBar) view_more.findViewById(R.id.progressBar);
// “正在加載...”文本控件
tvLoad = (TextView) view_more.findViewById(R.id.tv_Load);
}
/**
* 監(jiān)聽listView的滑動(dòng)狀態(tài)的改變
*/
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
Log.e("TAG", "lastVisibleIndex = " + lastVisibleIndex);
Log.e("TAG", "adapter.getCount() = " + adapter.getCount());
// 滑到底部后自動(dòng)加載,判斷l(xiāng)istView已停止轉(zhuǎn)動(dòng)并且最后可視的條目等于adapter的條目
// 注意這里在listView設(shè)置好adpter后,加了1個(gè)底部加載布局。
// 所以判斷條件為:lastVisibleIndex == adapter.getCount()
if (scrollState == SCROLL_STATE_IDLE
&& lastVisibleIndex == adapter.getCount()) {
/**
* 這里也要設(shè)置為可見,是由于當(dāng)你真正從網(wǎng)絡(luò)獲得數(shù)據(jù)且獲得失敗的時(shí)候。
* 我在失敗的方法里面,隱藏了底部的加載布局并提示用戶加載失敗。所以再次監(jiān)聽的時(shí)候需要
* 繼續(xù)顯示隱藏的控件。由于我摹擬的獲得數(shù)據(jù),失敗的情況這里不給出。實(shí)際中簡(jiǎn)單的加上幾句代碼就好了。
*/
pb.setVisibility(View.VISIBLE);
tvLoad.setVisibility(View.VISIBLE);
loadMoreData();// 加載更多數(shù)據(jù)
}
}
private void loadMoreData() {
}
/**
* 監(jiān)聽listView的滑動(dòng)
*/
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
// 計(jì)算最后可見條目的索引
lastVisibleIndex = firstVisibleItem + visibleItemCount - 1;
// 當(dāng)adapter中的所有條目數(shù)已和要加載的數(shù)據(jù)總條數(shù)相等時(shí),則移除底部的View
if (totalItemCount == totalCount + 1) {
// 移除底部的加載布局
listView.removeFooterView(view_more);
}
}
}
以上代碼就是我上面步驟2和步驟2中所說的東西。加上我寫了注釋,所以相信大家看起來很簡(jiǎn)單。關(guān)鍵點(diǎn)是怎樣判斷l(xiāng)istView是不是滑動(dòng)到了底部并停止了滑動(dòng)。我在代碼中添加了兩行l(wèi)og,可以打印日志信息。大家去自己調(diào)試就可以明白我對(duì)關(guān)鍵點(diǎn)是怎樣判斷的了,但是也有聰明的!嘿嘿!大家都懂,自己人,我就不接話了。如圖:
loadMoreData();// 加載更多數(shù)據(jù)
具體代碼實(shí)現(xiàn)為:
private void loadMoreData() {
// 獲得此時(shí)adapter中的總條目數(shù)
int count = adapter.getCount();
// 1次加載15條數(shù)據(jù),即下拉加載的履行
if (count + 15 < totalCount) {
start = count;
end = start + 15;
initData(start, end);// 摹擬網(wǎng)絡(luò)獲得數(shù)據(jù)操作
} else {// 數(shù)據(jù)不足15條直接加載到結(jié)束
start = count;
end = totalCount;
initData(start, end);// 摹擬網(wǎng)絡(luò)獲得數(shù)據(jù)曹祖
// 數(shù)據(jù)全部加載完成后,移除底部的view
listView.removeFooterView(view_more);
Toast.makeText(MainActivity.this, "數(shù)據(jù)已全部加載", 1).show();
}
}
private void initData(final int start, final int end) {
// 摹擬網(wǎng)絡(luò)要求獲得數(shù)據(jù),1次獲得15條
new Thread() {
public void run() {
try {
Thread.sleep(4000);// 摹擬獲得數(shù)據(jù)時(shí)的耗時(shí)3s
for (int i = start; i < end; i++) {
lists.add(i, "數(shù)據(jù)" + (i + 1));
}
// 給handler發(fā)消息更新UI,子線程不可以更新UI
Message message = new Message();
message.what = 1;
handler.sendMessage(message);
} catch (Exception e) {
e.printStackTrace();
}
};
}.start();
}
此時(shí)handler中還要再加1個(gè)case語句,用來刷新數(shù)據(jù)源,如圖紅色方框標(biāo)注地方,大家自己加1下。最后我會(huì)給項(xiàng)目源碼,大家可以下載看我的代碼:
好了下面給出幾張項(xiàng)目的展現(xiàn)圖片:
談?wù)勎业母形颍耗阈枰凶约旱南敕ㄋ悸罚说臇|西可以鑒戒,但是你需要從中學(xué)到點(diǎn)甚么。要是甚么都沒學(xué)到,那就失去了分享的意義了!分享是為了讓大家學(xué)到更多,收獲更多。希望我的這篇能夠給大家1點(diǎn)點(diǎn)收獲!
項(xiàng)目的源碼下載地址:點(diǎn)擊我下載項(xiàng)目源碼