上一篇文章说的是ListView展示本地的图片以及文本,这一篇说一下如何从网络获取图片以及文本来显示。事实上,一般是先获取Josn或sml数据,然后解释显示。我们先从网上获取xml,然后对其进行解析,最后显示在ListView上。具体步骤:
- 客户端发出请求,获取xml
- 客户端异步解析xml
- ListView将解析完的数据显示
一、Android客户端
(1)xml布局文件
mainxml,就是一个ListView。
1 <?xml version="1.0" encoding="utf-8"?>
2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
3 android:layout_width="fill_parent"
4 android:layout_height="fill_parent"
5 android:orientation="vertical">
6
7 <ListView
8 android:id="@+id/list"
9 android:layout_width="fill_parent"
10 android:layout_height="wrap_content"
11 android:divider="#b5b5b5"
12 android:dividerHeight="1dp"
13 android:listSelector="@drawable/list_selector" />
14
15 </LinearLayout>
ListView的每一行的布局,list_raw.xml,看一下结构图:
1 <?xml version="1.0" encoding="utf-8"?>
2 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
3 android:layout_width="fill_parent"
4 android:layout_height="wrap_content"
5 android:background="@drawable/list_selector"
6 android:orientation="horizontal"
7 android:padding="5dip" >
8
9 <!-- ListView最左边的缩略图 -->
10 <LinearLayout android:id="@+id/thumbnail"
11 android:layout_width="wrap_content"
12 android:layout_height="wrap_content"
13 android:padding="3dip"
14 android:layout_alignParentLeft="true"
15 android:background="@drawable/image_bg"
16 android:layout_marginRight="5dip">
17
18 <ImageView
19 android:id="@+id/list_image"
20 android:layout_width="50dip"
21 android:layout_height="50dip"
22 android:src="@drawable/rihanna"/>
23
24 </LinearLayout>
25
26 <!-- 歌曲名-->
27 <TextView
28 android:id="@+id/title"
29 android:layout_width="wrap_content"
30 android:layout_height="wrap_content"
31 android:layout_alignTop="@+id/thumbnail"
32 android:layout_toRightOf="@+id/thumbnail"
33 android:text="Rihanna Love the way lie"
34 android:textColor="#040404"
35 android:typeface="sans"
36 android:textSize="15dip"
37 android:textStyle="bold"/>
38
39 <!-- 歌手名 -->
40 <TextView
41 android:id="@+id/artist"
42 android:layout_width="fill_parent"
43 android:layout_height="wrap_content"
44 android:layout_below="@id/title"
45 android:textColor="#343434"
46 android:textSize="10dip"
47 android:layout_marginTop="1dip"
48 android:layout_toRightOf="@+id/thumbnail"
49 android:text="Just gona stand there and ..." />
50
51 <!-- 歌曲播放时间 -->
52 <TextView
53 android:id="@+id/duration"
54 android:layout_width="wrap_content"
55 android:layout_height="wrap_content"
56 android:layout_alignParentRight="true"
57 android:layout_alignTop="@id/title"
58 android:gravity="right"
59 android:text="5:45"
60 android:layout_marginRight="5dip"
61 android:textSize="10dip"
62 android:textColor="#10bcc9"
63 android:textStyle="bold"/>
64
65 <!-- 进入播放 -->
66 <ImageView android:layout_width="wrap_content"
67 android:layout_height="wrap_content"
68 android:src="@drawable/arrow"
69 android:layout_alignParentRight="true"
70 android:layout_centerVertical="true"/>
71
72 </RelativeLayout>
另外我们打算使用几个特效,一个是当点击列表项目的时候,项目背景色改变,其实就是一个selector;另一个就是用shape美化视觉效果,具体看xml代码:
1.list_selector.xml
1 <?xml version="1.0" encoding="utf-8"?>
2 <selector xmlns:android="http://schemas.android.com/apk/res/android">
3 <!-- Selector style for listrow -->
4 <item
5 android:state_selected="false"
6 android:state_pressed="false"
7 android:drawable="@drawable/gradient_bg" />
8 <item android:state_pressed="true"
9 android:drawable="@drawable/gradient_bg_hover" />
10 <item android:state_selected="true"
11 android:state_pressed="false"
12 android:drawable="@drawable/gradient_bg_hover" />
13 </selector>
2.gradient_bg.xml,是默认背景梯度风格
1 <?xml version="1.0" encoding="utf-8"?>
2 <shape xmlns:android="http://schemas.android.com/apk/res/android"
3 android:shape="rectangle">
4 <!-- Gradient Bg for listrow -->
5 <gradient
6 android:startColor="#f1f1f2"
7 android:centerColor="#e7e7e8"
8 android:endColor="#cfcfcf"
9 android:angle="270" />
10 </shape>
3.gradient_bg_hover.xml 梯度风格在悬停状态
1 <?xml version="1.0" encoding="utf-8"?>
2 <shape xmlns:android="http://schemas.android.com/apk/res/android"
3 android:shape="rectangle">
4 <!-- Gradient BgColor for listrow Selected -->
5 <gradient
6 android:startColor="#18d7e5"
7 android:centerColor="#16cedb"
8 android:endColor="#09adb9"
9 android:angle="270" />
10
11 </shape>
4.image_bg.xml 在图片周围的白色边条
1 <?xml version="1.0" encoding="utf-8"?>
2 <layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
3 <item>
4 <shape
5 android:shape="rectangle">
6 <stroke android:width="1dp" android:color="#dbdbdc" />
7 <solid android:color="#FFFFFF" />
8 </shape>
9 </item>
10 </layer-list>
以上效果基本上都用到了shape,对此不了解的可以去查看相关资料。上面就是全部的xml布局文件,下面将开始写代码。
(2)主要代码
代码部分主要涉及到一下几个功能,重写ListView的适配器(BaseAdapter),从网络获取图片,图片缓存的处理,xml的解析。
①重写ListView的适配器
1 import java.util.ArrayList;
2 import java.util.HashMap;
3 import android.app.Activity;
4 import android.content.Context;
5 import android.view.LayoutInflater;
6 import android.view.View;
7 import android.view.ViewGroup;
8 import android.widget.BaseAdapter;
9 import android.widget.ImageView;
10 import android.widget.TextView;
11
12 public class LazyAdapter extends BaseAdapter {
13
14 private Activity activity;
15 private ArrayList<HashMap<String, String>> data;
16 private static LayoutInflater inflater=null;
17 public ImageLoader imageLoader; //用来下载图片的类,后面有介绍
18
19 public LazyAdapter(Activity a, ArrayList<HashMap<String, String>> d) {
20 activity = a;
21 data=d;
22 inflater = (LayoutInflater)activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
23 imageLoader=new ImageLoader(activity.getApplicationContext());
24 }
25
26 public int getCount() {
27 return data.size();
28 }
29
30 public Object getItem(int position) {
31 return position;
32 }
33
34 public long getItemId(int position) {
35 return position;
36 }
37
38 public View getView(int position, View convertView, ViewGroup parent) {
39 View vi=convertView;
40 if(convertView==null)
41 vi = inflater.inflate(R.layout.list_row, null);
42
43 TextView title = (TextView)vi.findViewById(R.id.title); // 标题
44 TextView artist = (TextView)vi.findViewById(R.id.artist); // 歌手名
45 TextView duration = (TextView)vi.findViewById(R.id.duration); // 时长
46 ImageView thumb_image=(ImageView)vi.findViewById(R.id.list_image); // 缩略图
47
48 HashMap<String, String> song = new HashMap<String, String>();
49 song = data.get(position);
50
51 // 设置ListView的相关值
52 title.setText(song.get(CustomizedListView.KEY_TITLE));
53 artist.setText(song.get(CustomizedListView.KEY_ARTIST));
54 duration.setText(song.get(CustomizedListView.KEY_DURATION));
55 imageLoader.DisplayImage(song.get(CustomizedListView.KEY_THUMB_URL), thumb_image);
56 return vi;
57 <em> }
58 }</em>
②网络获取图片的类,ImageLoader.java:
1 import java.io.File;
2 import java.io.FileInputStream;
3 import java.io.FileNotFoundException;
4 import java.io.FileOutputStream;
5 import java.io.InputStream;
6 import java.io.OutputStream;
7 import java.net.HttpURLConnection;
8 import java.net.URL;
9 import java.util.Collections;
10 import java.util.Map;
11 import java.util.WeakHashMap;
12 import java.util.concurrent.ExecutorService;
13 import java.util.concurrent.Executors;
14 import android.app.Activity;
15 import android.content.Context;
16 import android.graphics.Bitmap;
17 import android.graphics.BitmapFactory;
18 import android.widget.ImageView;
19
20 public class ImageLoader {
21
22 MemoryCache memoryCache=new MemoryCache();
23 FileCache fileCache;
24 private Map<ImageView, String> imageViews=Collections.synchronizedMap(new WeakHashMap<ImageView, String>());
25 ExecutorService executorService;
26
27 public ImageLoader(Context context){
28 fileCache=new FileCache(context);
29 executorService=Executors.newFixedThreadPool(5);
30 }
31
32 final int stub_id = R.drawable.no_image;
33 public void DisplayImage(String url, ImageView imageView)
34 {
35 imageViews.put(imageView, url);
36 Bitmap bitmap=memoryCache.get(url);
37 if(bitmap!=null)
38 imageView.setImageBitmap(bitmap);
39 else
40 {
41 queuePhoto(url, imageView);
42 imageView.setImageResource(stub_id);
43 }
44 }
45
46 private void queuePhoto(String url, ImageView imageView)
47 {
48 PhotoToLoad p=new PhotoToLoad(url, imageView);
49 executorService.submit(new PhotosLoader(p));
50 }
51
52 private Bitmap getBitmap(String url)
53 {
54 File f=fileCache.getFile(url);
55
56 //从sd卡
57 Bitmap b = decodeFile(f);
58 if(b!=null)
59 return b;
60
61 //从网络
62 try {
63 Bitmap bitmap=null;
64 URL imageUrl = new URL(url);
65 HttpURLConnection conn = (HttpURLConnection)imageUrl.openConnection();
66 conn.setConnectTimeout(30000);
67 conn.setReadTimeout(30000);
68 conn.setInstanceFollowRedirects(true);
69 InputStream is=conn.getInputStream();
70 OutputStream os = new FileOutputStream(f);
71 Utils.CopyStream(is, os);
72 os.close();
73 bitmap = decodeFile(f);
74 return bitmap;
75 } catch (Exception ex){
76 ex.printStackTrace();
77 return null;
78 }
79 }
80
81 //解码图像用来减少内存消耗
82 private Bitmap decodeFile(File f){
83 try {
84 //解码图像大小
85 BitmapFactory.Options o = new BitmapFactory.Options();
86 o.inJustDecodeBounds = true;
87 BitmapFactory.decodeStream(new FileInputStream(f),null,o);
88
89 //找到正确的刻度值,它应该是2的幂。
90 final int REQUIRED_SIZE=70;
91 int width_tmp=o.outWidth, height_tmp=o.outHeight;
92 int scale=1;
93 while(true){
94 if(width_tmp/2<REQUIRED_SIZE || height_tmp/2<REQUIRED_SIZE)
95 break;
96 width_tmp/=2;
97 height_tmp/=2;
98 scale*=2;
99 }
100
101 BitmapFactory.Options o2 = new BitmapFactory.Options();
102 o2.inSampleSize=scale;
103 return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
104 } catch (FileNotFoundException e) {}
105 return null;
106 }
107
108 /任务队列
109 private class PhotoToLoad
110 {
111 public String url;
112 public ImageView imageView;
113 public PhotoToLoad(String u, ImageView i){
114 url=u;
115 imageView=i;
116 }
117 }
118
119 class PhotosLoader implements Runnable {
120 PhotoToLoad photoToLoad;
121 PhotosLoader(PhotoToLoad photoToLoad){
122 this.photoToLoad=photoToLoad;
123 }
124
125 @Override
126 public void run() {
127 if(imageViewReused(photoToLoad))
128 return;
129 Bitmap bmp=getBitmap(photoToLoad.url);
130 memoryCache.put(photoToLoad.url, bmp);
131 if(imageViewReused(photoToLoad))
132 return;
133 BitmapDisplayer bd=new BitmapDisplayer(bmp, photoToLoad);
134 Activity a=(Activity)photoToLoad.imageView.getContext();
135 a.runOnUiThread(bd);
136 }
137 }
138
139 boolean imageViewReused(PhotoToLoad photoToLoad){
140 String tag=imageViews.get(photoToLoad.imageView);
141 if(tag==null || !tag.equals(photoToLoad.url))
142 return true;
143 return false;
144 }
145
146 //用于显示位图在UI线程
147 class BitmapDisplayer implements Runnable
148 {
149 Bitmap bitmap;
150 PhotoToLoad photoToLoad;
151 public BitmapDisplayer(Bitmap b, PhotoToLoad p){bitmap=b;photoToLoad=p;}
152 public void run()
153 {
154 if(imageViewReused(photoToLoad))
155 return;
156 if(bitmap!=null)
157 photoToLoad.imageView.setImageBitmap(bitmap);
158 else
159 photoToLoad.imageView.setImageResource(stub_id);
160 }
161 }
162
163 public void clearCache() {
164 memoryCache.clear();
165 fileCache.clear();
166 }
167
168 }
③xml解析,xml的解析有很多方法,这里采用进行dom方式的xml解析。
1 import java.io.IOException;
2 import java.io.StringReader;
3 import java.io.UnsupportedEncodingException;
4 import javax.xml.parsers.DocumentBuilder;
5 import javax.xml.parsers.DocumentBuilderFactory;
6 import javax.xml.parsers.ParserConfigurationException;
7 import org.apache.http.HttpEntity;
8 import org.apache.http.HttpResponse;
9 import org.apache.http.client.ClientProtocolException;
10 import org.apache.http.client.methods.HttpPost;
11 import org.apache.http.impl.client.DefaultHttpClient;
12 import org.apache.http.util.EntityUtils;
13 import org.w3c.dom.Document;
14 import org.w3c.dom.Element;
15 import org.w3c.dom.Node;
16 import org.w3c.dom.NodeList;
17 import org.xml.sax.InputSource;
18 import org.xml.sax.SAXException;
19 import android.util.Log;
20
21 public class XMLParser {
22
23 // 构造方法
24 public XMLParser() {
25
26 }
27
28 /**
29 * 从URL获取XML使HTTP请求
30 * @param url string
31 * */
32 public String getXmlFromUrl(String url) {
33 String xml = null;
34
35 try {
36 // defaultHttpClient
37 DefaultHttpClient httpClient = new DefaultHttpClient();
38 HttpPost httpPost = new HttpPost(url);
39
40 HttpResponse httpResponse = httpClient.execute(httpPost);
41 HttpEntity httpEntity = httpResponse.getEntity();
42 xml = EntityUtils.toString(httpEntity);
43
44 } catch (UnsupportedEncodingException e) {
45 e.printStackTrace();
46 } catch (ClientProtocolException e) {
47 e.printStackTrace();
48 } catch (IOException e) {
49 e.printStackTrace();
50 }
51 return xml;
52 }
53
54 /**
55 * 获取XML DOM元素
56 * @param XML string
57 * */
58 public Document getDomElement(String xml){
59 Document doc = null;
60 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
61 try {
62
63 DocumentBuilder db = dbf.newDocumentBuilder();
64
65 InputSource is = new InputSource();
66 is.setCharacterStream(new StringReader(xml));
67 doc = db.parse(is);
68
69 } catch (ParserConfigurationException e) {
70 Log.e("Error: ", e.getMessage());
71 return null;
72 } catch (SAXException e) {
73 Log.e("Error: ", e.getMessage());
74 return null;
75 } catch (IOException e) {
76 Log.e("Error: ", e.getMessage());
77 return null;
78 }
79
80 return doc;
81 }
82
83 /** 获取节点值
84 * @param elem element
85 */
86 public final String getElementValue( Node elem ) {
87 Node child;
88 if( elem != null){
89 if (elem.hasChildNodes()){
90 for( child = elem.getFirstChild(); child != null; child = child.getNextSibling() ){
91 if( child.getNodeType() == Node.TEXT_NODE ){
92 return child.getNodeValue();
93 }
94 }
95 }
96 }
97 return "";
98 }
99
100 /**
101 * 获取节点值
102 * @param Element node
103 * @param key string
104 * */
105 public String getValue(Element item, String str) {
106 NodeList n = item.getElementsByTagName(str);
107 return this.getElementValue(n.item(0));
108 }
109 }
④程序缓存的处理,主要是内存缓存+文件缓存。内存缓存中网上很多是采用SoftReference来防止堆溢出:
MemoryCache.java:
1 import java.lang.ref.SoftReference;
2 import java.util.Collections;
3 import java.util.HashMap;
4 import java.util.Map;
5 import android.graphics.Bitmap;
6
7 public class MemoryCache {
8 private Map<String, SoftReference<Bitmap>> cache=Collections.synchronizedMap(new HashMap<String, SoftReference<Bitmap>>());//软引用
9
10 public Bitmap get(String id){
11 if(!cache.containsKey(id))
12 return null;
13 SoftReference<Bitmap> ref=cache.get(id);
14 return ref.get();
15 }
16
17 public void put(String id, Bitmap bitmap){
18 cache.put(id, new SoftReference<Bitmap>(bitmap));
19 }
20
21 public void clear() {
22 cache.clear();
23 }
24 }
FileCache.java
1 import java.io.File;
2 import android.content.Context;
3
4 public class FileCache {
5
6 private File cacheDir;
7
8 public FileCache(Context context){
9 //找一个用来缓存图片的路径
10 if (android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED))
11 cacheDir=new File(android.os.Environment.getExternalStorageDirectory(),"LazyList");
12 else
13 cacheDir=context.getCacheDir();
14 if(!cacheDir.exists())
15 cacheDir.mkdirs();
16 }
17
18 public File getFile(String url){
19
20 String filename=String.valueOf(url.hashCode());
21 File f = new File(cacheDir, filename);
22 return f;
23
24 }
25
26 public void clear(){
27 File[] files=cacheDir.listFiles();
28 if(files==null)
29 return;
30 for(File f:files)
31 f.delete();
32 }
33
34 }
⑤还有一个读取流的工具类,Utils.java:
1. import java.io.InputStream;
2. import java.io.OutputStream;
3.
4.
5. public class Utils {
6. public static void CopyStream(InputStream is, OutputStream os)
7. {
8. final int buffer_size=1024;
9. try
10. {
11. byte[] bytes=new byte[buffer_size];
12. for(;;)
13. {
14. int count=is.read(bytes, 0, buffer_size);
15. if(count==-1)
16. break;
17. os.write(bytes, 0, count);
18. }
19. is.close();
20. os.close();
21. }
22. catch(Exception ex){}
23. }
24. }
还可以像下面这样表达,方法是一样的,就是表达形式上不同:
1 public static byte[] readStream(InputStream inStream) throws Exception{
2
3 ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
4 byte[] buffer = new byte[1024];
5 int len = -1;
6 while( (len=inStream.read(buffer)) != -1){
7 outSteam.write(buffer, 0, len);
8
9 }
10 outSteam.close();
11 inStream.close();
12 return outSteam.toByteArray();
13
14 }
15 }
最后就是主Activity的代码了,
1 package com.example.androidhive;
2
3 import java.util.ArrayList;
4 import java.util.HashMap;
5 import org.w3c.dom.Document;
6 import org.w3c.dom.Element;
7 import org.w3c.dom.NodeList;
8 import android.app.Activity;
9 import android.os.Bundle;
10 import android.view.View;
11 import android.widget.AdapterView;
12 import android.widget.AdapterView.OnItemClickListener;
13 import android.widget.ListView;
14
15 public class CustomizedListView extends Activity {
16 // 所有的静态变量
17 static final String URL = "http://api.androidhive.info/music/music.xml";//xml目的地址,打开地址看一下
18 // XML 节点
19 static final String KEY_SONG = "song"; // parent node
20 static final String KEY_ID = "id";
21 static final String KEY_TITLE = "title";
22 static final String KEY_ARTIST = "artist";
23 static final String KEY_DURATION = "duration";
24 static final String KEY_THUMB_URL = "thumb_url";
25
26 ListView list;
27 LazyAdapter adapter;
28
29 @Override
30 public void onCreate(Bundle savedInstanceState) {
31 super.onCreate(savedInstanceState);
32 setContentView(R.layout.main);
33
34
35 ArrayList<HashMap<String, String>> songsList = new ArrayList<HashMap<String, String>>();
36
37 XMLParser parser = new XMLParser();
38 String xml = parser.getXmlFromUrl(URL); // 从网络获取xml
39 Document doc = parser.getDomElement(xml); // 获取 DOM 节点
40
41 NodeList nl = doc.getElementsByTagName(KEY_SONG);
42 // 循环遍历所有的歌节点 <song>
43 for (int i = 0; i < nl.getLength(); i++) {
44 // 新建一个 HashMap
45 HashMap<String, String> map = new HashMap<String, String>();
46 Element e = (Element) nl.item(i);
47 //每个子节点添加到HashMap关键= >值
48 map.put(KEY_ID, parser.getValue(e, KEY_ID));
49 map.put(KEY_TITLE, parser.getValue(e, KEY_TITLE));
50 map.put(KEY_ARTIST, parser.getValue(e, KEY_ARTIST));
51 map.put(KEY_DURATION, parser.getValue(e, KEY_DURATION));
52 map.put(KEY_THUMB_URL, parser.getValue(e, KEY_THUMB_URL));
53
54 // HashList添加到数组列表
55 songsList.add(map);
56 }
57
58
59 list=(ListView)findViewById(R.id.list);
60
61
62 adapter=new LazyAdapter(this, songsList);
63 list.setAdapter(adapter);
64
65
66 //为单一列表行添加单击事件
67
68 list.setOnItemClickListener(new OnItemClickListener() {
69
70 @Override
71 public void onItemClick(AdapterView<?> parent, View view,
72 int position, long id) {
73
74 //这里可以自由发挥,比如播放一首歌曲等等
75 }
76 });
77 }
78 }
最后看一下效果: