您的位置:首页 > 理论基础 > 计算机网络

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

2013-10-17 15:53 459 查看
上一篇文章说的是ListView展示本地的图片以及文本,这一篇说一下如何从网络获取图片以及文本来显示。事实上,一般是先获取Josn或sml数据,然后解释显示。我们先从网上获取xml,然后对其进行解析,最后显示在ListView上。具体步骤:

客户端发出请求,获取xml
客户端异步解析xml
ListView将解析完的数据显示

一、Android客户端



(1)xml布局文件

mainxml,就是一个ListView。

[java] view
plaincopy

<?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>

ListView的每一行的布局,list_raw.xml,看一下结构图:



[java] view
plaincopy

<?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

[java] view
plaincopy

<?xml version="1.0" encoding="utf-8"?>

<selector xmlns:android="http://schemas.android.com/apk/res/android">

<!-- Selector style for listrow -->

<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>



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

[java] view
plaincopy

<?xml version="1.0" encoding="utf-8"?>

<shape xmlns:android="http://schemas.android.com/apk/res/android"

android:shape="rectangle">

<!-- Gradient Bg for listrow -->

<gradient

android:startColor="#f1f1f2"

android:centerColor="#e7e7e8"

android:endColor="#cfcfcf"

android:angle="270" />

</shape>

3.gradient_bg_hover.xml 梯度风格在悬停状态

[java] view
plaincopy

<?xml version="1.0" encoding="utf-8"?>

<shape xmlns:android="http://schemas.android.com/apk/res/android"

android:shape="rectangle">

<!-- Gradient BgColor for listrow Selected -->

<gradient

android:startColor="#18d7e5"

android:centerColor="#16cedb"

android:endColor="#09adb9"

android:angle="270" />

</shape>

4.image_bg.xml 在图片周围的白色边条

[java] view
plaincopy

<?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

[java] view
plaincopy

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 extends BaseAdapter {

private Activity activity;

private ArrayList<HashMap<String, String>> data;

private static LayoutInflater inflater=null;

public ImageLoader imageLoader; //用来下载图片的类,后面有介绍

public LazyAdapter(Activity a, ArrayList<HashMap<String, String>> d) {

activity = a;

data=d;

inflater = (LayoutInflater)activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

imageLoader=new ImageLoader(activity.getApplicationContext());

}

public int getCount() {

return data.size();

}

public Object getItem(int position) {

return position;

}

public long getItemId(int position) {

return position;

}

public View getView(int position, 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 = new HashMap<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);

return vi;

<em> }

}</em>

②网络获取图片的类,ImageLoader.java:

[java] view
plaincopy

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=new MemoryCache();

FileCache fileCache;

private Map<ImageView, String> imageViews=Collections.synchronizedMap(new WeakHashMap<ImageView, String>());

ExecutorService executorService;

public ImageLoader(Context context){

fileCache=new FileCache(context);

executorService=Executors.newFixedThreadPool(5);

}

final int stub_id = R.drawable.no_image;

public void DisplayImage(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);

}

}

private void queuePhoto(String url, ImageView imageView)

{

PhotoToLoad p=new PhotoToLoad(url, imageView);

executorService.submit(new PhotosLoader(p));

}

private Bitmap getBitmap(String url)

{

File f=fileCache.getFile(url);

//从sd卡

Bitmap b = decodeFile(f);

if(b!=null)

return b;

//从网络

try {

Bitmap bitmap=null;

URL imageUrl = new URL(url);

HttpURLConnection conn = (HttpURLConnection)imageUrl.openConnection();

conn.setConnectTimeout(30000);

conn.setReadTimeout(30000);

conn.setInstanceFollowRedirects(true);

InputStream is=conn.getInputStream();

OutputStream os = new FileOutputStream(f);

Utils.CopyStream(is, os);

os.close();

bitmap = decodeFile(f);

return bitmap;

} catch (Exception ex){

ex.printStackTrace();

return null;

}

}

//解码图像用来减少内存消耗

private Bitmap decodeFile(File f){

try {

//解码图像大小

BitmapFactory.Options o = new BitmapFactory.Options();

o.inJustDecodeBounds = true;

BitmapFactory.decodeStream(new FileInputStream(f),null,o);

//找到正确的刻度值,它应该是2的幂。

final int REQUIRED_SIZE=70;

int width_tmp=o.outWidth, height_tmp=o.outHeight;

int scale=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 = new BitmapFactory.Options();

o2.inSampleSize=scale;

return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);

} catch (FileNotFoundException e) {}

return null;

}

/任务队列

private class PhotoToLoad

{

public String url;

public ImageView imageView;

public PhotoToLoad(String u, ImageView i){

url=u;

imageView=i;

}

}

class PhotosLoader implements Runnable {

PhotoToLoad photoToLoad;

PhotosLoader(PhotoToLoad photoToLoad){

this.photoToLoad=photoToLoad;

}

@Override

public void run() {

if(imageViewReused(photoToLoad))

return;

Bitmap bmp=getBitmap(photoToLoad.url);

memoryCache.put(photoToLoad.url, bmp);

if(imageViewReused(photoToLoad))

return;

BitmapDisplayer bd=new BitmapDisplayer(bmp, photoToLoad);

Activity a=(Activity)photoToLoad.imageView.getContext();

a.runOnUiThread(bd);

}

}

boolean imageViewReused(PhotoToLoad photoToLoad){

String tag=imageViews.get(photoToLoad.imageView);

if(tag==null || !tag.equals(photoToLoad.url))

return true;

return false;

}

