这两天写了个小程序,使用了从网络读取xml数据,并显示在ListView中。

这里面有几个关键点:

  • 从网络读取数据
  • SAX解析xml
  • 异步填充ListView

先看下截图: 






非常简单的界面哈



为了方便,我再自己的服务器上,放了一个xml文件,其内容主要是:


<?xml version="1.0"?>
<products>
	<product>
		<price>100</price>
		<name>android dev</name>
		<image src="image/android1.png"/>
	</product>
	<product>
		<price>100</price>
		<name>androiddev2</name>
		<image src="image/android1.png"/>
	</product>
<!-- 接着重复下去 .... -->
</products>



该程序,首先用一个AsyncTask新启一个线程,来下载和解析xml;和主线程通过Handler对象来通讯。





下载XML文件



通过HTTPGet来下载xml。这时apache提供的包,需要首先包含如下包:



import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;



代码如下:(使用Get方法)


protected String doInBackground(String... params) {
        	HttpGet httpRequest = new HttpGet(params[0]); //从url 创建一个HttpGet对象
        	HttpClient httpclient = new DefaultHttpClient();
        	
        	//mShowHtml.setText("");
        	try {
        		HttpResponse httpResponse = httpclient.execute(httpRequest);
        		
        		if(httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK){//获取http的返回值代码
        			HttpEntity entitiy = httpResponse.getEntity();
        			
        			InputStream in = entitiy.getContent();//获得内容
        			//解析xml,下面详述
        			InputSource source = new InputSource(in);
        			
        			SAXParserFactory sax = SAXParserFactory.newInstance();
        			XMLReader xmlReader = sax.newSAXParser().getXMLReader();
        			
        			xmlReader.setContentHandler(new ProductHandler());
        			
        			xmlReader.parse(source);
        			
        		}
        		else {
        			//return "请求失败!";
        			//mShowHtml.setText("请求失败");
        			//Message mymsg = mMainHandler.obtainMessage();
    				//mymsg.obj = "请求失败";
    				//mMainHandler.sendMessage(mymsg);
        		}
        	
        	}catch(IOException e){
        		e.printStackTrace();
        	}catch(SAXException e) {
        		e.printStackTrace();
        	}catch(ParserConfigurationException e) {
        		e.printStackTrace();
        	}
        	return null;
    	}




解析XML


使用SAX的解析方法,先包含必须得包


import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;


解析的代码,正如上面所示:

//InputSource是解析源,可以通过一个InputStream创建
	         InputSource source = new InputSource(in);        			
                 SAXParserFactory sax = SAXParserFactory.newInstance();
         	 XMLReader xmlReader = sax.newSAXParser().getXMLReader();                			 
                 xmlReader.setContentHandler(new ProductHandler());//ProductHandler是解析的句柄                		
 	         xmlReader.parse(source);

SAX主要使用ContentHandler接口来传递解析好得数据,不过,更经常使用的是DefaultHandler


class ProductHandler extends DefaultHandler { //从 DefaultHandler继承即可
    	private ProductInfo curProduct;
    	private String content;
    	//解析到一个标签时调用
    	public void startElement(String uri, String localName, String name, org.xml.sax.Attributes attributes) throws SAXException {
    		if(localName.equals("product")) {//遇到product标签,进行解析
    			curProduct = new ProductInfo();
    		}
    		else if(localName.equals("image")) {
    			//set name
    			curProduct.image = attributes.getValue("src");//提取image src属性
    		}
    		super.startElement(uri, localName, name, attributes);
    	}
    	
    	public void endElement(String uri, String localName, String name) throws SAXException{
    		if(localName.equals("product")) {//当解析到一个标签结束时,发送一个消息,把Product类作为参数传递
    			//send event
    			//get main handler
    			Message msg = mMainHandler.obtainMessage();
    			msg.obj = curProduct;
    			mMainHandler.sendMessage(msg);
    			//Log.i("Product:", curProduct.toString());
    		}
    		else if(localName.equals("name")) {
    			//set name
    			curProduct.name = content;//保存名字
    		}
    		else if(localName.equals("price")) {
    			curProduct.price = Float.parseFloat(content);//保存价格

    		}
    		super.endElement(uri, localName, name);
    	}
    	//这里获取具体的内容,是标签下保存的文本
    	public void characters (char[] ch, int start, int length)  throws SAXException
    	{
    		content = new String(ch, start, length);
    		//Log.i("Parser:" ,content);
    		super.characters(ch, start, length);
    	}
    }



