标签: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; } }
/** * 各种数据的下载实现类 * * @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 + "]"; } }
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>
<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