RSS阅读器的Logo:
RSS阅读器是一种软件或是说一个程序,这种软件可以自由读取RSS和Atom两种规范格式的文档,且这种读取RSS和Atom文档的软件有多个版本,由不同的人或公司开发,有着不同的名字。如目前流行的有:RSSReader、Feeddemon、SharpReader等。这些软件能够实现大致相同的功能,其实质都是为了方便地读取RSS和Atom文档。Really Simple Syndication “聚合真的很简单”就是RSS的英文原意。把新闻标题、摘要(Feed)、内容按照用户的要求,“送”到用户的桌面就是RSS的目的。
注:下面给出的这个例子来自Google Android SDK开发,经过阅读,理解,并加上自己的和汇总写下这篇文章。
首先,给出实现本实例的截图,我用这个阅读器打开了我的博客。
1.
这是程序运行的主界面,实现这个界面的代码如下:
package irdc.ex08_13; /* import相关class */ import android.app.Activity; import android.app.AlertDialog; import android.content.DialogInterface; import android.content.Intent; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.EditText; public class EX08_13 extends Activity { /* 变量声明 */ private Button mButton; private EditText mEditText; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); /* 初始化对象 */ mEditText=(EditText) findViewById(R.id.myEdit); mButton=(Button) findViewById(R.id.myButton); /* 设定Button的onClick事件 */ mButton.setOnClickListener(new Button.OnClickListener() { @Override public void onClick(View v) { String path=mEditText.getText().toString(); if(path.equals("")) { showDialog("网址不可为空白!"); } else { /* new一个Intent对象,并指定class */ Intent intent = new Intent(); intent.setClass(EX08_13.this,EX08_13_1.class); /* new一个Bundle对象,并将要传递的数据传入 */ Bundle bundle = new Bundle(); bundle.putString("path",path); /* 将Bundle对象assign给Intent */ intent.putExtras(bundle); /* 调用Activity EX08_13_1 */ startActivityForResult(intent,0); } } }); } /* 重写 onActivityResult()*/ @Override protected void onActivityResult(int requestCode,int resultCode, Intent data) { switch (resultCode) { /* * 当由主程序唤起的EX08_13_1发生运行错误时,会返回resultCode=99的信息,主程序 * 在接收到信息时,将以Dialog显示错误信息 */ case 99: /* 回传错误时以Dialog显示 */ Bundle bunde = data.getExtras(); String error = bunde.getString("error"); showDialog(error); break; default: break; } } /* 显示Dialog的method */ private void showDialog(String mess){ new AlertDialog.Builder(EX08_13.this).setTitle("Message") .setMessage(mess) .setNegativeButton("确定", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { } }) .show(); } }
说明:
* 这是一个ListActivity,主要用来显示订阅的RSS内容列表
* 当程序被主程序唤醒时,会先从Bundle对象中取得主程序传入的RSS网址,再调用自定义的getRss()方法
* 对RSS网址发出request,并解析由服务器返回的XML格式的文件。
* getRss()是自定义的方法,它除了会将RSS的request传送出去,在接收到服务器response时,会以
* SAXParser作为解析XML的主要工具,利用自定义的MyHandler类来取得XML内的相关信息。
* 调用getRss()时,会返回一个List<News>,内存的每一笔数据就是一个News对象,也就是所有新闻的列表;
* 在取得List<News>之后,将List传入自定义的MyAdapter对象,再使用setListAdapter()将数据设置给ListView。
* 程序中重写ListActivity的onListItemClick(),当ListView中的任一个item被单击时,onClick()会被触发,
* 此时会先取得单击的News对象,并将相关信息传入Bundle中,再将Bundle对象assign给Intent,并唤起EX08_13_2这个
* Activity。
* 当程序发生错误时,会返回resultCode=99的信息给上一个Activity
下面给出实现代码:
public class EX08_13_1 extends ListActivity { /* 变量声明 */ private TextView mText; private String title=""; private List<News> li=new ArrayList<News>(); @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); /* 设定layout为newslist.xml */ setContentView(R.layout.newslist); mText=(TextView) findViewById(R.id.myText); /* 取得Intent中的Bundle对象 */ Intent intent=this.getIntent(); Bundle bunde = intent.getExtras(); /* 取得Bundle对象中的数据 */ String path = bunde.getString("path"); /* 调用getRss()取得解析后的List */ li=getRss(path); mText.setText(title); /* 设定自定义的MyAdapter */ setListAdapter(new MyAdapter(this,li)); } /* 设定ListItem被按下时要做的动作 */ @Override protected void onListItemClick(ListView l,View v,int position, long id) { /* 取得News对象 */ News ns=(News)li.get(position); /* new一个Intent对象,并指定class */ Intent intent = new Intent(); intent.setClass(EX08_13_1.this,EX08_13_2.class); /* new一个Bundle对象,并将要传递的数据传入 */ Bundle bundle = new Bundle(); bundle.putString("title",ns.getTitle()); bundle.putString("desc",ns.getDesc()); bundle.putString("link",ns.getLink()); /* 将Bundle对象assign给Intent */ intent.putExtras(bundle); /* 调用Activity EX08_13_2 */ startActivity(intent); } /* 剖析XML的method */ private List<News> getRss(String path) { List<News> data=new ArrayList<News>(); URL url = null; try { url = new URL(path); /* 产生SAXParser对象 */ SAXParserFactory spf = SAXParserFactory.newInstance(); SAXParser sp = spf.newSAXParser(); /* 产生XMLReader对象 */ XMLReader xr = sp.getXMLReader(); /* 设定自定义的MyHandler给XMLReader */ MyHandler myExampleHandler = new MyHandler(); xr.setContentHandler(myExampleHandler); /* 解析XML */ xr.parse(new InputSource(url.openStream())); /* 取得RSS标题与内容列表 */ data =myExampleHandler.getParsedData(); title=myExampleHandler.getRssTitle(); } catch (Exception e) { /* 发生错误时回传result回上一个activity */ Intent intent=new Intent(); Bundle bundle = new Bundle(); bundle.putString("error",""+e); intent.putExtras(bundle); /* 错误的回传值设定为99 */ EX08_13_1.this.setResult(99, intent); EX08_13_1.this.finish(); } return data; } }
* 自定义的Handler对象,用来解析XML文件,并取得文件中的相关信息
* RSS返回的XML文件中,每一则新闻内容的起始为<item>,结束为</item>,格式如下:
*
* <item>
* <title>新闻标题</title>
* <guid isPermaLink="false">ID</guid>
* <link>新闻连接</link>
* <description>新闻描述</description>
* <pubDate>发布时间</pubDate>
* </item>
*
* MyHandler在解析上述XML文件内容的流程如下:
* 1.运行startDocument(),生成一个List<News>,作为存放News对象之用
* 2.当程序解析到Element的起始时,会运行startElement(),程序依据Element名称的不同,将其所对应的boolean
* 参数设置为true。
* 3.当程序解析到文件中的字符时(character),会运行characters()这个方法,将字符附加到(append)StringBuffer中
* 4.当程序解析到Element的结尾时会运行endElement(),程序依据Element名称的不同,将由StringBuffer转换成的字符串,
* 以setXXX()的方式把该属性写入News对象中,并把相对应的boolean变量设置为false。
实现代码如下:
public class MyHandler extends DefaultHandler { /* 变量声明 */ private boolean in_item = false; //News对象的四个属性标记 private boolean in_title = false; private boolean in_link = false; private boolean in_desc = false; private boolean in_date = false; private boolean in_mainTitle = false; private List<News> li; private News news; private String title=""; private StringBuffer buf=new StringBuffer(); /* 将转换成List<News>的XML数据回传 */ public List<News> getParsedData() { return li; } /* 将解析出的RSS title回传 */ public String getRssTitle() { return title; } /* XML文件开始解析时调用此method */ @Override public void startDocument() throws SAXException { li = new ArrayList<News>(); } /* XML文件结束解析时调用此method */ @Override public void endDocument() throws SAXException { } /* 解析到Element的开头时调用此method */ @Override public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException { if (localName.equals("item")) { this.in_item = true; /* 解析到item的开头时new一个News对象 */ news=new News(); } else if (localName.equals("title")) { if(this.in_item) { this.in_title = true; } else { this.in_mainTitle = true; } } else if (localName.equals("link")) { if(this.in_item) { this.in_link = true; } } else if (localName.equals("description")) { if(this.in_item) { this.in_desc = true; } } else if (localName.equals("pubDate")) { if(this.in_item) { this.in_date = true; } } } /* 解析到Element的结尾时调用此method */ @Override public void endElement(String namespaceURI, String localName, String qName) throws SAXException { if (localName.equals("item")) { this.in_item = false; /* 解析到item的结尾时将News对象写入List中 */ li.add(news); } else if (localName.equals("title")) { if(this.in_item) { /* 设定News对象的title */ news.setTitle(buf.toString().trim()); buf.setLength(0); this.in_title = false; } else { /* 设定RSS的title */ title=buf.toString().trim(); buf.setLength(0); this.in_mainTitle = false; } } else if (localName.equals("link")) { if(this.in_item) { /* 设定News对象的link */ news.setLink(buf.toString().trim()); buf.setLength(0); this.in_link = false; } } else if (localName.equals("description")) { if(in_item) { /* 设定News对象的description */ news.setDesc(buf.toString().trim()); buf.setLength(0); this.in_desc = false; } } else if (localName.equals("pubDate")) { if(in_item) { /* 设定News对象的pubDate */ news.setDate(buf.toString().trim()); buf.setLength(0); this.in_date = false; } } } /* 取得Element的开头结尾中间夹的字符串 */ @Override public void characters(char ch[], int start, int length) { if(this.in_item||this.in_mainTitle) { /* 将char[]加入StringBuffer */ buf.append(ch,start,length); } } }
* 这是一个JavaBean的类,用来存放每一篇新闻的详细信息。简单的说,每一个News对象就代表了一则新闻,News对象中的
*定义了的四个属性
*1.新闻的标题_title
*2.新闻的描述_desc
*3.网站的连接_link
*4.发布时间_date
public class News { /* News的四个变数 */ private String _title=""; private String _link=""; private String _desc=""; private String _date=""; public String getTitle() { return _title; } public String getLink() { return _link; } public String getDesc() { return _desc; } public String getDate() { return _date; } public void setTitle(String title) { _title=title; } public void setLink(String link) { _link=link; } public void setDesc(String desc) { _desc=desc; } public void setDate(String date) { _date=date; } }
package irdc.ex08_13; /* import相关class */ import java.util.List; import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.TextView; /* 自定义的Adapter,继承android.widget.BaseAdapter */ public class MyAdapter extends BaseAdapter { /* 变量声明 */ private LayoutInflater mInflater; private List<News> items; /* MyAdapter的建构子,传入两个参数 */ public MyAdapter(Context context,List<News> it) { /* 参数初始化 */ mInflater = LayoutInflater.from(context); items = it; } /* 因继承BaseAdapter,需重写以下method */ @Override public int getCount() { return items.size(); } @Override public Object getItem(int position) { return items.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position,View convertView,ViewGroup par) { ViewHolder holder; if(convertView == null) { /* 使用自定义的news_row作为Layout */ convertView = mInflater.inflate(R.layout.news_row, null); /* 初始化holder的text与icon */ holder = new ViewHolder(); holder.text = (TextView) convertView.findViewById(R.id.text); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } News tmpN=(News)items.get(position); holder.text.setText(tmpN.getTitle()); return convertView; } /* class ViewHolder */ private class ViewHolder { TextView text; } }
我们只需要点击其中的链接,就可以打开相应的网页文件,截图如下:
实现的代码如下:
注意,打开网页是调用系统自带的浏览器:
package irdc.ex08_13; /* import相关class */ import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.text.util.Linkify; import android.widget.TextView; public class EX08_13_2 extends Activity { /* 变量声明 */ private TextView mTitle; private TextView mDesc; private TextView mLink; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); /* 设定layout为newscontent.xml */ setContentView(R.layout.newscontent); /* 初始化对象 */ mTitle=(TextView) findViewById(R.id.myTitle); mDesc=(TextView) findViewById(R.id.myDesc); mLink=(TextView) findViewById(R.id.myLink); /* 取得Intent中的Bundle对象 */ Intent intent=this.getIntent(); Bundle bunde = intent.getExtras(); /* 取得Bundle对象中的数据 */ mTitle.setText(bunde.getString("title")); mDesc.setText(bunde.getString("desc")+"...."); mLink.setText(bunde.getString("link")); /* 设定mLink为网页连结 */ Linkify.addLinks(mLink,Linkify.WEB_URLS); } }