class ProductInfo {
    	public String name;
    	public float price;
    	public String image;
    	public String toString() {
    		return "\nName:" + name +"\nPrice :" + price + "\nImage:" + image;
    	}
    }



在AsyncTask中执行以上代码


public class GetHttpTask extends AsyncTask<String, Integer, String> {
   
    	public GetHttpTask() {
       	}
    	
    	protected void onPreExecute() { //在进入线程之前执行。该函数在调用者线程内执行
    		
    	}
    	
    	protected String doInBackground(String... params) {//线程的执行主体
        	HttpGet httpRequest = new HttpGet(params[0]);
  .................... //主要执行下载和解析的嗲吗
        	return null;
    	}
    	
    	protected void onPostExecute(String result) {//完成后调用
    		
    	}
    }

在主线程中,调用GetHttpTask的execute方法就可以执行

btn.setOnClickListener(new View.OnClickListener() {//在onCreate函数中调用
			
			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub
				httpGet();
			}
		});

httpGet方法:

void httpGet() {
    	GetHttpTask task = new GetHttpTask();
    	task.execute("http://192.168.1.111:8080/nfcdemo/products.xml");
    }






异步传递消息


上面的例子中,在endElement函数中,发送了消息,下面是接收消息


在主Activity中,声明了一个Handler mHandler对象,并在 onCreate时,这样做


mMainHandler = new Handler() {
        	public  void handleMessage(Message msg) {
        		//mShowHtml.append((String)msg.obj);
        		if(msg.obj != null) {
        			ProductInfo prod = (ProductInfo)msg.obj;
        			Log.i("Prodcut:", prod.toString());
        			//mShowHtml.append(prod.toString());
        			HashMap<String, Object> map = new HashMap<String, Object>();
        			map.put("name", prod.name);
        			map.put("price", "RMB" + prod.price);
        			mlist.add(map); //mlist保存了列表的具体数据
        			mProdList.notifyDataSetChanged();//mProdList是一个ListView对象,该函数引起ListView重读数据
        		}
        	}
        };


这个过程的要点基本如上,贴上所有代码吧!关于ListView的用法,可以参考


主代码:


package com.test.http;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParserFactory;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;

import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.SimpleAdapter;

public class HtmltestActivity extends Activity {
	EditText mUrlText;
	//EditText mShowHtml;
	ListView mProducts;
	Handler mMainHandler;
	SimpleAdapter mProdList;
	ArrayList<HashMap<String,Object>> mlist;
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        mUrlText = (EditText)findViewById(R.id.eturl);
        //mShowHtml = (EditText)findViewById(R.id.etshowhtml);
        mProducts = (ListView)findViewById(R.id.productList);
        
        Button btn = (Button)findViewById(R.id.btngo);
        
        
        mlist = new ArrayList<HashMap<String, Object>>();
        mProdList = new SimpleAdapter(this,
        		mlist,
        		R.layout.listitem,
        		new String[]{"name", "price"},
        		new int[]{R.id.prd_title, R.id.prd_price}
        		);
        
