首先我们需要理清思路:使用ListView显示数据是很方便的,ListVIew的数据之间通过适配器adapter去作为桥梁连接起来。当我们需要使用listview显示大量数据的时候,我们需要使用到分页功能,比如我们有一千条数据,那么我们应该分开数据一点一点的显示,比如每次用户刷新我就增加20条数据额、展示给用户,每次都是增加一定量的数据给用户,直到数据没有为止。为了改善用户体验,我们还应该把上一次用户退出的时候显示最新的20条数据保留本地,用户下次点进来就可以直接看到那些数据,用户点击刷新的时候再去加载本次位于服务器的最新20数据给用户,然后把原来的数据覆盖掉。然后用户下拉到20条数据底端的时候再去加载那之后的20条数据,这样往复循环就可以实现了。本人这次只是模拟。

首先,如何判断用户需要加载新的数据,就是需要监听listview的滚动事件,当用户滚动到最底屏的时候,有一个可以唯一确定的是,当前用户屏幕显示的最后一条数据的position+1等于总的数据量,这时候,我们就应该去添加新的数据,开一条新的线程完成该工作。然后利用handler去把数据添加到适配器的data中,然后调用adapter.notifyDataSetChanged()就可以更新ListVIew了。但是这里有一些问题需要注意的

1,我们需要保证我们当期用户点击下拉刷新之后下载好的数据全部加载到适配器的data的时候才可以去加载新的数据,这是因为某些用户可能很急躁,不断的下拉listview,导致不断的是的屏幕最后一条数据+1等于总的数据量,不断去触发线程下载新数据,所以,我们需要有一个flag去控制。

2,所有的数据下载都需要开线程去完成或者是使用异步任务,下载图片的时候建议使用异步任务,以为当用户快速滚动的时候会开很多的线程下载图片,异步任务能控制线程数量,或者使用Imageloder框架

3,缓存图片,每一次我们去加载新的资源的时候我们就需要把最新的20条数据覆盖原来的,保存本地,他应该在每一次去请求数据的时候开一条线程去完成。主要注意的是每一次请求的输入流只能使用一次,所以这里既需要写入本地有需要写入内存,所以需要请求两次来获得两个数据流,这里可以看getData()方法。

详细代码:

MainActivity

