這兩天寫了個小程式,使用了從網路讀取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); } }
ProductInfo類的定義非常簡單
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函數中調用@Overridepublic void onClick(View v) {// TODO Auto-generated method stubhttpGet();}});
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的用法,可以參考http://blog.csdn.net/hellogv/article/details/4542668主代碼:
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() {@Overridepublic void onClick(View v) {// TODO Auto-generated method stubhttpGet();}}); 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>