Android ListView从网络获取图片及文字显示

来源:互联网 发布:淘宝首页添加链接代码 编辑:程序博客网 时间:2024/04/30 02:33
这一篇说一下如何从网络获取图片以及文本来显示。事实上,一般是先获取Josn或sml数据,然后解释显示。我们先从网上获取xml,然后对其进行解析,最后显示在ListView上。具体步骤:
  • 客户端发出请求,获取xml
  • 客户端异步解析xml
  • ListView将解析完的数据显示
      一、Android客户端
1.jpg

(1)xml布局文件
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
<?xml version="1.0"encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical">
          
    <ListView
        android:id="@+id/list"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:divider="#b5b5b5"
        android:dividerHeight="1dp"
        android:listSelector="@drawable/list_selector"/>
  
</LinearLayout>


2.jpg

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
<?xml version="1.0"encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:background="@drawable/list_selector"
    android:orientation="horizontal"
    android:padding="5dip">
  
        <!--  ListView最左边的缩略图 -->
        <LinearLayout android:id="@+id/thumbnail"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:padding="3dip"              
        android:layout_alignParentLeft="true"
        android:background="@drawable/image_bg"
            android:layout_marginRight="5dip">
          
                <ImageView    
                android:id="@+id/list_image"  
                android:layout_width="50dip"
                android:layout_height="50dip"
                android:src="@drawable/rihanna"/>
          
        </LinearLayout>
      
        <!-- 歌曲名-->
    <TextView
        android:id="@+id/title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignTop="@+id/thumbnail"
        android:layout_toRightOf="@+id/thumbnail"
        android:text="Rihanna Love the way lie"
        android:textColor="#040404"
        android:typeface="sans"
        android:textSize="15dip"
        android:textStyle="bold"/>
  
        <!-- 歌手名 -->
    <TextView
        android:id="@+id/artist"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/title"
        android:textColor="#343434"
        android:textSize="10dip"
        android:layout_marginTop="1dip"
        android:layout_toRightOf="@+id/thumbnail"
        android:text="Just gona stand there and ..."/>
  
        <!-- 歌曲播放时间 -->
    <TextView
        android:id="@+id/duration"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_alignTop="@id/title"
        android:gravity="right"
        android:text="5:45"
        android:layout_marginRight="5dip"
        android:textSize="10dip"
        android:textColor="#10bcc9"
        android:textStyle="bold"/>
        
     <!-- 进入播放 -->   
     <ImageView android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:src="@drawable/arrow"
             android:layout_alignParentRight="true"
             android:layout_centerVertical="true"/>
  
</RelativeLayout>


另外我们打算使用几个特效,一个是当点击列表项目的时候,项目背景色改变,其实就是一个selector;另一个就是用shape美化视觉效果,具体看xml代码:

1.list_selector.xml
01
02
03
04
05
06
07
08
09
10
11
12
13
<?xml version="1.0"encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Selector styleforlistrow -->
<item
 android:state_selected="false"
    android:state_pressed="false"
    android:drawable="@drawable/gradient_bg"/>
<item android:state_pressed="true"
    android:drawable="@drawable/gradient_bg_hover"/>
<item android:state_selected="true"
 android:state_pressed="false"
    android:drawable="@drawable/gradient_bg_hover"/>
</selector>


3.jpg     

2.gradient_bg.xml,是默认背景梯度风格

01
02
03
04
05
06
07
08
09
10
<?xml version="1.0"encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
  <!--  Gradient Bgforlistrow -->
  <gradient
      android:startColor="#f1f1f2"
      android:centerColor="#e7e7e8"
      android:endColor="#cfcfcf"
      android:angle="270"/>
</shape>


3.gradient_bg_hover.xml 梯度风格在悬停状态
01
02
03
04
05
06
07
08
09
10
11
<?xml version="1.0"encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
  <!-- Gradient BgColorforlistrow Selected -->
  <gradient
      android:startColor="#18d7e5"
      android:centerColor="#16cedb"
      android:endColor="#09adb9"
      android:angle="270"/>
    
</shape>


4.image_bg.xml 在图片周围的白色边条
01
02
03
04
05
06
07
08
09
10
<?xml version="1.0"encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
      <shape
        android:shape="rectangle">
            <stroke android:width="1dp"android:color="#dbdbdc"/>
            <solid android:color="#FFFFFF"/>
        </shape>
   </item>
</layer-list>


以上效果基本上都用到了shape,对此不了解的可以去查看相关资料。上面就是全部的xml布局文件,下面将开始写代码。



(2)主要代码

