[置顶] Android开发RSS阅读器

        RSS阅读器的Logo:

                                       

        RSS阅读器是一种软件或是说一个程序,这种软件可以自由读取RSS和Atom两种规范格式的文档,且这种读取RSS和Atom文档的软件有多个版本,由不同的人或公司开发,有着不同的名字。如目前流行的有:RSSReader、Feeddemon、SharpReader等。这些软件能够实现大致相同的功能,其实质都是为了方便地读取RSS和Atom文档。Really Simple Syndication “聚合真的很简单”就是RSS的英文原意。把新闻标题、摘要(Feed)、内容按照用户的要求,“送”到用户的桌面就是RSS的目的。


注:下面给出的这个例子来自Google Android SDK开发,经过阅读,理解,并加上自己的和汇总写下这篇文章。


首先,给出实现本实例的截图,我用这个阅读器打开了我的博客。

1.

[置顶] Android开发RSS阅读器_第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();
  }
}

2.点击打开按钮,进入文档界面,实现的截图如下:

[置顶] Android开发RSS阅读器_第2张图片


说明:

* 这是一个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对象,下面给出这个类的介绍:


 * 自定义的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);
    }
  } 
}

在前两个类中都用到了News这个类对象,这是对阅读文档对象的定义,代码文件如下:

* 这是一个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; 
  }
}

第二个文件的主布局是一个ListView用来显示可阅读文档,下面给出提供数据的Adapter文件:

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;
  }
}

之后,当我们点击一条信息之后,就会进入下一界面,实现的截图如下:

[置顶] Android开发RSS阅读器_第3张图片


我们只需要点击其中的链接,就可以打开相应的网页文件,截图如下:

[置顶] Android开发RSS阅读器_第4张图片

实现的代码如下:

注意,打开网页是调用系统自带的浏览器:

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);
  }
}


你可能感兴趣的:(Android开发,Android开发)