        mProducts.setAdapter(mProdList);
        
        
        btn.setOnClickListener(new View.OnClickListener() {
			
			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub
				httpGet();
			}
		});
        
        mMainHandler = new Handler() {
        	public  void handleMessage(Message msg) {
        		//mShowHtml.append((String)msg.obj);
        		if(msg.obj != null) {
        			ProductInfo prod = (ProductInfo)msg.obj;
        			Log.i("Prodcut:", prod.toString());
        			//mShowHtml.append(prod.toString());
        			HashMap<String, Object> map = new HashMap<String, Object>();
        			map.put("name", prod.name);
        			map.put("price", "RMB" + prod.price);
        			mlist.add(map);
        			mProdList.notifyDataSetChanged();
        		}
        	}
        };
        
    }
    
    void httpGet() {
    	GetHttpTask task = new GetHttpTask();
    	task.execute("http://192.168.1.111:8080/nfcdemo/products.xml");
    }
    
    public class GetHttpTask extends AsyncTask<String, Integer, String> {
   
    	public GetHttpTask() {
       	}
    	
    	protected void onPreExecute() {
    		
    	}
    	
    	protected String doInBackground(String... params) {
        	HttpGet httpRequest = new HttpGet(params[0]);
        	HttpClient httpclient = new DefaultHttpClient();
        	
        	//mShowHtml.setText("");
        	try {
        		HttpResponse httpResponse = httpclient.execute(httpRequest);
        		
        		if(httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK){
        			HttpEntity entitiy = httpResponse.getEntity();
        			
        			InputStream in = entitiy.getContent();
        			
        			InputSource source = new InputSource(in);
        			
        			SAXParserFactory sax = SAXParserFactory.newInstance();
        			XMLReader xmlReader = sax.newSAXParser().getXMLReader();
        			
        			xmlReader.setContentHandler(new ProductHandler());
        			
        			xmlReader.parse(source);
        			
        		}
        		else {
        			//return "请求失败!";
        			//mShowHtml.setText("请求失败");
        			//Message mymsg = mMainHandler.obtainMessage();
    				//mymsg.obj = "请求失败";
    				//mMainHandler.sendMessage(mymsg);
        		}
        	
        	}catch(IOException e){
        		e.printStackTrace();
        	}catch(SAXException e) {
        		e.printStackTrace();
        	}catch(ParserConfigurationException e) {
        		e.printStackTrace();
        	}
        	return null;
    	}
    	
    	protected void onPostExecute(String result) {
    		//mShowHtml.setText(result);
    	}
    }
    
    class ProductInfo {
    	public String name;
    	public float price;
    	public String image;
    	public String toString() {
    		return "\nName:" + name +"\nPrice :" + price + "\nImage:" + image;
    	}
    }
    
    class ProductHandler extends DefaultHandler {
    	private ProductInfo curProduct;
    	private String content;
    	
    	public void startElement(String uri, String localName, String name, org.xml.sax.Attributes attributes) throws SAXException {
    		if(localName.equals("product")) {
    			curProduct = new ProductInfo();
    		}
    		else if(localName.equals("image")) {
    			//set name
    			curProduct.image = attributes.getValue("src");
    		}
    		super.startElement(uri, localName, name, attributes);
    	}
    	
    	public void endElement(String uri, String localName, String name) throws SAXException{
    		if(localName.equals("product")) {
    			//send event
    			//get main handler
    			Message msg = mMainHandler.obtainMessage();
    			msg.obj = curProduct;
    			mMainHandler.sendMessage(msg);
    			//Log.i("Product:", curProduct.toString());
    		}
    		else if(localName.equals("name")) {
    			//set name
    			curProduct.name = content;
    		}
    		else if(localName.equals("price")) {
    			curProduct.price = Float.parseFloat(content);
    		}
    		super.endElement(uri, localName, name);
    	}
    	
    	public void characters (char[] ch, int start, int length)  throws SAXException
    	{
    		content = new String(ch, start, length);
    		//Log.i("Parser:" ,content);
    		super.characters(ch, start, length);
    	}
    }
}




main.xml

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

    <LinearLayout
        android:id="@+id/linearLayout1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >




        <EditText
            android:id="@+id/eturl"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1" >

            <requestFocus />
        </EditText>


        <Button
            android:id="@+id/btngo"
            android:layout_width="100dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="Go!" />

    </LinearLayout>



    <ListView
        android:id="@+id/productList"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >

    </ListView>

</LinearLayout>



listitem.xml


<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >


    <ImageView
        android:id="@+id/product_icon"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:src="@drawable/ic_launcher" />

    <TextView
        android:id="@+id/prd_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_toRightOf="@+id/product_icon"
        android:text="TextView" />


    <TextView
        android:id="@+id/prd_price"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_alignParentTop="true"
        android:text="TextView" />

</RelativeLayout>