代码部分主要涉及到一下几个功能,重写ListView的适配器(BaseAdapter),从网络获取图片,图片缓存的处理,xml的解析。
①重写ListView的适配器,这部分可以参考上一篇文章,LazyAdapter.java   
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
import java.util.ArrayList;
import java.util.HashMap;
import android.app.Activity;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
  
public class LazyAdapter extendsBaseAdapter {
      
    privateActivity activity;
    privateArrayList<HashMap<String, String>> data;
    privatestaticLayoutInflater inflater=null;
    publicImageLoader imageLoader;//用来下载图片的类,后面有介绍
      
    publicLazyAdapter(Activity a, ArrayList<HashMap<String, String>> d) {
        activity = a;
        data=d;
        inflater = (LayoutInflater)activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        imageLoader=newImageLoader(activity.getApplicationContext());
    }
  
    publicintgetCount() {
        returndata.size();
    }
  
    publicObject getItem(intposition) {
        returnposition;
    }
  
    publiclonggetItemId(intposition) {
        returnposition;
    }
      
    publicView getView(intposition, View convertView, ViewGroup parent) {
        View vi=convertView;
        if(convertView==null)
            vi = inflater.inflate(R.layout.list_row,null);
  
        TextView title = (TextView)vi.findViewById(R.id.title);// 标题
        TextView artist = (TextView)vi.findViewById(R.id.artist);// 歌手名
        TextView duration = (TextView)vi.findViewById(R.id.duration);// 时长
        ImageView thumb_image=(ImageView)vi.findViewById(R.id.list_image);// 缩略图
          
        HashMap<String, String> song =newHashMap<String, String>();
        song = data.get(position);
          
        // 设置ListView的相关值
        title.setText(song.get(CustomizedListView.KEY_TITLE));
        artist.setText(song.get(CustomizedListView.KEY_ARTIST));
        duration.setText(song.get(CustomizedListView.KEY_DURATION));
        imageLoader.DisplayImage(song.get(CustomizedListView.KEY_THUMB_URL), thumb_image);
        returnvi;
  <EM>  }
}</EM>


②网络获取图片的类,ImageLoader.java:   
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Collections;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; 
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.widget.ImageView;
    
public class ImageLoader {
    
    MemoryCache memoryCache=newMemoryCache();
    FileCache fileCache;
    privateMap<ImageView, String> imageViews=Collections.synchronizedMap(newWeakHashMap<ImageView, String>());
    ExecutorService executorService; 
    
    publicImageLoader(Context context){
        fileCache=newFileCache(context);
        executorService=Executors.newFixedThreadPool(5);
    }
    
    finalintstub_id = R.drawable.no_image;
    publicvoidDisplayImage(String url, ImageView imageView)
    {
        imageViews.put(imageView, url);
        Bitmap bitmap=memoryCache.get(url);
        if(bitmap!=null)
            imageView.setImageBitmap(bitmap);
        else
        {
            queuePhoto(url, imageView);
            imageView.setImageResource(stub_id);
        }
    }
    
    privatevoidqueuePhoto(String url, ImageView imageView)
    {
        PhotoToLoad p=newPhotoToLoad(url, imageView);
        executorService.submit(newPhotosLoader(p));
    }
    
    privateBitmap getBitmap(String url)
    {
        File f=fileCache.getFile(url);
    
        //从sd卡
        Bitmap b = decodeFile(f);
        if(b!=null)
            returnb;
    
        //从网络
        try{
            Bitmap bitmap=null;
            URL imageUrl =newURL(url);
            HttpURLConnection conn = (HttpURLConnection)imageUrl.openConnection();
            conn.setConnectTimeout(30000);
            conn.setReadTimeout(30000);
            conn.setInstanceFollowRedirects(true);
            InputStream is=conn.getInputStream();
            OutputStream os =newFileOutputStream(f);
            Utils.CopyStream(is, os);
            os.close();
            bitmap = decodeFile(f);
            returnbitmap;
        }catch(Exception ex){
           ex.printStackTrace();
           returnnull;
        }
    }
    
    //解码图像用来减少内存消耗
    privateBitmap decodeFile(File f){
        try{
            //解码图像大小
            BitmapFactory.Options o =newBitmapFactory.Options();
            o.inJustDecodeBounds =true;
            BitmapFactory.decodeStream(newFileInputStream(f),null,o);
    
            //找到正确的刻度值,它应该是2的幂。
            finalintREQUIRED_SIZE=70;
            intwidth_tmp=o.outWidth, height_tmp=o.outHeight;
            intscale=1;
            while(true){
                if(width_tmp/2<REQUIRED_SIZE || height_tmp/2<REQUIRED_SIZE)
                    break;
                width_tmp/=2;
                height_tmp/=2;
                scale*=2;
            }
    
            BitmapFactory.Options o2 =newBitmapFactory.Options();
            o2.inSampleSize=scale;
            returnBitmapFactory.decodeStream(newFileInputStream(f),null, o2);
        }catch(FileNotFoundException e) {}
        returnnull;
    }
    
