ListView实现保存上一次最新数据跟下拉的时候显示最新的数据
ListView实现保存上一次最新数据和下拉的时候显示最新的数据
ContactsService.java
这样大概的代码就全了,关于adapter以及其他的类的,参考我的前面两篇blog,没有改变他们
之前跟大家分享的都是每一次用户点击的时候都会先从网络获取数据,这样子的用户体验很不好,我们应该实现这样的一个功能:当用户退出该软件的时候,我们把用户当前的最新的15(大概)条信息保留下来,比如微信中的朋友圈,然后当用户再次使用的时候就先看到这上次保存的内容,然后当用户点击下拉或者是上刷新的时候,我们就给用户返回最新的消息,包括之前对用户保留的信息的更改。那么实现这个功能如何做?
个人思路:
首先,当用户第一次使用软件的时候,我们给他提供15条最新的数据,在获取这15条最新的数据的时候,我们同时把这15条数据用一个线程去保存本地。假如用户点击下拉刷新的时候,我们再去加载新的数据,然后把原来的数据去掉,使用新的数据。每一次当用户点进来的时候,我们判断该文件是否存在,假如存在就把它使用listview显示出来,当用户点击下拉刷新的时候我们再去加载新的数据,然后显示在用户界面。这样一个功能类似的就可以实现了:
下面是源代码分析:
MainActivity.java,他的功能作用,使用handler绑定适配器,然后更新listview的data,开启线程去下载数据,监听listview的滚动事件,删除缓存目录
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 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); listview.addFooterView(footer);//添加页脚,用于改善用户体验 listview.setAdapter(adapter);//绑定适配器 listview.removeFooterView(footer);//首次不用显式页脚 } if (msg.what == YES)//成功加载最新数据 { isFinsh = true;//把页脚标志改为true adapter.data.addAll((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);//加载页脚 new Thread(new Runnable() { public void run() { try { List<Contacts> data = null; if (!saveXMLToSD.exists()) { data = ContactsService.getData();//首次文件不存在的时候加载数据 } else { //首次数据存在的时候从用户的xml文件中读取数据,这样给用户比较快的感觉,每一次登陆都显示上一次登陆的结果 FileInputStream fis = new FileInputStream(saveXMLToSD); data = 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(); listview.setOnScrollListener(new ListViewScrollListener());//监听滚动事件 } class ListViewScrollListener implements OnScrollListener { public void onScrollStateChanged(AbsListView view, int scrollState) { } public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { int lastItemId = listview.getLastVisiblePosition(); //当可视的item的最后一条达到了总数目则说明用户已经达到了数据的最低部,这时候应该从网络获取最新数据 if (lastItemId + 1 == totalItemCount && totalItemCount > 0) { isFinsh = false;//这时候没有获取完全,标志位false if (!isFinsh) { //这时候把标志值为true,因为下拉底部的时候需要把这一次的下拉完全获取玩之前不去再次加载,而获取是使用线程的,需要时间,所以必须先要把该标志值为true isFinsh = true; listview.addFooterView(footer);//必须也只能在这里设置页脚的出现,必须在UI线程完成 //开启线程加载最新的数据 new Thread(new Runnable() { @Override public void run() { try { List<Contacts> data = ContactsService.getData(); Message msg = Message.obtain(); msg.what = YES; msg.obj = data; System.out.println(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();// 一定需要这句,否则会失败 } }
ContactsService.java
public class ContactsService { /** * 返回最新数据 list集合返回 * * @return * @throws Exception * @throws IOException */ public static List<Contacts> getData() throws Exception, IOException { String pathXML = "http://192.168.1.101: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; 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(); 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 { RandomAccessFile accessFile = new RandomAccessFile(MainActivity.saveXMLToSD, "rwd");// 保留的文件 int len; byte[] buffer = new byte[1024]; while ((len = content.read(buffer)) != -1) { accessFile.write(buffer, 0, len); } accessFile.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; } }
这样大概的代码就全了,关于adapter以及其他的类的,参考我的前面两篇blog,没有改变他们