//用于显示位图在UI线程

class BitmapDisplayer implements Runnable

{

Bitmap bitmap;

PhotoToLoad photoToLoad;

public BitmapDisplayer(Bitmap b, PhotoToLoad p){bitmap=b;photoToLoad=p;}

public void run()

{

if(imageViewReused(photoToLoad))

return;

if(bitmap!=null)

photoToLoad.imageView.setImageBitmap(bitmap);

else

photoToLoad.imageView.setImageResource(stub_id);

}

}

public void clearCache() {

memoryCache.clear();

fileCache.clear();

}

}

③xml解析,xml的解析有很多方法,这里采用进行dom方式的xml解析。

[java] view
plaincopy

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 {

// 构造方法

public XMLParser() {

}

/**

* 从URL获取XML使HTTP请求

* @param url string

* */

public String getXmlFromUrl(String url) {

String xml = null;

try {

// defaultHttpClient

DefaultHttpClient httpClient = new DefaultHttpClient();

HttpPost httpPost = new HttpPost(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();

}

return xml;

}

/**

* 获取XML DOM元素

* @param XML string

* */

public Document getDomElement(String xml){

Document doc = null;

DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();

try {

DocumentBuilder db = dbf.newDocumentBuilder();

InputSource is = new InputSource();

is.setCharacterStream(new StringReader(xml));

doc = db.parse(is);

} catch (ParserConfigurationException e) {

Log.e("Error: ", e.getMessage());

return null;

} catch (SAXException e) {

Log.e("Error: ", e.getMessage());

return null;

} catch (IOException e) {

Log.e("Error: ", e.getMessage());

return null;

}

return doc;

}

/** 获取节点值

* @param elem element

*/

public final String 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 ){

return child.getNodeValue();

}

}

}

}

return "";

}

/**

* 获取节点值

* @param Element node

* @param key string

* */

public String getValue(Element item, String str) {

NodeList n = item.getElementsByTagName(str);

return this.getElementValue(n.item(0));

}

}

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

MemoryCache.java:

[java] view
plaincopy

import java.lang.ref.SoftReference;

import java.util.Collections;

import java.util.HashMap;

import java.util.Map;

import android.graphics.Bitmap;

public class MemoryCache {

private Map<String, SoftReference<Bitmap>> cache=Collections.synchronizedMap(new HashMap<String, SoftReference<Bitmap>>());//软引用

public Bitmap get(String id){

if(!cache.containsKey(id))

return null;

SoftReference<Bitmap> ref=cache.get(id);

return ref.get();

}

public void put(String id, Bitmap bitmap){

cache.put(id, new SoftReference<Bitmap>(bitmap));

}

public void clear() {

cache.clear();

}

}

FileCache.java

[java] view
plaincopy

import java.io.File;

import android.content.Context;

public class FileCache {

private File cacheDir;

public FileCache(Context context){

//找一个用来缓存图片的路径

if (android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED))

cacheDir=new File(android.os.Environment.getExternalStorageDirectory(),"LazyList");

else

cacheDir=context.getCacheDir();

if(!cacheDir.exists())

cacheDir.mkdirs();

}

public File getFile(String url){

String filename=String.valueOf(url.hashCode());

File f = new File(cacheDir, filename);

return f;

}

public void clear(){

File[] files=cacheDir.listFiles();

if(files==null)

return;

for(File f:files)

f.delete();

}

}

⑤还有一个读取流的工具类,Utils.java:

[java] view
plaincopy

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){}

}

}

还可以像下面这样表达,方法是一样的,就是表达形式上不同:

[java] view
plaincopy

public static byte[] readStream(InputStream inStream) throws Exception{

ByteArrayOutputStream outSteam = new ByteArrayOutputStream();

byte[] buffer = new byte[1024];

int len = -1;

while( (len=inStream.read(buffer)) != -1){

outSteam.write(buffer, 0, len);

}

outSteam.close();

inStream.close();

return outSteam.toByteArray();

}

}

最后就是主Activity的代码了,

[java] view
plaincopy

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 extends Activity {

// 所有的静态变量

static final String URL = "http://api.androidhive.info/music/music.xml";//xml目的地址,打开地址看一下

// XML 节点

static final String KEY_SONG = "song"; // parent node

static final String KEY_ID = "id";

static final String KEY_TITLE = "title";

static final String KEY_ARTIST = "artist";

static final String KEY_DURATION = "duration";

static final String KEY_THUMB_URL = "thumb_url";

ListView list;

LazyAdapter adapter;

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

ArrayList<HashMap<String, String>> songsList = new ArrayList<HashMap<String, String>>();

XMLParser parser = new XMLParser();

String xml = parser.getXmlFromUrl(URL); // 从网络获取xml

Document doc = parser.getDomElement(xml); // 获取 DOM 节点

NodeList nl = doc.getElementsByTagName(KEY_SONG);

// 循环遍历所有的歌节点 <song>

for (int i = 0; i < nl.getLength(); i++) {

// 新建一个 HashMap

HashMap<String, String> map = new HashMap<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=new LazyAdapter(this, songsList);

list.setAdapter(adapter);

//为单一列表行添加单击事件

list.setOnItemClickListener(new OnItemClickListener() {

@Override

public void onItemClick(AdapterView<?> parent, View view,

int position, long id) {

//这里可以自由发挥,比如播放一首歌曲等等

}

});

}

}

最后看一下效果:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息