    /任务队列
    privateclassPhotoToLoad
    {
        publicString url;
        publicImageView imageView;
        publicPhotoToLoad(String u, ImageView i){
            url=u;
            imageView=i;
        }
    }
    
    classPhotosLoaderimplementsRunnable {
        PhotoToLoad photoToLoad;
        PhotosLoader(PhotoToLoad photoToLoad){
            this.photoToLoad=photoToLoad;
        }
    
        @Override
        publicvoidrun() {
            if(imageViewReused(photoToLoad))
                return;
            Bitmap bmp=getBitmap(photoToLoad.url);
            memoryCache.put(photoToLoad.url, bmp);
            if(imageViewReused(photoToLoad))
                return;
            BitmapDisplayer bd=newBitmapDisplayer(bmp, photoToLoad);
            Activity a=(Activity)photoToLoad.imageView.getContext();
            a.runOnUiThread(bd);
        }
    }
    
    booleanimageViewReused(PhotoToLoad photoToLoad){
        String tag=imageViews.get(photoToLoad.imageView);
        if(tag==null|| !tag.equals(photoToLoad.url))
            returntrue;
        returnfalse;
    }
    
    //用于显示位图在UI线程
    classBitmapDisplayerimplementsRunnable
    {
        Bitmap bitmap;
        PhotoToLoad photoToLoad;
        publicBitmapDisplayer(Bitmap b, PhotoToLoad p){bitmap=b;photoToLoad=p;}
        publicvoidrun()
        {
            if(imageViewReused(photoToLoad))
                return;
            if(bitmap!=null)
                photoToLoad.imageView.setImageBitmap(bitmap);
            else
                photoToLoad.imageView.setImageResource(stub_id);
        }
    }
    
    publicvoidclearCache() {
        memoryCache.clear();
        fileCache.clear();
    }
    
}


③xml解析,xml的解析有很多方法,这里采用进行dom方式的xml解析。
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
import java.io.IOException;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;  
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;  
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException; 
import android.util.Log;
    
public class XMLParser {
    
    // 构造方法
    publicXMLParser() {
    
    }
    
    /**
     * 从URL获取XML使HTTP请求
     * @param url string
     * */
    publicString getXmlFromUrl(String url) {
        String xml =null;
    
        try{
            // defaultHttpClient
            DefaultHttpClient httpClient =newDefaultHttpClient();
            HttpPost httpPost =newHttpPost(url);
    
            HttpResponse httpResponse = httpClient.execute(httpPost);
            HttpEntity httpEntity = httpResponse.getEntity();
            xml = EntityUtils.toString(httpEntity);
    
        }catch(UnsupportedEncodingException e) {
            e.printStackTrace();
        }catch(ClientProtocolException e) {
            e.printStackTrace();
        }catch(IOException e) {
            e.printStackTrace();
        }
        returnxml;
    }
    
    /**
     * 获取XML DOM元素
     * @param XML string
     * */
    publicDocument getDomElement(String xml){
        Document doc =null;
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        try{
    
            DocumentBuilder db = dbf.newDocumentBuilder();
    
            InputSource is =newInputSource();
                is.setCharacterStream(newStringReader(xml));
                doc = db.parse(is); 
    
            }catch(ParserConfigurationException e) {
                Log.e("Error: ", e.getMessage());
                returnnull;
            }catch(SAXException e) {
                Log.e("Error: ", e.getMessage());
                returnnull;
            }catch(IOException e) {
                Log.e("Error: ", e.getMessage());
                returnnull;
            }
    
            returndoc;
    }
    
    /** 获取节点值
      * @param elem element
      */
     publicfinalString getElementValue( Node elem ) {
         Node child;
         if( elem !=null){
             if(elem.hasChildNodes()){
                 for( child = elem.getFirstChild(); child !=null; child = child.getNextSibling() ){
                     if( child.getNodeType() == Node.TEXT_NODE  ){
                         returnchild.getNodeValue();
                     }
                 }
             }
         }
         return"";
     }
    
     /**
      * 获取节点值
      * @param Element node
      * @param key string
      * */
     publicString getValue(Element item, String str) {
            NodeList n = item.getElementsByTagName(str);
            returnthis.getElementValue(n.item(0));
        }
}


④程序缓存的处理,主要是内存缓存+文件缓存。内存缓存中网上很多是采用SoftReference来防止堆溢出:

MemoryCache.java:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import java.lang.ref.SoftReference;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import android.graphics.Bitmap;
  
