当前位置: 代码迷 >> Android >> 基于Android4.0ListView从网络获取图片文字资源展示
  详细解决方案

基于Android4.0ListView从网络获取图片文字资源展示

热度:32   发布时间:2016-04-28 01:25:28.0
基于Android4.0ListView从网络获取图片文字资源显示

平时的一些Android学习视频中,他们都是基于Android2.2的去使用ListView,我看到都是会在UI线程中去访问网络获取数据,但是这在Android4.0之后是行不通的。

首先我们来理一下思绪:

我们需要从网络上下载一份xml数据,里面包含了需要显示的文字和图片路径。所以我们首先需要的就是先去下载数据,下载数据完成之后再在Adapter中显示图片的时候去下载图片,然后显示出来。这是基本的思路。但是做着做着会发现一些问题,比如,我们如何能保证数据下载完全,才去绑定适配器和数据他们。然后假如我们是在一条子线程中去完成下载数据,下载完成之后再去绑定适配器,这样子貌似可以,但是会发现有一些问题,我们需要自定义适配器然后去更新ImageView,这就需要使用到Handler。那么下载完成绑定适配器,如何再在适配器中去更新UI呢,这时候的适配器是运行在子线程的,假如把Ui线程的handler作为参数给了adapter,那么宅adapter里面发送消息给UI的handler,可以UI的handler如何找的到属于ListView的一个Item的ImageView,所以这个行不通。

所以正确的做法应该是:我们应该在UI线程绑定适配器,我们先使用没有值的List传给adapter,这时候适配器就运行在UI线程了,同时在UI线程中启动一条线程去下载数据,假如下载完成,则发送使用Handler发送一条消息,这时候handler应该使用的是adapter中的handler,因为adapter是运行在UI线程,不需要再有Looper,直接使用Handler的handlerMessage()就好。然后在adapter的getView方法中,在开启一条线程去完成图片的下载,假如下载完成,则使用adapter的handler发送一条消息,定义一个tag显示下载完成。假如下载失败则发送一个空消息,what为-1这样,就能在adapter的Handler中去处理ImageView,然后更新它了。这就是大概的思路,下面请看源码分析:

MainActivity.java他的主要功能是绑定数据和适配器,ListView和适配器,启动一条线程去现在.xml文件

