开发者社区> 范大脚脚> 正文

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" />


结果截图:




本文转自莫水千流博客园博客,原文链接:http://www.cnblogs.com/zhoug2020/p/7411879.html,如需转载请自行联系原作者


版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
IDEA创建maven项目过慢,一直卡在resolving dependencies...的解决办法
作为一个从事 Java 开发的程序员,每天离不开ide的帮助。一开始学习java的时候基本都是使用eclipse进行开发, 后来接触了idea,发现是真的香,比eclipse好用太多了,能够大大提升开发的效率。
5 0
从零开始学设计模式(六):适配器模式(Adapter Pattern)
前面的几篇文章分别介绍了设计模式中的创建型设计模式,它们分别是:
4 0
Gradle Logging
日志是构建工具的主要“ UI”。 如果太冗长,那么真正的警告和问题很容易被隐藏起来。 另一方面,你需要相关的信息来判断事情是否出了问题。 Gradle 定义了6个日志级别,如日志级别所示。 除了通常可以看到的日志级别之外,还有两个 gradle 特定的日志级别。 这些层次是安静和生命周期。 后者是默认的,用于报告构建进度。
4 0
Gradle Writing Build Scripts
The Gradle build language Gradle 构建语言 Gradle 提供了一种领域特定语言(DSL)来描述构建,这种构建语言在 Groovy 和 Kotlin 都可以使用。 Groovy 构建脚本可以包含任何 Groovy 语言元素。 Kotlin 构建脚本可以包含任何 Kotlin 语言元素。 Gradle 假设每个构建脚本都使用 UTF-8进行编码。
4 0
RocketMQ入门级使用演示-3
RocketMQ入门级使用演示-3
3 0
RocketMQ入门级使用演示-5
RocketMQ入门级使用演示-5
3 0
从零开始学设计模式(四):工厂模式(Factory Pattern)
工厂模式(Factory Pattern)也是 Java中最常用的设计模式之一。这种类型的设计模式也属于创建型模式,它提供了一种创建对象的最佳方式。
3 0
Gradle Authoring Tasks
在入门教程中,您学习了如何创建简单的任务。 稍后您还学习了如何向这些任务添加额外的行为,并学习了如何在任务之间创建依赖关系。 这一切都是关于简单的任务,但 Gradle 把任务的概念更进一步。 Gradle 支持增强型任务,这些任务具有自己的属性和方法。 这与您习惯使用 Ant 目标的情况大不相同。 这些强化的任务要么是你提供的,要么是内置在 Gradle 的。
3 0
Gradle 构建脚本基础(introductory tutorial)
Projects and tasks 项目和任务 每个 Gradle 构建都由一个或多个项目组成。 一个项目代表什么取决于你在 Gradle 上做什么。 例如,一个项目可能表示一个库 JAR 或一个 web 应用程序。 它可以表示从其他项目生成的 jar 组装起来的发行版 ZIP。 一个项目并不一定代表要构建的东西。 它可能代表要做的事情,比如将应用程序部署到登台或生产环境。 不要担心,如果这看起来有点含糊现在。 Gradle 的按惯例构建支持为项目增加了一个更具体的定义。
5 0
+关注
3656
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
OceanBase 入门到实战教程
立即下载
阿里云图数据库GDB,加速开启“图智”未来.ppt
立即下载
实时数仓Hologres技术实战一本通2.0版(下)
立即下载