public class MemoryCache {
    privateMap<String, SoftReference<Bitmap>> cache=Collections.synchronizedMap(newHashMap<String, SoftReference<Bitmap>>());//软引用
      
    publicBitmap get(String id){
        if(!cache.containsKey(id))
            returnnull;
        SoftReference<Bitmap> ref=cache.get(id);
        returnref.get();
    }
      
    publicvoidput(String id, Bitmap bitmap){
        cache.put(id,newSoftReference<Bitmap>(bitmap));
    }
  
    publicvoidclear() {
        cache.clear();
    }
}


FileCache.java
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import java.io.File;
import android.content.Context;
  
public class FileCache {
      
    privateFile cacheDir;
      
    publicFileCache(Context context){
        //找一个用来缓存图片的路径
        if(android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED))
            cacheDir=newFile(android.os.Environment.getExternalStorageDirectory(),"LazyList");
        else
            cacheDir=context.getCacheDir();
        if(!cacheDir.exists())
            cacheDir.mkdirs();
    }
      
    publicFile getFile(String url){
          
        String filename=String.valueOf(url.hashCode());
        File f =newFile(cacheDir, filename);
        returnf;
          
    }
      
    publicvoidclear(){
        File[] files=cacheDir.listFiles();
        if(files==null)
            return;
        for(File f:files)
            f.delete();
    }
  
}


⑤还有一个读取流的工具类,Utils.java:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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=newbyte[buffer_size];
 for(;;)
 {
 intcount=is.read(bytes,0, buffer_size);
if(count==-1)
break;
os.write(bytes, 0, count);
is.close();
os.close();
 }
}
catch(Exception ex){}
 }
}


还可以像下面这样表达,方法是一样的,就是表达形式上不同:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
public static byte[] readStream(InputStream inStream)throwsException{
  
ByteArrayOutputStream outSteam =newByteArrayOutputStream();
byte[] buffer =newbyte[1024];
int len = -1;
while( (len=inStream.read(buffer)) != -1){
outSteam.write(buffer,0, len);
  
}
outSteam.close();
inStream.close();
return outSteam.toByteArray();
  
}
}


最后就是主Activity的代码了,
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
package com.example.androidhive;
  
import java.util.ArrayList;
import java.util.HashMap;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ListView;
  
public class CustomizedListView extendsActivity {
        // 所有的静态变量
        staticfinalString URL = "http://api.androidhive.info/music/music.xml";//xml目的地址,打开地址看一下
        // XML 节点
        staticfinalString KEY_SONG ="song";// parent node
        staticfinalString KEY_ID = "id";
        staticfinalString KEY_TITLE ="title";
        staticfinalString KEY_ARTIST ="artist";
        staticfinalString KEY_DURATION ="duration";
        staticfinalString KEY_THUMB_URL ="thumb_url";
          
        ListView list;
        LazyAdapter adapter;
  
        @Override
        publicvoidonCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                setContentView(R.layout.main);
                  
  
                ArrayList<HashMap<String, String>> songsList =newArrayList<HashMap<String, String>>();
  
                XMLParser parser =newXMLParser();
                String xml = parser.getXmlFromUrl(URL);// 从网络获取xml
                Document doc = parser.getDomElement(xml);// 获取 DOM 节点
                  
                NodeList nl = doc.getElementsByTagName(KEY_SONG);
                // 循环遍历所有的歌节点 <song>
                for(inti = 0; i < nl.getLength(); i++) {
                        // 新建一个 HashMap
                        HashMap<String, String> map =newHashMap<String, String>();
                        Element e = (Element) nl.item(i);
                        //每个子节点添加到HashMap关键= >值
                        map.put(KEY_ID, parser.getValue(e, KEY_ID));
                        map.put(KEY_TITLE, parser.getValue(e, KEY_TITLE));
                        map.put(KEY_ARTIST, parser.getValue(e, KEY_ARTIST));
                        map.put(KEY_DURATION, parser.getValue(e, KEY_DURATION));
                        map.put(KEY_THUMB_URL, parser.getValue(e, KEY_THUMB_URL));
  
                        // HashList添加到数组列表
                        songsList.add(map);
                }
                  
  
                list=(ListView)findViewById(R.id.list);
                  
                  
              adapter=newLazyAdapter(this, songsList);       
              list.setAdapter(adapter);
          
  
        //为单一列表行添加单击事件
  
        list.setOnItemClickListener(newOnItemClickListener() {
  
                        @Override
                        publicvoidonItemClick(AdapterView<?> parent, View view,
                                        intposition,long id) {
                                                          
                        //这里可以自由发挥,比如播放一首歌曲等等
                        }
                });               
        }       
}


最后看一下效果:
4.jpg 
原创粉丝点击