前言
本周一直在说Android多线程的那些事儿,本篇博客聊一聊Android开发中一个比较经典的案例,网络数据图文混排,本片博客的案例只涉及关于开启多线程访问网络数据,不涉及缓存的内容。众所周知,从网络上获取一段文本肯定要比获取一张张的图片要省时,所以一般如果是获取图片+文本的数据,会先开启一条线程获取文本数据,再从开启另外的线程来单独获取图片信息。本案例填充一个自定义的XML布局文件作为数据项,并使用ListView承载数据。
数据准备
本案例中的服务端数据以Json的形式传递,在服务端使用.Net开发一个一般处理程序,序列化一个产品对象,里面包含名称、价格、图片名称,最后序列化成JSON格式的数据返回给客户端。关于.Net下如何序列化一个对象成JSON格式,可以参见博客:C#--对象转Json序列化,这里不再累述,大家可以使用自己熟悉的服务端技术模拟JSON数据。
获取JSON数据的一般处理程序地址:http://192.168.1.102:1231/json/returnCommondityJson.ashx,数据如下
1 [{"imageName":"image1.png","name":"苹果","price":12}, 2 {"imageName":"image2.png","name":"闹钟","price":56}, 3 {"imageName":"image3.png","name":"蛋糕","price":24}, 4 {"imageName":"image4.png","name":"零钱包","price":8}, 5 {"imageName":"image5.png","name":"书本","price":42}, 6 {"imageName":"image6.png","name":"糖果","price":16}, 7 {"imageName":"image7.png","name":"西瓜","price":2}]
本案例的URL地址均使用一个CommonUri类进行管理:
1 package com.example.handlerimageortext; 2 3 public class CommonUri { 4 // 访问服务器数据的链接 5 public static final String PRODUCT_URL = "http://192.168.1.102:1231/json/returnCommondityJson.ashx"; 6 // 图片的连接 7 public static final String PRODUCT_IMG="http://192.168.1.102:1231/json/img/"; 8 }
使用AsyncTask获取Json数据
在UI线程中,使用AsyncTask的方式访问网络获取JSON数据,并对其进行解析,关于Android下JSON解析的内容可以参见博客:JSON解析。
1 public class MyTask extends AsyncTask<String, Void, List<Map<String,Object>>>{ 2 @Override 3 protected void onPreExecute() { 4 super.onPreExecute(); 5 // 显示对话框 6 dialog.show(); 7 } 8 9 @Override 10 protected List<Map<String, Object>> doInBackground(String... params) { 11 List<Map<String,Object>> list=new ArrayList<Map<String,Object>>(); 12 try { 13 // 获取网络JSON格式数据 14 HttpClient httpClient=new DefaultHttpClient(); 15 HttpPost httpPost=new HttpPost(params[0]); 16 HttpResponse httpResponse=httpClient.execute(httpPost); 17 if(httpResponse.getStatusLine().getStatusCode()==200){ 18 String jsonString=EntityUtils.toString(httpResponse.getEntity(),"utf-8"); 19 // 解析Json格式数据,并使用一个List<Map>存放 20 JSONArray jsonArray=new JSONArray(jsonString); 21 for(int i=0;i<jsonArray.length();i++){ 22 JSONObject jsonObject=jsonArray.getJSONObject(i); 23 Map<String,Object> map=new HashMap<String, Object>(); 24 map.put("name",jsonObject.get("name")); 25 map.put("price",jsonObject.get("price")); 26 map.put("imageName",jsonObject.get("imageName")); 27 list.add(map); 28 } 29 } 30 } catch (Exception e) { 31 e.printStackTrace(); 32 } 33 return list; 34 } 35 @Override 36 protected void onPostExecute(List<Map<String, Object>> result) { 37 super.onPostExecute(result); 38 // 把查询到的数据传递给适配器 39 adapter.setData(result); 40 // 为ListView设定适配器 41 listview.setAdapter(adapter); 42 adapter.notifyDataSetChanged(); 43 // 隐藏对话框 44 dialog.dismiss(); 45 } 46 }
下载图片信息
上面的方法中,使用AsyncTask访问网络获取到产品的信息,其中有图片的名称,可以通过这个地址下载图片到本地。
新创建一个类,用于下载图片,但是需要在主线程中访问图片的信息,可以使用接口回调的方式在Handler中处理子线程发送过来的消息。注释比较全,这里就不再累述了。
1 package com.example.handlerimageortext; 2 3 import java.io.IOException; 4 import java.net.MalformedURLException; 5 import java.net.URL; 6 import android.graphics.drawable.Drawable; 7 import android.os.Handler; 8 import android.os.Message; 9 10 public class DownLoadImage { 11 private String image_path; 12 13 public DownLoadImage(String image_path) { 14 // 保存图片的下载地址 15 this.image_path = image_path; 16 } 17 18 public void loadImage(final ImageCallback callback) { 19 final Handler handler = new Handler() { 20 @Override 21 public void handleMessage(Message msg) { 22 super.handleMessage(msg); 23 // 接受到消息后,调用接口回调的方法 24 callback.getDrawable((Drawable) msg.obj); 25 } 26 }; 27 // 开启一个新线程用于访问图片数据 28 new Thread(new Runnable() { 29 30 @Override 31 public void run() { 32 try { 33 // 下载图片为Drawable对象 34 Drawable drawable = Drawable.createFromStream(new URL( 35 image_path).openStream(), ""); 36 // 把图片对象包装成一个消息发送给Handler 37 Message message = Message.obtain(); 38 message.what = 1; 39 message.obj = drawable; 40 handler.sendMessage(message); 41 } catch (MalformedURLException e) { 42 e.printStackTrace(); 43 } catch (IOException e) { 44 e.printStackTrace(); 45 } 46 } 47 }).start(); 48 } 49 50 // 定义一个公开的接口,用于执行回调操作 51 public interface ImageCallback { 52 public void getDrawable(Drawable draw); 53 } 54 }
数据的适配器
上面已经获取到Json数据中产品的数据,和产品的图片,现在声明一个Adapter类,继承自BaseAdapter,使用一个布局XML资源文件,用于填充数据。
1 public class MyAdapter extends BaseAdapter{ 2 private Context context; 3 private LayoutInflater layoutInflater; 4 private List<Map<String,Object>> list=null; 5 public MyAdapter(Context context){ 6 this.context=context; 7 layoutInflater=LayoutInflater.from(context); 8 } 9 10 public void setData(List<Map<String,Object>> list){ 11 this.list=list; 12 } 13 14 @Override 15 public int getCount() { 16 return list.size(); 17 } 18 19 @Override 20 public Object getItem(int position) { 21 return list.get(position); 22 } 23 24 @Override 25 public long getItemId(int position) { 26 return position; 27 } 28 29 @Override 30 public View getView(int position, View convertView, ViewGroup parent) { 31 View view=null; 32 if(convertView==null){ 33 // 如果View为空,则以布局XML资源文件填充View 34 view=layoutInflater.inflate(R.layout.item,null); 35 }else{ 36 view=convertView; 37 } 38 TextView name=(TextView)view.findViewById(R.id.textView1); 39 TextView price=(TextView)view.findViewById(R.id.textView2); 40 // 因为需要在回调接口中访问这个ImageView控件,所以需要声明为final 41 final ImageView imageview=(ImageView)view.findViewById(R.id.imageView1); 42 name.setText(list.get(position).get("name").toString()); 43 price.setText(list.get(position).get("price").toString()); 44 45 // 使用DownLoadImage,下载地址代表的图片 46 DownLoadImage downLoadImage=new DownLoadImage(CommonUri.PRODUCT_IMG+list.get(position).get("imageName").toString()); 47 // 使用回调接口,设置ImageView的图片 48 downLoadImage.loadImage(new ImageCallback() { 49 @Override 50 public void getDrawable(Drawable draw) { 51 imageview.setImageDrawable(draw); 52 } 53 }); 54 return view; 55 } 56 }
效果展示: