listview加载数据
首先我们需要理清思路:使用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加载数据的更多相关文章
- ListView 加载数据时 触摸报错
问题起因: 在做一个从sd卡中加载数据显示在ListView中,由于数据可能比较多,考虑到用户体验,就使用AsyncTask来异步加载,数据一条一条的添加至ListView中. 开始数据比较少的时候, ...
- [转]listview加载性能优化ViewHolder
当listview有大量的数据需要加载的时候,会占据大量内存,影响性能,这时候就需要按需填充并重新使用view来减少对象的创建. ListView加载数据都是在public View getView( ...
- android之 listview加载性能优化ViewHolder
在android开发中Listview是一个很重要的组件,它以列表的形式根据数据的长自适应展示具体内容,用户可以自由的定义listview每一列的布局,但当listview有大量的数据需要加载的时候, ...
- listview加载性能优化ViewHolder
在android开发中Listview是一个很重要的组件,它以列表的形式根据数据的长自适应展示具体内容,用户可以自由的定义listview每一列的布局, 但当listview有大量的数据需要加载的时候 ...
- listview加载性能优化
在android开发中Listview是一个很重要的组件,它以列表的形式根据数据的长自适应展示具体内容,用户可以自由的定义listview每一列的布局,但当listview有大量的数据需要加载的时候, ...
- 安卓开发笔记——ListView加载性能优化ViewHolder
在前不久做安卓项目的时候,其中有个功能是爬取某网站上的新闻信息,用ListView展示,虽然做了分页,但还是觉得达不到理想流畅效果. 上网查阅了些资料,发现一些挺不错的总结,这里记录下,便于复习. 当 ...
- AppCan学习笔记----关闭页面listview动态加载数据
AppCan页面关闭 AppCan 的页面是由两个HTML组成,如果要完全关闭的话需要在主HTML eg.index.html中关闭,关闭方法:appcan.window.close(-1); 管道 ...
- ListView用法及加载数据时的闪烁问题和加载数据过慢问题
ListView介绍及添加数据时的闪烁问题 1. ListView类 1.1 ListView常用的基本属性: (1)FullRowSelect:设置是否行选择模式.(默认为false) 提示 ...
- Android中ListView分页加载数据
public class MainActivity extends Activity { private ListView listView=null; //listview的数据填充器 privat ...
随机推荐
- jersey练习
package com.tz.router; import java.util.ArrayList; import java.util.Date; import java.util.List; imp ...
- MikroTik RouterOS安装后初始化配置(PPPOE拨号上网)
1.修改登入密码 路由器默认登入账号为admin,密码为空,强烈建议修改登入密码保证安全: 2.修改接口名称 选择Interface,切换到Ethernet标签,找到状态是R(run)的两个端口. 给 ...
- Java中的锁(转)
Java中的锁 锁像synchronized同步块一样,是一种线程同步机制,但比Java中的synchronized同步块更复杂.因为锁(以及其它更高级的线程同步机制)是由synchronized同步 ...
- HDU 4731 Minimum palindrome (2013成都网络赛,找规律构造)
Minimum palindrome Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Other ...
- STM32 F4 GPIO Modes
STM32 F4 GPIO Modes Goal: creating a visual summary of GPIO configuration modes. The summary at the ...
- Extended APDU support
http://pcsclite.alioth.debian.org/ccid_extended_apdu.html To be able to use an extended APDU you nee ...
- 【Go入门教程2】基本构成元素:标识符(identifier)、关键字(keyword 25个)、字面量(literal)、分隔符(delimiter)、和 操作符(operator)
基本构成要素 Go 的语言符号 又称 词法元素,共包括 5 类内容——标识符(identifier).关键字(keyword).字面量(literal).分隔符(delimiter) 和 操作符(op ...
- TF400511: Your team has not defined any iterations to use as sprints
tfs里面的冲刺对于开发团队来说, 是非常重要的一个功能,是团队开发进度的晴雨表: 但是如果从此死活出不来,怎么办呢? TF400511:您的团队尚未定义任何要用作冲刺 (sprint) 的迭代 TF ...
- 给第三方dll强签名
假若我们要对第三方控件或者是其他的没有源代码的DLL文件想做类似的处理,增加强名称签名,怎么处理,是很多人都会面对的问题. 步骤: 1.首先采用反汇编工具ildasm生成中间语言. ildas ...
- Delphi XE中String、ANSIString、TBytes之间的转换
一.string转为ansistring1.直接赋值 (有警告)2.ansistring()类型强制转换.(无警告) 二.ansistring 转为string 1.直接赋值 (有警告)2.strin ...