上一篇文章说的是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的适配器,这部分可以参考上一篇文章,LazyAdapter.java
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:
- import java.io.InputStream;
- import java.io.OutputStream;
- public class Utils {
- public static void CopyStream(InputStream is, OutputStream os)
- {
- final int buffer_size=1024;
- try
- {
- byte[] bytes=new byte[buffer_size];
- for(;;)
- {
- int count=is.read(bytes, 0, buffer_size);
- if(count==-1)
- break;
- os.write(bytes, 0, count);
- }
- is.close();
- os.close();
- }
- catch(Exception ex){}
- }
- }
还可以像下面这样表达,方法是一样的,就是表达形式上不同:
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 }
最后看一下效果:
转载:http://blog.csdn.net/wangjinyu501/article/details/8219317