public class MainActivity extends Activity
{
// 每次都保证把最新的数据保存到本地,下次用户点开的时候就可以直接显示这些以前最新的数据,
// 当用户下拉刷新之后,在把下拉刷新之后的数据读入到该文件中,每次用户点开都是上次最新的不过现在没有刷新的文件
public static final File saveXMLToSD = new File(Environment.getExternalStorageDirectory(), "list.xml");
private ListView listview;// listview
private File cache;// 缓存目录
public static final int OK = 1;// 成功获得数据
public static final int YES = 2;// 成功获得最新数据数据
public static final int ERROR = -1;// 失败获得数据
private boolean flag = true;
private View footer;
private ListAdapter adapter;
private boolean isFinsh = false;
// 负责当数据完成下载之后绑定适配器,用户首次使用点击屏幕就不会有异常
private Handler mHandler = new Handler()
{
@SuppressWarnings("unchecked")
public void handleMessage(android.os.Message msg)
{
if (msg.what == OK)
{
adapter = new ListAdapter(MainActivity.this, R.layout.list_item, cache, (List<Contacts>) msg.obj);
System.out.println("(List<Contacts>) msg.obj" + ((List<Contacts>) msg.obj).size());
listview.addFooterView(footer);// 添加页脚,用于改善用户体验
listview.setAdapter(adapter);// 绑定适配器
listview.removeFooterView(footer);// 首次不用显式页脚
}
if (msg.what == YES)// 成功加载最新数据
{
flag = true;//这时候才可以继续去下载数据
adapter.setData((List<Contacts>) msg.obj);
adapter.notifyDataSetChanged();// 通知数据更改成功,更新ListView
if (listview.getFooterViewsCount() > 0)
listview.removeFooterView(footer);// 有页脚存在则去除,此时已经完全加载数据
}
if (msg.what == ERROR)
{
Toast.makeText(getApplicationContext(), "网络连接失败", Toast.LENGTH_SHORT).show();
}
};
}; @Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listview = (ListView) findViewById(R.id.listview); cache = new File(Environment.getExternalStorageDirectory(), "cahce");
if (!cache.exists())
cache.mkdirs();
footer = getLayoutInflater().inflate(R.layout.footer, null);// 加载页脚
listview.setOnScrollListener(new ListViewScrollListener());// 监听滚动事件
new Thread(new Runnable()
{
public void run()
{
try
{
List<Contacts> data = new ArrayList<Contacts>();
if (!saveXMLToSD.exists())
{
// data.addAll(ContactsService.getData());//
// 首次文件不存在的时候加载数据
data.addAll(ContactsService.getData()); } else
{
// 首次数据存在的时候从用户的xml文件中读取数据,这样给用户比较快的感觉,每一次登陆都显示上一次登陆的结果
FileInputStream fis = new FileInputStream(saveXMLToSD);
// data = ContactsService.parserXML(fis);
data.addAll(ContactsService.parserXML(fis));
}
Message msg = Message.obtain();
msg.what = OK;
msg.obj = data;
mHandler.sendMessage(msg);// 成功发送消息
} catch (Exception e)
{
mHandler.sendEmptyMessage(ERROR);
e.printStackTrace();
}
}
}).start(); } class ListViewScrollListener implements OnScrollListener
{
public void onScrollStateChanged(AbsListView view, int scrollState)
{ } /**
* firstVisibleItem 屏幕中第一个可见的item的position
* visibleItemCount 屏幕中可见的item的数量,
* totalItemCount 一共有的数据总量
*/
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount)
{
int lastItemId = listview.getLastVisiblePosition();//屏幕中最后的一个可视的item的position
System.out.println("lastItemId=" + lastItemId + "firstVisibleItem=" + firstVisibleItem + "visibleItemCount=" + visibleItemCount + "totalItemCount=" + totalItemCount);
// 当可视的item的最后一条达到了总数目则说明用户已经达到了数据的最低部,这时候应该从网络获取最新数据
if (lastItemId + 1 == totalItemCount && totalItemCount > 0)
{
if (flag)
{
flag = false;//防止用户不断下拉listview,所以一旦有一次下拉到最低端之后数据下载之后,马上设为false,只有数据下载完成之后才在handlr中设置为true
// 这时候把标志值为true,因为下拉底部的时候需要把这一次的下拉完全获取玩之前不去再次加载,而获取是使用线程的,需要时间,所以必须先要把该标志值为true
listview.addFooterView(footer);
// 开启线程加载最新的数据
new Thread(new Runnable()
{
@Override
public void run()
{
try
{
Thread.currentThread().sleep(3000);
List<Contacts> data = ContactsService.getData();
Message msg = Message.obtain();
msg.what = YES;
msg.obj = data;
mHandler.sendMessage(msg);
} catch (Exception e)
{
mHandler.sendEmptyMessage(-1);
e.printStackTrace();
}
}
}).start();
}
}
}
} // 当用户退出当前应用的时候把所有的缓存图片删除
@Override
protected void onDestroy()
{
if (cache.exists())
{
for (File item : cache.listFiles())
{
item.delete();
}
cache.delete();
}
super.onDestroy();// 一定需要这句,否则会失败
} }

adapter,下载图片,绑定数据,更新数据

public class ListAdapter extends BaseAdapter
{
public List<Contacts> data = new ArrayList<Contacts>();
private int listItem;
private File cache;
private LayoutInflater inflater;
private Contacts contact;
private static ImageView imageview; /**
* @return the data
*/
public List<Contacts> getData()
{
return data;
} /**
* @param data the data to set
*/
public void setData(List<Contacts> data)
{
this.data.addAll(data);
} public ListAdapter (Context mainActivity , int listItem , File cache , List<Contacts> data)
{
this.data.addAll(data);
this.listItem = listItem;
this.cache = cache;
inflater = (LayoutInflater) mainActivity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
} @Override
public int getCount()
{
return data.size();
} @Override
public Object getItem(int position)
{
return data.get(position);
} @Override
public long getItemId(int position)
{
return position;
} @Override
public View getView(int position, View convertView, ViewGroup parent)
{
ViewHolder holder = null;
if (null == convertView)
{
convertView = inflater.inflate(listItem, null);
holder = new ViewHolder();
holder.imageview = (ImageView) convertView.findViewById(R.id.imageview);
holder.textView = (TextView) convertView.findViewById(R.id.textview);
convertView.setTag(holder); } else
{
holder = (ViewHolder) convertView.getTag();
}
contact = data.get(position);
holder.textView.setText(contact.name);
imageview = holder.imageview;
loadImageView(contact.path);
return convertView;
} static class ViewHolder
{
ImageView imageview;
TextView textView;
} private void loadImageView(String path)
{ new MyAsyncTask(imageview).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, path);
} private class MyAsyncTask extends AsyncTask<String, Void, Uri>
{ private ImageView imageView; public MyAsyncTask (ImageView imageView)
{
super();
this.imageView = imageView;
} //后台下载图片,使用线程池控制,也可以线程对象的重用
@Override
protected Uri doInBackground(String... params)
{
String path = params[0];
try
{
Uri uri = ContactsService.loadSaveImage(path, cache);
return uri;
} catch (Exception e)
{
e.printStackTrace();
}
return null;
} @Override
protected void onPostExecute(Uri result)
{
if(result!= null && imageView != null)
imageView.setImageURI(result);//运行在UI线程直接更新ImageView
else if(imageView != null)
{
imageView.setImageResource(R.drawable.ic_launcher);//默认图片
}
} } }

service,下载数据,缓存图片,解析XML

public class ContactsService
{
/**
* 返回最新数据 list集合返回
*
* @return
* @throws Exception
* @throws IOException
*/
public static List<Contacts> getData() throws Exception, IOException
{ String pathXML = "http://10.10.117.197:8080/web/list2.xml";
HttpClient client = new DefaultHttpClient();
HttpPost post = new HttpPost(pathXML);
HttpResponse httpResponse = client.execute(post);
if (200 == httpResponse.getStatusLine().getStatusCode())
{
HttpEntity entity = httpResponse.getEntity();
final InputStream content = entity.getContent();
new Thread(new Runnable()
{
public void run()
{
saveToSD(content);// 每一次实现下载最新的数据的同时需要去保存本地
}
}).start();
return parserXML(client.execute(post).getEntity().getContent());// 返回最新的数据给listview显示
}
return null;
} /**
* 解析XML数据,并以集合返回
* @param content
* @return
* @throws Exception
*/
public static List<Contacts> parserXML(InputStream content) throws Exception
{
XmlPullParser parser = Xml.newPullParser();
parser.setInput(content, "UTF-8");
int event = parser.getEventType();
List<Contacts> data = new ArrayList<Contacts>();
Contacts item = null;
int i = 1;
while (event != XmlPullParser.END_DOCUMENT)
{
switch (event)
{
case XmlPullParser.START_TAG:
if ("contact".equals(parser.getName()))
{
item = new Contacts();
item.id = Integer.valueOf(parser.getAttributeValue(0));
break;
}
if ("name".equals(parser.getName()))
{
item.name = parser.nextText()+i++;
break;
}
if ("image".equals(parser.getName()))
{
item.path = parser.getAttributeValue(0);
break;
}
break; case XmlPullParser.END_TAG:
if ("contact".equals(parser.getName()))
{
data.add(item);
item = null;
}
break;
}
event = parser.next();
} return data;
} /**
* 保存最新的数据到本地
*
* @param content
* 输入流
*/
private static void saveToSD(InputStream content)
{
try
{
FileOutputStream fos = new FileOutputStream(MainActivity.saveXMLToSD);
int len;
byte[] buffer = new byte[1024];
while ((len = content.read(buffer)) != -1)
{
fos.write(buffer, 0, len);
}
fos.close();
} catch (FileNotFoundException e)
{
e.printStackTrace();
} catch (IOException e)
{
e.printStackTrace();
} } /**
* 缓存图片
*
* @param path
* 下载路径
* @param cache
* 缓存目录
* @return
* @throws Exception
*/
public static Uri loadSaveImage(String path, File cache) throws Exception
{
File localFile = new File(cache, MD5.getMD5(path) + path.substring(path.lastIndexOf(".")));
if (localFile.exists())
{
return Uri.fromFile(localFile);
} else
{
BufferedOutputStream localFileBufferedOutputStream = null;
HttpResponse httpResponse = null; FileOutputStream localFileOutputStream = new FileOutputStream(localFile);
localFileBufferedOutputStream = new BufferedOutputStream(localFileOutputStream);
HttpClient client = new DefaultHttpClient();
HttpPost post = new HttpPost(path);
httpResponse = client.execute(post); if (200 == httpResponse.getStatusLine().getStatusCode())
{
InputStream content = null;
try
{
HttpEntity entity = httpResponse.getEntity();
content = entity.getContent();
int len;
byte[] buffer = new byte[1024];
while ((len = content.read(buffer)) != -1)
{
localFileBufferedOutputStream.write(buffer, 0, len);
localFileBufferedOutputStream.flush();
}
return Uri.fromFile(localFile);
} catch (IllegalStateException e)
{
e.printStackTrace();
} catch (IOException e)
{
e.printStackTrace();
} finally
{
try
{
localFileBufferedOutputStream.close();
} catch (IOException e)
{
e.printStackTrace();
}
} } }
return null;
}
}

