码迷,mamicode.com
首页 > 移动开发 > 详细

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

时间:2015-04-18 17:52:25      阅读:213      评论:0      收藏:0      [点我收藏+]

标签:android4.0之后使用listvi   handler机制   自定义适配器   多线程下载   

平时的一些Android学习视频中,他们都是基于Android的去使用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" />

这样就可以完成了。

最后得到的结果截图:

技术分享技术分享






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

标签:android4.0之后使用listvi   handler机制   自定义适配器   多线程下载   

原文地址:http://blog.csdn.net/liweijie_chengxuyuan/article/details/45114037

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!