***************************************
在经过几年的经验累积之后,我终于决定整理一下曾经遇到的各种问题,给各位走在android开发路上的朋友一点帮助,更多相关问题,请访问我的博客:http://blog.youkuaiyun.com/xiaoliluote 如果您对该问题有更多的解决方式,请留言,验证之后我会编辑博客
***************************************
我们会发现,有时我们的listview 需要加载很多的数据,如果数据量足够大的时候,就可能会让这个加载数据变得非常慢,即使是使用新线程来加载数据,也会等待较长的时间, 于是下拉刷新/ 上拉刷新的功能就出现了,每次可能只显示10条或者20条数据,然后当用户下拉/上拉 到最后一条/第一条 数据,就 再次加载10条/20条数据,这样可以大大提升APP的反应速度,非常赞!
这里楼主以下拉刷新为例,给大家演示,上拉刷新的原理和下拉刷新的原理一致,大家可以举一反三,达到更好的学习效果
进入主题,下拉刷新是应用在listview中的,最简单的使用listview,我们使用系统的SimpleAdapter就可以了,xml 的界面呢,也只需要直接使用<ListView>即可, 但是由于我们需要监听用户的手势(下拉滚动),那么我们就需要自定义listview 的控件,
这里楼主提示一点:对于有些可以重复利用的代码,我们要有意识的整理出来,尽量的提高代码的可重复调用性,不要将类似的代码都写一次,那样会使得代码臃肿,也不太好维护。
这里楼主创建了一个类名为PaginationListView 的文件,代码如下:
public class PaginationListView extends ListView implements OnScrollListener{
private View footerView; //定义下拉到底部时,给用户看什么东西,一般是一个图片+ 提示的文字
int totalItemCount = 0;
int lastVisibleItem = 0;
boolean isLoading = false;
public PaginationListView(Context context) {
super(context);
initView(context);
}
public PaginationListView(Context context, AttributeSet attrs) {
super(context, attrs);
initView(context);
}
public PaginationListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initView(context);
}
private void initView(Context context){
LayoutInflater mInflater = LayoutInflater.from(context);
footerView = mInflater.inflate(R.layout.footer, null);
footerView.setVisibility(View.GONE); //数据初始时,下拉的提示是隐藏的
this.setOnScrollListener(this); //监听滚动方法
this.addFooterView(footerView);
}
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
if(lastVisibleItem == totalItemCount && scrollState == SCROLL_STATE_IDLE){
if(!isLoading){
isLoading = true;
footerView.setVisibility(View.VISIBLE);
onLoadListener.onLoad();
}
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
this.lastVisibleItem = firstVisibleItem + visibleItemCount;
this.totalItemCount = totalItemCount;
}
private OnLoadListener onLoadListener; //定义一个回调函数,用于返回到加载数据的类,去更新数据
public void setOnLoadListener(OnLoadListener onLoadListener){
this.onLoadListener = onLoadListener;
}
public interface OnLoadListener{
void onLoad();
}
//当数据加载完毕之后,那么就要再次隐藏提示的底部文件
public void loadComplete(){
footerView.setVisibility(View.GONE);
isLoading = false;
this.invalidate();
}
}
这里提供一下我的底部提示文件 footer.xml 各位可以根据自己的需要自行补脑,做出更好看的效果
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/footer_ll"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal" >
<ProgressBar
android:id="@+id/progress"
style="?android:attr/progressBarStyleSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="30dp"
android:layout_marginTop="30dp" />
<MY_extend.my_textview
android:id="@+id/tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:text="pleast wait while load data..."
android:textColor="#FF0000" />
</LinearLayout>
然后呢,我们的界面,就不要再用 <ListView> 啦,改用我们上面的这个
<PaginationListView
android:id="@+id/dyn_browse_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="3dp"
android:paddingRight="3dp" >
</PaginationListView>
接下来回到我们的activity 中,首先,这个 activity 需要实现 OnLoadListener监听
public class Act_Contact_Browse extends Activity implements OnLoadListener{}
会实现一个方法
@Override
public void onLoad() {}
然后获取到界面上的ListView
PaginationListView contact_list = (PaginationListView) findViewById(R.id.dyn_browse_list);
设置监听
contact_list.setOnLoadListener(this);
接下来就可以设定值到我们的 适配器(也许你使用的就是最简单的SimpleAdapter),这里楼主是自己定义的适配器
不过这个无关紧要,都一样,然后呢,我们需要设定全局变量
public int paginationCode = 10; //设定每页显示多少条数据
public ArrayList<HashMap<String, Object>> paginationList; //显示到界面上的数据
public ArrayList<HashMap<String, Object>> _rawData; // 原始的数据, 因为原始的数据可能有100条,但是每次只取10条到界面,所以用了两个参数
然后楼主写了一个方法来加载这些数据,这个方法呢,也许有点复杂,你看不明白,不过没关系,拿过来直接用就可以了,有时间的就可以仔细看一下逻辑。 因为我们要判断几个条件,比如:
1.如果总数据一共不足10条,那么就不会出现滚动的监听,底部的提示也不会出现
2.如果10< 数据 <20 ,那么第一次能滚动,第二次就不会再出现那滚动的提示了
private void LoadPaginationData() {
if (paginationCode == 10) {
if (_rawData.size() > 10) {
for (int i = 0; i < paginationCode; i++) {
paginationList.add(_rawData.get(i));
}
} else {
for (int i = 0; i < _rawData.size(); i++) {
paginationList.add(_rawData.get(i));
}
}
if (paginationList.size() != 0) {
if (_rawData.size() > paginationCode + 10)
paginationCode += 10;
else if (_rawData.size() > 10)
paginationCode = paginationCode + _rawData.size() - paginationCode;
}
} else {
if (_rawData.size() > paginationCode + 10) {
for (int i = paginationCode; i < paginationCode + 10; i++) {
paginationList.add(_rawData.get(i));
}
} else {
if (_rawData.size() - paginationCode > 0) {
for (int i = paginationList.size(); i < paginationCode; i++) {
paginationList.add(_rawData.get(i));
}
} else {
for (int i = paginationList.size(); i < _rawData.size(); i++) {
paginationList.add(_rawData.get(i));
}
}
}
if (paginationList.size() != 0) {
if (_rawData.size() > paginationCode + 10)
paginationCode += 10;
else if (_rawData.size() - paginationList.size() > 0)
paginationCode = paginationCode + _rawData.size() - paginationCode;
}
}
}
接下来我们就要去我们的onload 方法里面写东西了, 当用户滚动到最后一条,我们最早写的那个 自定义的listview 类,会调用我们的回调函数(onload),我们在这个方法里面去更新数据
@Override
public void onLoad() {
if (_rawData.size() > paginationList.size()) {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
LoadPaginationData();
adr_contact.updateView(paginationList); //这个方法是我的自定义的适配器里面的方法,我传递了一个参数过去
//这个参数就是最新的数据
adr_contact.notifyDataSetChanged(); //然后调用这个方法,适配器就会重新调用一次 getView方法,然后把我的最新的数据
//加载进去
contact_list.loadComplete(); //加载完成了之后,调用我们自定义的listview里面的这个方法,隐藏掉底部的提示
}
}, 1);
} else {
contact_list.loadComplete();
}
}
自定义适配器的所有代码我就不贴了,贴一下部分代码
public void updateView(ArrayList<HashMap<String, Object>> datas) {
_rawData = datas; //传递最新的数据过来
}
然后getView 方法里面,所有的数据,都是从 _rawData 里面取出来的