紧接上一篇文章的内容,实现了RSS阅读器就可以直接阅读网上的xml内容了。
首先,根据xml设计布局文件:
main_activity:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<ListView
android:id="@+id/itemlist"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
/>
</RelativeLayout>
show_activity:
<?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" >
<TextView
android:id="@+id/content"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1.0"
android:autoLink="all"
android:text="" />
<Button
android:id="@+id/back"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="返回" />
</LinearLayout>
第二,完成实体类,用以直接操作实现:
RSSFeed类:
public class RSSFeed {
private String title;//标题
private String pubdate;//发布日期
private int itemcount;//用于计算列表的数目
private List<RSSItem> itemlist;//用于描述列表
public RSSFeed() {
//加入对象的创建
itemlist = new ArrayList<RSSItem>();
}
public int addItem(RSSItem item) {
itemlist.add(item);
itemcount++;
return itemcount;
}
//根据下标获取RssItem
public RSSItem getItem(int location) {
return itemlist.get(location);
}
//为ListView 设置HashMap<String,Object>
public List<HashMap<String, Object>> getAllItemsForListView() {
List<HashMap<String, Object>> data = new ArrayList<HashMap<String, Object>>();
int size = itemlist.size();
for (int i = 0; i < size; i++) {
HashMap<String, Object> item = new HashMap<String, Object>();
item.put(RSSItem.TITLE, itemlist.get(i).getTitle());
item.put(RSSItem.PUBDATE, itemlist.get(i).getPubdate());
data.add(item);
}
return data;
}
public int getItemCount() {
return itemlist.size();
}
public void setTitle(String title) {
this.title = title;
}
public void setPubDate(String pubdate) {
this.pubdate = pubdate;
}
public String getTitle() {
return title;
}
public String getPubDate() {
return pubdate;
}
public List<RSSItem> getRssItems() {
return itemlist;
}
}
RSSItem类:
public class RSSItem {
public static final String TITLE = "title";
public static final String PUBDATE = "pubdate";
private String title;
private String description;
private String link;
private String category;
private String pubdate;
public RSSItem() {
}
public String getTitle() {
if(title.length()>20){
return title.substring(0, 19)+"...";
}
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getLink() {
return link;
}
public void setLink(String link) {
this.link = link;
}
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category = category;
}
public String getPubdate() {
return pubdate;
}
public void setPubdate(String pubdate) {
this.pubdate = pubdate;
}
@Override
public String toString() {
return "RSSItem [title=" + title + ", description=" + description
+ ", link=" + link + ", category=" + category + ", pubdate="
+ pubdate + "]";
}
}
第三,实体解析类:
public class RssFeed_SAXParser {
public RSSFeed getFeed(String urlStr) throws MalformedURLException,
Exception, SAXException {// 需要穿一个URL地址
URL url = new URL(urlStr);
System.out.println("RssFeed_SAXParser-->url:" + url);
SAXParserFactory parserFactory = SAXParserFactory.newInstance();// 构建sax解析工厂
SAXParser saxParser = parserFactory.newSAXParser();// 解析工厂生产解析器
XMLReader xmlReader = saxParser.getXMLReader();// 通过saxParser构建xmlReader阅读器
// 构建自定义的xml解析器 作为 xmlReader的处理器(代理)
RssHandler rssHandler = new RssHandler();
xmlReader.setContentHandler(rssHandler);
// 使用url打开流,并将流作为 xmlReader解析的输入源并解析
InputSource is = new InputSource(url.openStream());
xmlReader.parse(is);
return rssHandler.getFeed();
}
}
实现ContentHandler(RssHandler类):
public class RssHandler extends DefaultHandler {
RSSFeed RSSFeed;// 用于保存解析过程中的channel
RSSItem RSSItem;// 用于保存解析过程中的item
String lastElementName = "";// 标记变量,用于标记在解析过程中我们关心的几个标签,若不是我们关心的标签记做0
final int RSS_TITLE = 1;// 若是 title 标签,记做1,注意有两个title,但我们都保存在item的成员变量中
final int RSS_LINK = 2;// 若是 link 标签,记做2
final int RSS_DESCRIPTION = 3;// 若是 description 标签,记做3
final int RSS_CATEGORY = 4;// 若是category标签,记做 4
final int RSS_PUBDATE = 5; // 若是pubdate标签,记做5,注意有两个pubdate,但我们都保存在item的pubdate成员变量中
int currentstate = 0;
public RssHandler() {
}
// 下面通过重载 DefaultHandler 的 5 个方法来实现 sax 解析
// 1. 这个方法在解析xml文档的一开始执行,一般我们需要在该方法中初始化解析过程中有可能用到的变量
public void startDocument() throws SAXException {
super.startDocument();
RSSFeed = new RSSFeed();
RSSItem = new RSSItem();
}
// 2. 当遇到文本结点时进行处理,空白符不用做处理,只需要对字符做处理
@Override
public void characters(char[] ch, int start, int length) {
String theString = new String(ch, start, length);
// 获取字符串
String text = new String(ch, start, length);
Log.i("i", "要获取的内容:"+text);
// 判断当前标志位 与那一种标志相同,然后做相应处理
switch (currentstate) {
case RSS_TITLE:
RSSItem.setTitle(text);
currentstate = 0;// 设置完后,重置为开始状态
break;
case RSS_LINK:
RSSItem.setLink(text);
currentstate = 0;// 设置完后,重置为开始状态
break;
case RSS_DESCRIPTION:
RSSItem.setDescription(text);
currentstate = 0;// 设置完后,重置为开始状态
break;
case RSS_CATEGORY:
RSSItem.setCategory(text);
currentstate = 0;// 设置完后,重置为开始状态
break;
case RSS_PUBDATE:
RSSItem.setPubdate(text);
currentstate = 0;// 设置完后,重置为开始状态
break;
default:
return;
}
}
/**
* 3. 这个方法在解析标签开始标记时执行,一般我们需要在该方法取得标签属性值,但由于我们的rss文档
* 中并没有任何我们关心的标签属性,因此我们主要在这里进行的是设置标记变量currentstate, 以 标记我们处理到哪个标签
*/
@Override
public void startElement(String uri, String localName, String qName,
Attributes attributes) throws SAXException {
super.startElement(uri, localName, qName, attributes);
// localName:不含命名空间前缀的标签名(建议使用)
// qName:含有命名空间前缀的标签名
// attributes:接收属性值
if (localName.equals("channel")) {
currentstate = 0;
return;
}
if (localName.equals("item")) {
RSSItem = new RSSItem();
return;
}
if (localName.equals("title")) {
currentstate = RSS_TITLE;
return;
}
if (localName.equals("description")) {
currentstate = RSS_DESCRIPTION;
return;
}
if (localName.equals("link")) {
currentstate = RSS_LINK;
return;
}
if (localName.equals("category")) {
currentstate = RSS_CATEGORY;
return;
}
if (localName.equals("pubdate")) {
currentstate = RSS_PUBDATE;
return;
}
currentstate = 0;
}
// 4. 结束元素节点
@Override
public void endElement(String uri, String localName, String qName)
throws SAXException {
// 如果解析一个item节点结束,就将RSSItem添加到RSSFeed中
if (localName.equals("item")) {
RSSFeed.addItem(RSSItem);
return;
}
}
@Override
public void endDocument() throws SAXException {
super.endDocument();
// 这个方法在整个xml文档解析结束时执行,一般需要在该方法中返回或保存整个文档解析解析结果,
// 但由于我们已经在解析过程中把结果保持在rssFeed中,所以这里什么也不做
}
public RSSFeed getRssFeed() {
return RSSFeed;
}
}
第四,Activity实现:
ActivityMain:
public class ActivityMain extends Activity implements OnItemClickListener {
public final String RSS_URL = "http://blog.sina.com.cn/rss/1267454277.xml";
public final String tag = "RSSReader";
private RSSFeed feed = null;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.activity_activity_main);
try {// 调用getFeed方法,从服务器取得rss提要
feed = new RssFeed_SAXParser().getFeed(RSS_URL);
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
showListView(); // 把rss内容绑定到ui界面进行显示
}
private void showListView() {
ListView itemlist = (ListView) findViewById(R.id.itemlist);
if (feed == null) {
setTitle("访问的RSS无效");
return;
}
SimpleAdapter adapter = new SimpleAdapter(this,
feed.getAllItemsForListView(),
android.R.layout.simple_list_item_2, new String[] {
RSSItem.TITLE, RSSItem.PUBDATE }, new int[] {
android.R.id.text1, android.R.id.text2 });
itemlist.setAdapter(adapter); // listview绑定适配器
itemlist.setOnItemClickListener(this); // 设置itemclick事件代理
itemlist.setSelection(0);
}
public void onItemClick(AdapterView parent, View v, int position, long id) {// itemclick事件代理方法{
Intent itemintent = new Intent(this, ActivityShowDescription.class);// 构建一个“意图”,用于指向activity
Bundle b = new Bundle();// 构建buddle,并将要传递参数都放入buddle
b.putString("title", feed.getItem(position).getTitle());
b.putString("description", feed.getItem(position).getDescription());
b.putString("link", feed.getItem(position).getLink());
b.putString("pubdate", feed.getItem(position).getPubdate());
itemintent.putExtra("Android.intent.extra.RSSItem", b); // 用android.intent.extra.INTENT的名字来传递参数
startActivityForResult(itemintent, 0);
}
}
ActivityShowDescription:
public class ActivityShowDescription extends Activity {
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.show_activity);
String content = null;
Intent startingIntent = getIntent();
if (startingIntent != null) {
Bundle bundle = startingIntent
.getBundleExtra("Android.intent.extra.RSSItem");
if (bundle == null) {
content = "不好意思程序出错啦";
} else {
content = bundle.getString("title") + "\n\n"
+ bundle.getString("pubdate") + "\n\n"
+ bundle.getString("description").replace('\n', ' ')
+ "\n\n详细信息请访问以下网址:\n" + bundle.getString("link");
}
} else {
content = "不好意思程序出错啦";
}
TextView textView = (TextView) findViewById(R.id.content);
textView.setText(content);
Button backbutton = (Button) findViewById(R.id.back);
backbutton.setOnClickListener(new Button.OnClickListener() {
public void onClick(View v) {
finish();
}
});
}
}
最后,运行前还要在AndroidManifest.xml中加入许可才能完成解析网上的xml文件:
<uses-permission android:name="android.permission.INTERNET" />
由于时间紧,具体的解析、结果、错误分析,请继续关注,以后会更新添加。
注意,如下两图中的Extra命名必须一致:
《1,ActivityMain中》
《2,showDescription中》