构建 Android(安卓)手机 RSS 阅读器
最近开始学习android,使用的资料是IBM developerWorks的android开发的文章,个人觉得对android学习有很大的参考价值。在ibm中国上有中文版,但不知道是否翻译上的疏漏,还是由于android1.5版本以后的差异,文章中的代码在调试时总是有这样那样的问题(哪怕是一步一步照着文章做)。所以自己对其中的内容进行了一些整理,使后来者少走弯路。
一、构建Android 手机 RSS 阅读器
1、在eclipse中新建andriod project,工程名:rss,sdk:android1.6,activity:main。
2、打开droid draw,设计一个界面,generate xml代码如下:
<?xmlversion="1.0"encoding="utf-8"?>
<LinearLayout
android:id="@+id/widget28"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
xmlns:android="http://schemas.android.com/apk/res/android"
>
<TextView
android:id="@+id/feedtitle"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Android RSSReader"
>
</TextView>
<TextView
android:id="@+id/feedpubdate"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
>
</TextView>
<ListView
android:id="@+id/itemlist"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
>
</ListView>
</LinearLayout>
将xml代码复制粘贴到main.xml中(原来的内容删除)。
3、新建类RSSItem,这是个pojo类,映射了rss中的item元素:
publicclassRSSItem {
privateString_title=null;
privateString_description=null;
privateString_link=null;
privateString_category=null;
privateString_pubdate=null;
RSSItem()
{
}
voidsetTitle(String title)
{
_title= title;
}
voidsetDescription(String description)
{
_description= description;
}
voidsetLink(String link)
{
_link= link;
}
voidsetCategory(String category)
{
_category= category;
}
voidsetPubDate(String pubdate)
{
_pubdate= pubdate;
}
String getTitle()
{
return_title;
}
String getDescription()
{
return_description;
}
String getLink()
{
return_link;
}
String getCategory()
{
return_category;
}
String getPubDate()
{
return_pubdate;
}
publicString toString()
{
// limit how much text you display
if(_title.length() > 42)
{
return_title.substring(0, 42) +"...";
}
return_title;
}
}
4、新建pojo类RSSFeed,映射rss中的channel元素:
publicclassRSSFeed
{
privateString_title=null;
privateString_pubdate=null;
privateint_itemcount= 0;
privateList<RSSItem>_itemlist;
RSSFeed()
{
_itemlist=newVector<RSSItem>(0);
}
intaddItem(RSSItem item)
{
_itemlist.add(item);
_itemcount++;
return_itemcount;
}
RSSItem getItem(intlocation)
{
return_itemlist.get(location);
}
List<RSSItem> getAllItems()
{
return_itemlist;
}
intgetItemCount()
{
return_itemcount;
}
voidsetTitle(String title)
{
_title= title;
}
voidsetPubDate(String pubdate)
{
_pubdate= pubdate;
}
String getTitle()
{
return_title;
}
String getPubDate()
{
return_pubdate;
}
}
5、新建helper类RSSHandler,用于对rss进行xml解析,并将解析结果包装为RSSFeed和RSSItem对象,方便在ui界面中显示:
publicclassRSSHandlerextendsDefaultHandler{//继承DefaultHandler,方便进行sax解析
RSSFeed_feed;//临时变量,用于保存解析过程中的channel
RSSItem_item;//临时变量,用于保存解析过程中的item
//标记变量,用于标记在解析过程中我们关心的几个标签
intcurrentstate= 0;//若不是我们关心的标签,记做0
finalintRSS_TITLE= 1;//若是title标签,记做1,注意有两个title,但我们都保存在_item的title成员变量中
finalintRSS_LINK= 2;//若是link标签,记做2
finalintRSS_DESCRIPTION= 3;//若是description标签,记做3
finalintRSS_CATEGORY= 4;//若是category标签,记做4
finalintRSS_PUBDATE= 5;//若是pubdate标签,记做5,注意有两个pubdate,但我们都保存在_item的pubdate成员变量中
RSSHandler()
{
}
RSSFeed getFeed()//通过这个方法把解析结果封装在RSSFeed对象中并返回
{
return_feed;
}
//下面通过重载DefaultHandler的5个方法来实现sax解析
publicvoidstartDocument()throwsSAXException
{//这个方法在解析xml文档的一开始执行,一般我们需要在该方法中初始化解析过程中有可能用到的变量
_feed=newRSSFeed();
_item=newRSSItem();
}
publicvoidendDocument()throwsSAXException
{//这个方法在整个xml文档解析结束时执行,一般需要在该方法中返回或保存整个文档解析解析结果,但由于
//我们已经在解析过程中把结果保持在_feed中,所以这里什么也不做
}
publicvoidstartElement(String namespaceURI, String localName,String qName,
Attributes atts)throwsSAXException
{//这个方法在解析标签开始标记时执行,一般我们需要在该方法取得标签属性值,但由于我们的rss文档
//中并没有任何我们关心的标签属性,因此我们主要在这里进行的是设置标记变量currentstate,以
//标记我们处理到哪个标签
if(localName.equals("channel"))
{//channel这个标签没有任何值得我们关心的内容,所以currentstate置为0
currentstate= 0;
return;
}
if(localName.equals("image"))
{//如果是image这个标签,说明channel元素的子元素title和pubdate都已经解析出来了
//(参考xml文件结构),那么应该把它们的值从_item转移到_feed,因为我们只是图方便才
//把它们存放在_item的成员中,实际上它们应该是_feed的成员
_feed.setTitle(_item.getTitle());
_feed.setPubDate(_item.getPubDate());
}
if(localName.equals("item"))
{//若是item标签,则重新构造一个RSSItem,从而把已有(已经解析过的)item数据扔掉,当
//然事先是已经保存到_feed的itemlist集合中了
_item=newRSSItem();
return;
}
if(localName.equals("title"))
{//若是title标签,置currentstate为1,表明这是我们关心的数据,这样在characters
//方法中会把元素内容保存到_item变量中
currentstate=RSS_TITLE;
return;
}
if(localName.equals("description"))
{//若是description标签,置currentstate为3,表明这是我们关心的数据,这样在characters
//方法中会把元素内容保存到_item变量中
currentstate=RSS_DESCRIPTION;
return;
}
if(localName.equals("link"))
{//若是description标签,置currentstate为2,表明这是我们关心的数据,这样在characters
//方法中会把元素内容保存到_item变量中
currentstate=RSS_LINK;
return;
}
if(localName.equals("category"))
{//若是description标签,置currentstate为4,表明这是我们关心的数据,这样在characters
//方法中会把元素内容保存到_item变量中
currentstate=RSS_CATEGORY;
return;
}
if(localName.equals("pubDate"))
{//若是description标签,置currentstate为5,表明这是我们关心的数据,这样在characters
//方法中会把元素内容保存到_item变量中
currentstate=RSS_PUBDATE;
return;
}
currentstate= 0;//如果不是上面列出的任何标签,置currentstate为0,我们不关心
}
publicvoidcharacters(charch[],intstart,intlength)
{//这个方法在解析标签内容(即开始标记-结束标记之间的部分)时执行,一般我们在里这获取元素体内容
String theString =newString(ch,start,length);//获取元素体内容
Log.i("RSSReader","characters["+ theString +"]");
switch(currentstate)//根据currentstate标记判断这个元素体是属于我们关心的哪个元素
{
caseRSS_TITLE://若是title元素,放入_item的title属性
_item.setTitle(theString);
currentstate= 0;
break;
caseRSS_LINK://若是link元素,放入_item的link属性
_item.setLink(theString);
currentstate= 0;
break;
caseRSS_DESCRIPTION://若是description元素,放入_item的description属性
_item.setDescription(theString);
currentstate= 0;
break;
caseRSS_CATEGORY://若是category元素,放入_item的category属性
_item.setCategory(theString);
currentstate= 0;
break;
caseRSS_PUBDATE://若是pubdate元素,放入_item的pubdate属性
_item.setPubDate(theString);
currentstate= 0;
break;
default:
return;
}
}
publicvoidendElement(String namespaceURI, String localName, String qName)
throwsSAXException
{//这个方法在解析标签结束标记时执行,一般我们需要在该方法保存元素内容
if(localName.equals("item"))
{//item标签解析结束,把_item保存到_feed的itemlist属性中
_feed.addItem(_item);
return;
}
}
}
6、修改main.java,调用前面的类,从intentert获取rss列表并显示在ui上:
publicclassmainextendsActivityimplementsOnItemClickListener{
privateRSSFeedfeed=null;
privateStringtag=this.getClass().getName();
@Override
publicvoidonCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
//调用getFeed方法,从服务器取得rss提要,strings.xml中定义了r.string.RSSFEEDOFCHOICE
feed= getFeed(this.getString(R.string.RSSFEEDOFCHOICE));
//把rss内容绑定到ui界面进行显示
UpdateDisplay();
}
privateRSSFeed getFeed(String urlToRssFeed)
{//该方法通过url获得xml并解析xml内容为RSSFeed对象
try//异常处理
{
RSSHandler theRssHandler=newRSSHandler();
URL url =newURL(urlToRssFeed);
//构建Sax解析工厂
SAXParserFactory factory = SAXParserFactory.newInstance();
//使用Sax解析工厂构建Sax解析器
SAXParser parser = factory.newSAXParser();
//使用Sax解析器构建xmlReader
XMLReader xmlreader = parser.getXMLReader();
//构建自定义的RSSHandler作为xmlReader的处理器(或代理)
xmlreader.setContentHandler(theRssHandler);
//使用url打开流,并将流作为xmlReader的输入源并解析
xmlreader.parse(newInputSource(url.openStream()));
//将解析结果作为RSSFeed对象返回
returntheRssHandler.getFeed();
}
catch(Exception ee)
{
returnnull;
}
}
privatevoidUpdateDisplay()
{
TextView feedtitle = (TextView) findViewById(R.id.feedtitle);
TextView feedpubdate = (TextView) findViewById(R.id.feedpubdate);
ListView itemlist = (ListView) findViewById(R.id.itemlist);
if(feed==null)
{
feedtitle.setText("No RSS Feed Available");
return;
}
//设置channel的标题和日期
feedtitle.setText(feed.getTitle());
feedpubdate.setText(feed.getPubDate());
//构建数组适配器,用于绑定listview
ArrayAdapter<RSSItem> adapter =new
ArrayAdapter<RSSItem>(this,android.R.layout.
simple_list_item_1,feed.getAllItems());
itemlist.setAdapter(adapter);//listview绑定适配器
itemlist.setSelection(0);
itemlist.setOnItemClickListener(this);//设置itemclick事件代理
}
//itemclick事件代理方法
publicvoidonItemClick(AdapterView<?> parent, View v,intposition,longid) {
Log.d(tag,"item clicked!");
//构建一个“意图”,用于指向activity:detail
Intent itemintent =newIntent(this,detail.class);
//构建buddle,并将要传递参数都放入buddle
Bundle b =newBundle();
b.putString("title",feed.getItem(position).getTitle());
b.putString("description",feed.getItem(position).getDescription());
b.putString("link","http://www.csdn.net");// feed.getItem(position).getLink());
b.putString("pubdate",feed.getItem(position).getPubDate());
//用android.intent.extra.INTENT的名字来传递参数
itemintent.putExtra("android.intent.extra.INTENT", b);
//把意图转给子activity
this.startActivityForResult(itemintent, 0);
//this.startActivity(itemintent);
}
}
到此,程序已经可以显示第1个activity(页面)了。但由于程序使用了网络,我们还必须在AndroidManifest.xml中增加使用网络的权限:
<uses-permissionandroid:name="android.permission.INTERNET"/>
否则,程序会提示Permission denied错误。
7、打开droid draw,设计第2个activity(页面)detail.xml:
<?xmlversion="1.0"encoding="utf-8"?>
<LinearLayout
android:id="@+id/widget28"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
xmlns:android="http://schemas.android.com/apk/res/android"
>
<TextView
android:id="@+id/storybox"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="TextView"
android:autoLink="all"
>
</TextView>
<Button
android:id="@+id/back"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="back"
>
</Button>
</LinearLayout>
8、同时在AndroidManifest.xml中增加这个activity的声明:
<activityandroid:name=".detail">
</activity>
9、新建class detail.java:
publicclassdetailextendsActivity {
publicvoidonCreate(Bundle icicle)
{
super.onCreate(icicle);
setContentView(R.layout.detail);//加载detail.xml作为本视图
String theStory =null;
//获取调用者的“意图”
Intent startingIntent = getIntent();
if(startingIntent !=null)
{
//通过调用者意图获取对应的“参数”,字符串android.intent.extra.INTENT与调用者指定的一致
Bundle b = startingIntent.getBundleExtra("android.intent.extra.INTENT");
if(b ==null)
{
theStory ="bad bundle?";
}
else
{//读取“参数”的内容
theStory = b.getString("title") +"/n/n"+ b.getString("pubdate")
+"/n/n"+ b.getString("description").replace('/n',' ')
+"/n/nMore information:/n"+ b.getString("link");
}
}
else
{
theStory ="Information Not Found.";
}
//构建textview并用参数值设置其text
TextView db= (TextView) findViewById(R.id.storybox);
db.setText(theStory);
//构建button并设置其onclicke事件的监听者(代理)
Button backbutton = (Button) findViewById(R.id.back);
backbutton.setOnClickListener(newButton.OnClickListener()
{
publicvoidonClick(View v)
{
finish();//结束本activity,返回给调用者
}
});
}
}
更多相关文章
- 安卓基础知识总结
- 【Android自助餐】Handler消息机制完全解析(二)MessageQueue的队列
- 我的Android进阶之旅------>Android中解析XML 技术详解---->SAX
- Android之父深入解析Android
- Google工程师解析Android系统架构
- Android中APK安装过程及原理解析
- Android(安卓)中自定义 Menu 资源
- 【Android(安卓)P】 JobScheduler服务源码解析(三)—— 使用Job需
- Android:图文解析带你快速了解RxJava的底层原理