• 构建 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代码如下:

    <?xml version="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元素:

    public class RSSItem {

    private String _title = null;

        private String _description = null;

        private String _link = null;

        private String _category = null;

        private String _pubdate = null;

     

       

        RSSItem()

        {

        }

        void setTitle(String title)

        {

            _title = title;

        }

        void setDescription(String description)

        {

            _description = description;

        }

        void setLink(String link)

        {

            _link = link;

        }

        void setCategory(String category)

        {

            _category = category;

        }

        void setPubDate(String pubdate)

        {

            _pubdate = pubdate;

        }

        String getTitle()

        {

            return _title;

        }

        String getDescription()

        {

            return _description;

        }

        String getLink()

        {

            return _link;

        }

        String getCategory()

        {

            return _category;

        }

        String getPubDate()

        {

            return _pubdate;

        }

        public String toString()

        {

            // limit how much text you display

            if (_title.length() > 42)

            {

                return _title.substring(0, 42) + "...";

            }

            return _title;

        }

    }

    4、新建pojo类RSSFeed,映射rss中的channel元素:

    public class RSSFeed

    {

        private String _title = null;

        private String _pubdate = null;

        private int _itemcount = 0;

        private List<RSSItem> _itemlist;

       

       

        RSSFeed()

        {

            _itemlist = new Vector<RSSItem>(0);

        }

        int addItem(RSSItem item)

        {

            _itemlist.add(item);

            _itemcount++;

            return _itemcount;

        }

        RSSItem getItem(int location)

        {

            return _itemlist.get(location);

        }

        List<RSSItem> getAllItems()

        {

            return _itemlist;

        }

        int getItemCount()

        {

            return _itemcount;

        }

        void setTitle(String title)

        {

            _title = title;

        }

        void setPubDate(String pubdate)

        {

            _pubdate = pubdate;

        }

        String getTitle()

        {

            return _title;

        }

        String getPubDate()

        {

            return _pubdate;

        }

    }

    5、新建helper类RSSHandler,用于对rss进行xml解析,并将解析结果包装为RSSFeed和RSSItem对象,方便在ui界面中显示:

    public class RSSHandler extends DefaultHandler{//继承 DefaultHandler,方便进行 sax 解析

    RSSFeed _feed; //临时变量,用于保存解析过程中的channel

        RSSItem _item; //临时变量,用于保存解析过程中的item

        //标记变量,用于标记在解析过程中我们关心的几个标签

        int currentstate = 0; //若不是我们关心的标签,记做 0

        final int RSS_TITLE = 1; //若是title标签,记做 1,注意有两个title,但我们都保存在_itemtitle成员变量中

        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,但我们都保存在_itempubdate成员变量中

       

        RSSHandler()

        {

        }

        RSSFeed getFeed()//通过这个方法把解析结果封装在 RSSFeed 对象中并返回

        {

            return _feed;

        }

        //下面通过重载 DefaultHandler 5 个方法来实现 sax 解析

        public void startDocument() throws SAXException

        {//这个方法在解析xml文档的一开始执行,一般我们需要在该方法中初始化解析过程中有可能用到的变量

            _feed = new RSSFeed();

            _item = new RSSItem();

        }

        public void endDocument() throws SAXException

        {//这个方法在整个xml文档解析结束时执行,一般需要在该方法中返回或保存整个文档解析解析结果,但由于

         //我们已经在解析过程中把结果保持在_feed,所以这里什么也不做

        }

        public void startElement(String namespaceURI, String localName,String qName,

                Attributes atts) throws SAXException

    {//这个方法在解析标签开始标记时执行,一般我们需要在该方法取得标签属性值,但由于我们的rss文档

         //中并没有任何我们关心的标签属性,因此我们主要在这里进行的是设置标记变量currentstate,

         //标记我们处理到哪个标签

    if (localName.equals("channel"))

    {//channel这个标签没有任何值得我们关心的内容,所以currentstate置为0

    currentstate = 0;

    return;

    }

    if (localName.equals("image"))

    {//如果是image这个标签,说明channel元素的子元素titlepubdate都已经解析出来了

     //(参考xml文件结构),那么应该把它们的值从_item转移到_feed,因为我们只是图方便才

     //把它们存放在_item的成员中,实际上它们应该是_feed的成员

    _feed.setTitle(_item.getTitle());

    _feed.setPubDate(_item.getPubDate());

    }

    if (localName.equals("item"))

    {//若是item标签,则重新构造一个RSSItem,从而把已有(已经解析过的)item数据扔掉,

     //然事先是已经保存到_feeditemlist集合中了

    _item = new RSSItem();

    return;

    }

    if (localName.equals("title"))

    {//若是title标签,currentstate1,表明这是我们关心的数据,这样在characters

     //方法中会把元素内容保存到_item变量中

    currentstate = RSS_TITLE;

    return;

    }

    if (localName.equals("description"))

    {//若是description标签,currentstate3,表明这是我们关心的数据,这样在characters

     //方法中会把元素内容保存到_item变量中

    currentstate = RSS_DESCRIPTION;

    return;

    }

    if (localName.equals("link"))

    {//若是description标签,currentstate2,表明这是我们关心的数据,这样在characters

     //方法中会把元素内容保存到_item变量中

    currentstate = RSS_LINK;

    return;

    }

    if (localName.equals("category"))

    {//若是description标签,currentstate4,表明这是我们关心的数据,这样在characters

     //方法中会把元素内容保存到_item变量中

    currentstate = RSS_CATEGORY;

    return;

    }

    if (localName.equals("pubDate"))

    {//若是description标签,currentstate5,表明这是我们关心的数据,这样在characters

     //方法中会把元素内容保存到_item变量中

    currentstate = RSS_PUBDATE;

    return;

    }

    currentstate = 0;//如果不是上面列出的任何标签,currentstate0,我们不关心

    }

        public void characters(char ch[], int start, int length)

        {//这个方法在解析标签内容(即开始标记-结束标记之间的部分)时执行,一般我们在里这获取元素体内容

            String theString = new String(ch,start,length); //获取元素体内容

            Log.i("RSSReader","characters[" + theString + "]");      

            switch (currentstate)//根据currentstate标记判断这个元素体是属于我们关心的哪个元素

            {

                case RSS_TITLE://若是title元素,放入_itemtitle属性

                    _item.setTitle(theString);

                    currentstate = 0;

                    break;

                case RSS_LINK://若是link元素,放入_itemlink属性

                    _item.setLink(theString);

                    currentstate = 0;

                    break;

                case RSS_DESCRIPTION://若是description元素,放入_itemdescription属性

                    _item.setDescription(theString);

                    currentstate = 0;

                    break;

                case RSS_CATEGORY://若是category元素,放入_itemcategory属性

                    _item.setCategory(theString);

                    currentstate = 0;

                    break;

                case RSS_PUBDATE://若是pubdate元素,放入_itempubdate属性

                    _item.setPubDate(theString);

                    currentstate = 0;

                    break;

                default:

                    return;

            }

           

        }

        public void endElement(String namespaceURI, String localName, String qName)

        throws SAXException

    {//这个方法在解析标签结束标记时执行,一般我们需要在该方法保存元素内容

    if (localName.equals("item"))

    {//item标签解析结束,_item保存到_feeditemlist属性中

    _feed.addItem(_item);

    return;

    }

    }

       

    }

    6、修改main.java,调用前面的类,从intentert获取rss列表并显示在ui上:

    public class main extends Activity implements OnItemClickListener{

    private RSSFeed feed = null;

    private String tag=this.getClass().getName();

        @Override

        public void onCreate(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();

        }

        private RSSFeed getFeed(String urlToRssFeed)

        {//该方法通过url获得xml并解析xml内容为RSSFeed对象

            try //异常处理

            {

               RSSHandler theRssHandler=new RSSHandler();

               URL url = new URL(urlToRssFeed);

               // 构建Sax解析工厂

               SAXParserFactory factory = SAXParserFactory.newInstance();

               // 使用Sax解析工厂构建Sax解析器

               SAXParser parser = factory.newSAXParser();

               // 使用Sax解析器构建xml Reader

               XMLReader xmlreader = parser.getXMLReader();

               // 构建自定义的RSSHandler作为xml Reader的处理器(或代理)

               xmlreader.setContentHandler(theRssHandler);

               // 使用url打开流,并将流作为xml Reader的输入源并解析

               xmlreader.parse(new InputSource(url.openStream()));

               // 将解析结果作为 RSSFeed 对象返回

               return theRssHandler.getFeed();

            }

            catch (Exception ee)

            {

                return null;

            }

        }

        private void UpdateDisplay()

        {

            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事件代理方法

    public void onItemClick(AdapterView<?> parent, View v, int position, long id) {

    Log.d(tag,"item clicked!");

    //构建一个意图,用于指向activity detail

    Intent itemintent = new Intent(this,detail.class);

            //构建buddle,并将要传递参数都放入buddle

            Bundle b = new Bundle();

            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-permission android:name="android.permission.INTERNET" />

    否则,程序会提示Permission denied错误。

    7、打开droid draw,设计第2个activity(页面)detail.xml:

    <?xml version="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的声明:

    <activity android:name=".detail" >

    </activity>

    9、新建class detail.java:

    public class detail extends Activity {

    public void onCreate(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(new Button.OnClickListener()

            {

                public void onClick(View v)

                {

                    finish();//结束本activity,返回给调用者

                }

            });       

        }

    }

    10、运行程序,在ddms中进行调试。

  • 相关阅读:
    NSInternalInconsistencyException 关于iOS的背后线程刷新页面问题
    关于IOS UIlable 一行未满就换行
    ios 提交审核时出现二进制文件无效问题
    测试中缺陷等级分类
    测试的分类
    测试是一个纸杯
    如何测试一部电梯
    一个简单的测试案例
    【JVM】【常用工具】【MAT】macbook安装MAT(独立版,非eclipse插件)
    【Canal】【MySQL】解密阿里Canal :(一)Canal工作原理
  • 原文地址:https://www.cnblogs.com/encounter/p/2188535.html
Copyright © 2020-2023  润新知