domain

public class Contacts
{
public int id;
public String name;
public String path;
public Contacts (){}
public Contacts (int id , String name , String path)
{
super();
this.id = id;
this.name = name;
this.path = path;
}
@Override
public boolean equals(Object o)
{
return false;
}
}

Util类 MD5命名

public class MD5 {

	public static String getMD5(String content) {
try {
MessageDigest digest = MessageDigest.getInstance("MD5");
digest.update(content.getBytes());
return getHashString(digest); } catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return null;
} private static String getHashString(MessageDigest digest) {
StringBuilder builder = new StringBuilder();
for (byte b : digest.digest()) {
builder.append(Integer.toHexString((b >> 4) & 0xf));
builder.append(Integer.toHexString(b & 0xf));
}
return builder.toString();
}
}

layout文件

main

<LinearLayout 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"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.example.asynctasklistdemo.MainActivity" > <ListView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/listview" /> </LinearLayout>

listview的item

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="horizontal" > <ImageView
android:id="@+id/imageview"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_gravity="center"
android:src="@drawable/ic_launcher" /> <TextView
android:id="@+id/textview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:textColor="#000000"
android:textSize="40sp" /> </LinearLayout>

页脚footer

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="horizontal"> <ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="数据正在加载。。。"
android:textSize="20sp"
android:layout_gravity="center"
/>
</LinearLayout>

权限:

    <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />

结果截图:

