原文链接找不到了,
效果如下:
代码如下
package net.example.listdemo;
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;
import android.widget.Toast;
public class MainActivity extends Activity implements OnScrollListener{
private ListView lv;//ListView
private int totalCount;// 数据总条数
private List<String> lists = new ArrayList<String>();//这是数据源
private ArrayAdapter<String> adapter;
private View view_more;
private ProgressBar pb;
private TextView tvLoad;
private int lastVisibleIndex;
int start=0;
int end=0;
//写个handler机制。处理问题
private Handler handler=new Handler(){
@Override
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
super.handleMessage(msg);
switch(msg.what){
case 0:
if(null==adapter){
adapter=new ArrayAdapter<>(MainActivity.this,android.R.layout.simple_list_item_1,lists);
lv.setAdapter(adapter);
lv.addFooterView(view_more);
//设置监听
setListeners();
}else{
adapter.notifyDataSetChanged();
}
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initViews();
initData();
}
/**
* 模拟网络请求数据,一次获取15条
*/
private void initData() {
// TODO Auto-generated method stub
new Thread(){
public void run() {
totalCount = 100;// 假设数据一共有100条,将来调接口可以获取到这个值
for(int i=0;i<15;i++){
lists.add("数据"+(i+1));
}
Message msg=new Message();
msg.what=0;
handler.sendMessage(msg);
};
}.start();
}
private void initData(final int start, final int end) {
// 模拟网络请求获取数据,一次获取15条
new Thread() {
public void run() {
try {
Thread.sleep(4000);// 模拟获取数据时的耗时3s
for (int i = start; i < end; i++) {
lists.add(i, "数据" + (i + 1));
}
// 给handler发消息更新UI,子线程不可以更新UI
Message message = new Message();
message.what = 0;
handler.sendMessage(message);
} catch (Exception e) {
e.printStackTrace();
}
};
}.start();
}
/**
* 初始化控件
*/
private void initViews() {
lv = (ListView) findViewById(R.id.lv);
// 构建底部加载布局
view_more = (View) getLayoutInflater()
.inflate(R.layout.view_more, null);
// 进度条
pb = (ProgressBar) view_more.findViewById(R.id.progressBar);
// “正在加载...”文本控件
tvLoad = (TextView) view_more.findViewById(R.id.tv_Load);
}
public void setListeners() {
if(totalCount>15){
lv.setOnScrollListener(this);
}else{
lv.removeFooterView(view_more);
}
};
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
// TODO Auto-generated method stub
Log.e("sunlei", "lastVisibleIndex = " + lastVisibleIndex);
Log.e("sunlei", "adapter.getCount() = " + adapter.getCount());
// 滑到底部后自动加载,判断listView已经停止滚动并且最后可视的条目等于adapter的条目
// 注意这里在listView设置好adpter后,加了一个底部加载布局。
// 所以判断条件为:lastVisibleIndex == adapter.getCount()
if (scrollState == SCROLL_STATE_IDLE
&& lastVisibleIndex == adapter.getCount()) {
/**
* 这里也要设置为可见,是因为当你真正从网络获取数据且获取失败的时候。
* 我在失败的方法里面,隐藏了底部的加载布局并提示用户加载失败。所以再次监听的时候需要
* 继续显示隐藏的控件。因为我模拟的获取数据,失败的情况这里不给出。实际中简单的加上几句代码就行了。
*/
pb.setVisibility(View.VISIBLE);
tvLoad.setVisibility(View.VISIBLE);
loadMoreData();// 加载更多数据
}
}
private void loadMoreData() {
// 获取此时adapter中的总条目数
int count = adapter.getCount();
// 一次加载15条数据,即下拉加载的执行
if (count +15< totalCount) {
start = count;
end = start + 15;
initData(start, end);// 模拟网络获取数据操作
} else {// 数据不足15条直接加载到结束
start = count;
end = totalCount;
initData(start, end);// 模拟网络获取数据曹祖
// 数据全部加载完成后,移除底部的view
lv.removeFooterView(view_more);
Toast.makeText(MainActivity.this, "数据已经全部加载", 1).show();
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
// TODO Auto-generated method stub
Log.d("sunlei","firstVisibleItem="+firstVisibleItem);
Log.d("sunlei","totalItemCount="+totalItemCount);
Log.d("sunlei"," visibleItemCount+"+visibleItemCount);
// 计算最后可见条目的索引
lastVisibleIndex = firstVisibleItem + visibleItemCount -1;
// 当adapter中的所有条目数已经和要加载的数据总条数相等时,则移除底部的View
if (totalItemCount == totalCount +1) {
// 移除底部的加载布局
lv.removeFooterView(view_more);
}
}
}
布局文件如下:主页布局
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<ListView
android:id="@+id/lv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
</RelativeLayout>
加载更多布局:
<?xml version="1.0" encoding="utf-8"?>
<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:text="@string/hasmore"
android:layout_centerInParent="true"
/>
<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>
这里面有些方法参数是需要额外了解的,如下:
假设一共有10条数据需要加载在ListView 展示。我们定义为 A 。还有个加载更多部分位于ListView底部,我们定义为B。
此时ListView由A和B俩部分组成。
文中 public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) 方法中参数解释如下:
firstVisibleItem:表示在当前屏幕显示的第一个listItem在整个listView里面的位置(下标从0开始);
visibleItemCount:表示在现时屏幕可以见到的ListItem(部分显示的ListItem也算)总数(注意如果B存在,那么B也是1个,包含B)
totalItemCount:表示的是当前ListView中数据的长度(注意如果B存在,那么B也是1个,包含B。所以应该是A+B)
adapter.getCount():表示的是当前ListView中数据的长度(注意如果B存在,那么B也是1个,不包含B。所以应该是A的长度,注意和上面的区别)