public class MainActivity extends Activity{	private ListView listview;//显示数据的ListView	@Override	protected void onCreate(Bundle savedInstanceState)	{		super.onCreate(savedInstanceState);		setContentView(R.layout.activity_main);		File cache = new File(Environment.getExternalStorageDirectory(), "cahce");//创建缓存的目录文件夹		if (!cache.exists())			cache.mkdirs();		listview = (ListView)findViewById(R.id.listview);//找到ListView		List<Contacts> data = new ArrayList<Contacts>();//首先使用空的data		ListViewAdapter adapter = new ListViewAdapter(this,R.layout.item_listview,data,cache);//绑定适配器,这时候就使得适配器运行在UI线程中		listview.setAdapter(adapter);//绑定ListView和适配器		new MyThread().start();//启动线程去下载.xml数据	}}class MyThread extends Thread{	@Override	public void run()	{		try		{			ContacesService.getContacts(ListViewAdapter.mHandler);//下载.xml数据		} catch (Exception e)		{			ListViewAdapter.mHandler.sendEmptyMessage(-1);//有异常则利用,必须只能利用adapter的handler去发送一条消息通知下载失败			e.printStackTrace();		}	}}
ListAdapter.java,他的功能主要是把数据显示在ListVIew上,然后其中需要启动线程去下载图片,有关于这个listview的消息都需要通过该handler发送然后达到这里去处理。

public class ListViewAdapter extends BaseAdapter{	private static final int OK = 2;//图片下载完成msg.what=1	private static final int FAILE = -1;//图片下载失败msg.what=-1	private static ImageView image;	private File cache;	@SuppressLint("HandlerLeak")	public static Handler mHandler = new Handler()//数据UI的Handler	{		@SuppressWarnings("unchecked")		public void handleMessage(android.os.Message msg) {			if(msg.what == 1)			{				data.addAll((List<Contacts>) msg.obj);//数据下载完成则更新data			}			if(image!= null && msg.what == OK)			{				image.setImageURI((Uri) msg.obj);//图片下载完成则更新ImageView			}		};	};		private static List<Contacts> data;//数据	private int itemListviewl;//layout的id	private LayoutInflater inflater;//layout填充器	public ListViewAdapter (Activity mainActivity , int itemListview , List<Contacts> data,File cache)	{		ListViewAdapter.data = data;		this.itemListviewl = itemListview;		this.cache = cache;		inflater = (LayoutInflater) mainActivity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);	}	//获得item的总数目	@Override	public int getCount()	{		return data.size();	}	//获得某一个item的数据	@Override	public Object getItem(int position)	{		return data.get(position);	}	//获得某一个item所在数据中的位置	@Override	public long getItemId(int position)	{		return position;	}	//每一次需要显示在ListView的条目都会调用该方法,来获得一个view然后把对应的数据显示出来	@Override	public View getView(int position, View convertView, ViewGroup parent)	{		ViewHolder holder = null;		if(convertView == null)		{			convertView = inflater.inflate(itemListviewl, 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();		}		Contacts contact = data.get(position);		holder.textview.setText(contact.name);		asyncTask(contact.path,holder.imageView,cache);		return convertView;	}		/**	 * 下载图片	 * @param path 下载路径	 * @param imageView 下载图片需要显示的ImageView	 * @param cache 下载图片需要保存的文件夹	 */	private void asyncTask(final String path, final ImageView imageView,final File cache)	{		Runnable runnable = new Runnable()		{			public void run()			{				try				{					Uri uri = ContacesService.getImage(path,cache);					System.out.println("uri="+uri);					if(uri != null)					{						Message msg = Message.obtain();						msg.what = OK;						msg.obj = uri;						image = imageView;						mHandler.sendMessage(msg);//下载成功发送消息					}					else					{						mHandler.sendEmptyMessage(FAILE);//下载失败发送消息					}				} catch (Exception e)				{					mHandler.sendEmptyMessage(FAILE);//下载失败发送消息					e.printStackTrace();				}							}		};		new Thread(runnable).start();//启动线程开始下载	}	/**	 * 当条目多的时候用于增加性能	 * @author Administrator	 *	 */	class ViewHolder	{		ImageView imageView;		TextView textview;	}}

ContacesService.java,他的作用是把从网络下载的数据封装一起,下载各种数据,发送各种消息

/** * 各种数据的下载实现类 *  * @author Administrator * */public class ContacesService{	/**	 * 下载.xml文件的驱动类	 * 	 * @param handler	 *            需要发送消息的adapter中的handler	 * @throws Exception	 */	public static void getContacts(Handler handler) throws Exception	{		String path = "http://192.168.1.101:8080/web/list.xml";		HttpURLConnection conn = (HttpURLConnection) new URL(path).openConnection();		conn.setConnectTimeout(5000);		conn.setRequestMethod("GET");		if (200 == conn.getResponseCode())		{			System.out.println("11111");			InputStream inputStream = conn.getInputStream();			parserXML(inputStream, handler);		}	}	/**	 * 下载并且解析.xml文件 生成一个集合利用handler发送给adapter处理	 * 	 * @param inputStream	 *            输入流	 * @param handler需要发送消息的adapter中的handler	 * @throws Exception	 * @throws IOException	 */	private static void parserXML(InputStream inputStream, Handler handler) throws Exception, IOException	{		XmlPullParser parser = Xml.newPullParser();		parser.setInput(inputStream, "UTF-8");		List<Contacts> contacts = new ArrayList<Contacts>();		System.out.println(2222);		Contacts contact = null;		int event = parser.getEventType();		while (XmlPullParser.END_DOCUMENT != event)		{			switch (event)			{			case XmlPullParser.START_TAG:				if ("contact".equals(parser.getName()))				{					contact = new Contacts();					contact.id = Integer.valueOf(parser.getAttributeValue(0));				}				if ("name".equals(parser.getName()))				{					contact.name = parser.nextText();				}				if ("image".equals(parser.getName()))				{					contact.path = parser.getAttributeValue(0);				}				break;			case XmlPullParser.END_TAG:				if ("contact".equals(parser.getName()))				{					contacts.add(contact);					contact = null;				}				break;			}			event = parser.next();		}		if (contacts.size() != 0)		{			Message msg = Message.obtain();			msg.what = 1;			msg.obj = contacts;			System.out.println(contacts);// 用于测试是否下载成功			handler.sendMessage(msg);// 发送adapter,让他去更新data的值		} else		{			handler.sendEmptyMessage(-1);// 失败发送-1		}	}	/**	 * 实现从网络下载图片并且保存本地,当本地存在该图片则直接读取	 * 	 * @param path	 *            图片的路径	 * @param cache	 *            缓存二栋目录	 * @return	 * @throws Exception	 */	public static Uri getImage(String path, File cache) throws Exception	{		File localFile = new File(cache, MD5.getMD5(path) + path.substring(path.lastIndexOf(".")));// MD																									// 5加密		if (localFile.exists())		{			return Uri.fromFile(localFile);		} else		{			HttpURLConnection conn = (HttpURLConnection) new URL(path).openConnection();			conn.setConnectTimeout(5000);			conn.setRequestMethod("GET");			if (200 == conn.getResponseCode())			{				System.out.println("11111");				InputStream inputStream = conn.getInputStream();				FileOutputStream fos = new FileOutputStream(localFile);				int len;				byte[] buffer = new byte[1024];				while ((len = inputStream.read(buffer)) != -1)				{					fos.write(buffer, 0, len);				}				fos.close();				inputStream.close();				return Uri.fromFile(localFile);			}		}		return null;	}}
domai类

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 String toString()	{		return "Contacts [id=" + id + ", name=" + name + ", path=" + path + "]";	}}

然后是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();    }}
关于布局:main.xml

<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:orientation="vertical" >    <ListView        android:layout_width="match_parent"        android:layout_height="match_parent"        android:id="@+id/listview"/></LinearLayout>
item.xml
<?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:orientation="horizontal" >    <ImageView        android:id="@+id/imageview"        android:layout_width="50dp"        android:layout_height="50dp"/>    <TextView        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:id="@+id/textView"        android:textSize="20sp" /></LinearLayout>

最后,由于需要访问网络以及从SD 卡写入读取数据,需要权限:

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

这样就可以完成了。

最后得到的结果截图:







  相关解决方案