listview加载数据的更多相关文章

  1. ListView 加载数据时 触摸报错

    问题起因: 在做一个从sd卡中加载数据显示在ListView中,由于数据可能比较多,考虑到用户体验,就使用AsyncTask来异步加载,数据一条一条的添加至ListView中. 开始数据比较少的时候, ...

  2. [转]listview加载性能优化ViewHolder

    当listview有大量的数据需要加载的时候,会占据大量内存,影响性能,这时候就需要按需填充并重新使用view来减少对象的创建. ListView加载数据都是在public View getView( ...

  3. android之 listview加载性能优化ViewHolder

    在android开发中Listview是一个很重要的组件,它以列表的形式根据数据的长自适应展示具体内容,用户可以自由的定义listview每一列的布局,但当listview有大量的数据需要加载的时候, ...

  4. listview加载性能优化ViewHolder

    在android开发中Listview是一个很重要的组件,它以列表的形式根据数据的长自适应展示具体内容,用户可以自由的定义listview每一列的布局, 但当listview有大量的数据需要加载的时候 ...

  5. listview加载性能优化

    在android开发中Listview是一个很重要的组件,它以列表的形式根据数据的长自适应展示具体内容,用户可以自由的定义listview每一列的布局,但当listview有大量的数据需要加载的时候, ...

  6. 安卓开发笔记——ListView加载性能优化ViewHolder

    在前不久做安卓项目的时候,其中有个功能是爬取某网站上的新闻信息,用ListView展示,虽然做了分页,但还是觉得达不到理想流畅效果. 上网查阅了些资料,发现一些挺不错的总结,这里记录下,便于复习. 当 ...

  7. AppCan学习笔记----关闭页面listview动态加载数据

    AppCan页面关闭 AppCan 的页面是由两个HTML组成,如果要完全关闭的话需要在主HTML eg.index.html中关闭,关闭方法:appcan.window.close(-1); 管道 ...

  8. ListView用法及加载数据时的闪烁问题和加载数据过慢问题

    ListView介绍及添加数据时的闪烁问题 1.     ListView类 1.1 ListView常用的基本属性: (1)FullRowSelect:设置是否行选择模式.(默认为false) 提示 ...

  9. Android中ListView分页加载数据

    public class MainActivity extends Activity { private ListView listView=null; //listview的数据填充器 privat ...

随机推荐

  1. 「WC 2019」数树

    「WC 2019」数树 一道涨姿势的EGF好题,官方题解我并没有完全看懂,尝试用指数型生成函数和组合意义的角度推了一波.考场上只得了 44 分也暴露了我在数数的一些基本套路上的不足,后面的 \(\ex ...

  2. BZOJ.4816.[SDOI2017]数字表格(莫比乌斯反演)

    题目链接 总感觉博客园的\(Markdown\)很..\(gouzhi\),可以看这的. 这个好像简单些啊,只要不犯sb错误 [Update] 真的算反演中比较裸的题了... \(Descriptio ...

  3. HDU 5909 Tree Cutting 动态规划 快速沃尔什变换

    Tree Cutting 题目连接: http://acm.hdu.edu.cn/showproblem.php?pid=5909 Description Byteasar has a tree T ...

  4. Java调用OCR进行图片识别

    使用Java语言,通过Tesseract-OCR对图片进行识别. 1.Tesseract-OCR 下载windows版本并安装. 2.程序如下: a.ImageIOHelper类 package OC ...

  5. authentication unavailable: no polkit agent available to authenticate action 'org.libvirt.unix.manage'的问题解决

    这个主要是WebVirtMgr的安装导致出现的错误,解决方法如下: 1.增加libvirtd用户组 groupadd libvirtd 2.设置用户到组 sudo usermod -a -G libv ...

  6. Introduction to the Optimizer --cbo

    http://docs.oracle.com/cd/B10500_01/server.920/a96533/optimops.htm

  7. AngularJS报错:[$compile:tpload]

    页面中有: <div ng-view></div> 这里会根据不同的路由设置选择不同模版下的内容. 如果把AngularJS网站项目放在包含中文字的目录中,会报以上的错. 解决 ...

  8. delphi中单独编译pas生成dcu文件

    delphi中单独编译pas生成dcu文件 在网上下载了一个带源码的组件,结果碰到提示说缺少xxx.dcu.一看它的目录下确实没有,那能不能生成一个呢? 当然可以! 方法是使用delphi的安装目录\ ...

  9. Occlusion Culling遮挡剔除理解设置和地形优化应用

    这里使用的是unity5.5版本 具体解释网上都有,就不多说了,这里主要说明怎么使用,以及参数设置和实际注意点 在大场景地形的优化上,但也不是随便烘焙就能降低帧率的,必须结合实际情况来考虑,当然还有透 ...

  10. (转载):ASCII,Unicode和UTF-8 编码

    UTF-8是Unicode的一种实现方式,也就是它的字节结构有特殊要求,所以我们说一个汉字的范围是0X4E00到0x9FA5,是指unicode值,至于放在utf-8的编码里去就是由三个字节来组织,所 ...