模仿网易新闻客户端(RSS版)(二)

一、摘要继上一篇博客《模仿网易新闻客户端(一)》之后,笔者继续开发我们自己的“网易新闻客户端”,由于找不到现成的url新闻链接地址,所以这里就用RSS订阅所提供的url,这里所用到的链接仍然是网易新闻中心的RSS地址http://www.163.com/rss/,然后通过解析xml内容,以ListView的方式呈现在手机界面上。还有一个问题,因为RSS所提供的xml资源里面,没有对应item的图片,所以,ListView里面每一个Item都没有图片,这点有点遗憾,但也没事,实现其功能就行了。废话不多说,老惯例,先看效果图

二、效果截图

2012040223024787.png     360手机助手截图01.png        360手机助手截图02.png    360手机助手截图03.png 



三、解析RSS

首先,先大概地看一下RSS所提供XML的数据结构,下面是一个RSS文件结构示例
?
代码片段,双击复制
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
<?xml version= "1.0" encoding= "GBK" ?>  
<?xml-stylesheet type= "text/css" ?>  
<rss version= "2.0" >  
<channel>  
<title>网易头条新闻</title>  
<link><a href= "\"http://news.163.com/</link\"" target= "\"_blank\"" >http://news. 163 .com/</link></a>  
<description>网易头条新闻</description>  
<pubDate>Mon, 2 Apr 2012 01 : 07 : 10 GMT</pubDate>  
<lastBuildDate>Mon, 2 Apr 2012 01 : 07 : 10 GMT</lastBuildDate>  
<item id= "1" >  
<title>...</title>  
<link><a href= "\"http://news.163.com/12/0402/08/7U2TBKQF0001124J.html</link\"" target= "\"_blank\"" >http://news. 163 .com/ 12 / 0402 / 08 /7U2TBKQF0001124J.html</link></a>  
<description>......</description>  
<pubDate> 2012 - 04 - 02 09 : 07 : 10 </pubDate>  
</item>  
</channel>  
</rss>



了解了有哪些节点,下面来编写元数据类RSSItem.java

?
代码片段,双击复制
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
package com.and.netease.rss;
 
public class RSSItem {
 
    private String title;
    private String link;
    private String description;
    private String pubDate;
 
    public RSSItem() {
         super ();
    }
 
    public String getTitle() {
         return title;
    }
 
    public void setTitle(String title) {
         this .title = title;
    }
 
    public String getLink() {
         return link;
    }
 
    public void setLink(String link) {
         this .link = link;
    }
 
    public String getDescription() {
         return description;
    }
 
    public void setDescription(String description) {
         this .description = description;
    }
 
    public String getPubDate() {
         return pubDate;
    }
 
    public void setPubDate(String pubDate) {
         this .pubDate = pubDate;
    }
 
    @Override
    public String toString() {
         return "RSSItem [title=" + title + ", link=" + link + ", description="
                + description + ", pubDate=" + pubDate + "]" ;
    }
 
}


紧接着编写解析XML的Handler类:RSSHandler.java

?
代码片段,双击复制
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
package com.and.netease.rss;
 
import java.util.ArrayList;
import java.util.List;
 
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
 
import android.text.Html;
 
public class RSSHandler extends DefaultHandler {
 
    private List<RSSItem> list;
    private RSSItem item;
    private String tag = "" ;
     
    private StringBuffer buffer;
    @Override
    public void characters( char [] ch, int start, int length)
             throws SAXException {
         super .characters(ch, start, length);
         if (item!= null ){
             String data = new String(ch,start,length);
             if (tag.equals( "title" )){
                item.setTitle(data);
             } else if (tag.equals( "link" )){
                item.setLink(data);
             } else if (tag.equals( "description" )){
//                item.setDescription(data);
                buffer.append(Html.fromHtml(data));
             } else if (tag.equals( "pubDate" )){
                item.setPubDate(data);
             }
         }
    }
 
    @Override
    public void endDocument() throws SAXException {
         super .endDocument();
    }
 
    @Override
    public void endElement(String uri, String localName, String qName)
             throws SAXException {
         super .endElement(uri, localName, qName);
         if (localName.equals( "item" )){
             item.setDescription(buffer.toString());
             list.add(item);
             item = null ;
             buffer = null ;
         }
         tag = "" ;
    }
 
    @Override
    public void startDocument() throws SAXException {
         super .startDocument();
         list = new ArrayList<RSSItem>();
    }
 
    @Override
    public void startElement(String uri, String localName, String qName,
             Attributes attributes) throws SAXException {
         super .startElement(uri, localName, qName, attributes);
         if (localName.equals( "item" )){
             item = new RSSItem();
             buffer = new StringBuffer();
         }
         tag = localName;
    }
     
    public List<RSSItem> getData(){
         return list;
    }
 
}



注意:在存description的值的时候,可能会出问题,有可能读取出来的全是省略号,因为第一次读取到数据,存入了相应的RSSItem实例变量中,第二次又读取到省略号,再存入当前的RSSItem实例中,造成最后只有第二次存入的值,因为它前面的文字和后面的省略号不是一次读取完的,所以我这里用了一个StringBuffer来存它,之后一次性地存入到当拉RSSItem中。当然有些提供的RSS源代码中不是这样的。具体需要自己测试一下才好。

当然,这里所解析的所有URL地址放在一个常量类里面的,开始本想放String.xml文件中,但是地址里面有特殊字符,为了简便,就专门定义了一个常量类用来存放URL地址,如下CONST.java
?
代码片段,双击复制
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
View Code
 
package com.and.netease;
 
public class CONST {
 
    public static final String URL_NEWS_TOP = "http://news.163.com/special/00011K6L/rss_newstop.xml" ;
    public static final String URL_NEWS_SPORT = "http://sports.163.com/special/00051K7F/rss_sportslq.xml" ;
    public static final String URL_NEWS_PLAY = "http://ent.163.com/special/00031K7Q/rss_toutiao.xml" ;
    public static final String URL_NEWS_FINANCE = "http://money.163.com/special/00252EQ2/yaowenrss.xml" ;
    public static final String URL_NEWS_SCIENCE = "http://tech.163.com/special/000944OI/headlines.xml" ;
 
    //国内
    public static final String URL_NEWS_DOMESTIC = "http://news.163.com/special/00011K6L/rss_gn.xml" ;
    //军事
    public static final String URL_NEWS_MILITARY = "http://news.163.com/special/00011K6L/rss_war.xml" ;
    //国际
    public static final String URL_NEWS_INTERNATIONAL = "http://news.163.com/special/00011K6L/rss_gj.xml" ;
    //社会
    public static final String URL_NEWS_COMMUNITY = "http://news.163.com/special/00011K6L/rss_sh.xml" ;
    //深度
    public static final String URL_NEWS_DEPTH = "http://news.163.com/special/00011K6L/rss_hotnews.xml" ;
    //彩票
    public static final String URL_NEWS_TICKET = "http://sports.163.com/special/00051K7F/rss_sportscp.xml" ;
    //电影
    public static final String URL_NEWS_FILM = "http://ent.163.com/special/00031K7Q/rss_entmovie.xml" ;
    //音乐
    public static final String URL_NEWS_MUSIC = "http://ent.163.com/special/00031K7Q/rss_entmusic.xml" ;
    //IT
    public static final String URL_NEWS_IT = "http://tech.163.com/special/000944OI/kejiyejie.xml" ;
    //汽车
    public static final String URL_NEWS_CAR = "http://auto.163.com/special/00081K7D/rsstoutiao.xml" ;
    //数码
    public static final String URL_NEWS_DIGITAL = "http://tech.163.com/digi/special/00161K7K/rss_digixj.xml" ;
     
     
    //网易话题
    public static final String URL_TOPIC = "http://news.163.com/special/00011K6L/rss_newsspecial.xml" ;
    //网易图片
    public static final String URL_PICTURE = "http://news.163.com/special/00011K6L/rss_photo.xml" ;
    //网易跟帖
    public static final String URL_FOLLOW = "" ;
    //网易投票
    public static final String URL_VOTE = "" ;
     
}


后面的网易跟帖和投票,我实在找不到合适的URL地址了,所以就都用的网易图片的URL,因为里面我没有涉及到跟帖和投票的操作。如上最后一张图片所示。


四、关于Tab(新闻)页面的讲解

由于RSS格式的限制,所以里面的各个页面大体框架类似,下面只大概讲解一下“新闻”页面。它是TabHost里面的其中一个页面,在这个页面中涉及到另外几个页面,如“头条”、“体育”、“娱乐”、“财经”等一些,所以这个页面让它继承自ActivityGroup类,在这个ActivityGroup类里面可以管理很多的Activity。那要怎样把一个Activity添加到ActivityGroup中来呢?
?
代码片段,双击复制
01
intent = new Intent(TabNewsActivity. this , TabNewsTopActivity. class );        page = getLocalActivityManager().startActivity( "activity1" , intent).getDecorView();        LayoutParams params = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);        layout_news_main.addView(page, params);


这样,就可以把一个Activity转化成View,然后添加到当前的ActivityGroup中。

另外,这里涉及到一个Progress的处理,当请求数据的时候,让它显示一个旋转的进度提示。这里面用到了ViewSwitcher这个类,在它里面添加两个视图View,然后在不同的时候控件它具体显示哪一个View而达到目的,我这里在ViewSwitcher里面分别添加了一个ListView和一个ProgressBar,当请求网络的时候,让它显示ProgressBar界面,请求完成,让它显示ListView。

?
代码片段,双击复制
01
viewSwitcher = (ViewSwitcher) findViewById(R.id.viewswitcher_news_top);        listView = new MyListView( this );        ...        viewSwitcher.addView(listView);        viewSwitcher.addView(getLayoutInflater().inflate(R.layout.layout_progress_page, null ));        viewSwitcher.showNext();


完整代码TabNewsTopActivity.java

?
代码片段,双击复制
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
View Code
 
package com.and.netease;
 
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.Charset;
import java.util.List;
 
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
 
import org.xml.sax.InputSource;
import org.xml.sax.XMLReader;
 
import com.and.netease.MyListView.OnRefreshListener;
import com.and.netease.rss.RSSHandler;
import com.and.netease.rss.RSSItem;
 
import android.app.Activity;
import android.content.Intent;
import android.graphics.Color;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.ViewSwitcher;
 
public class TabNewsTopActivity extends Activity {
 
    MyListView listView;
 
    List<RSSItem> list;
    RSSHandler rssHandler;
 
    MyAdapter adapter;
 
    ViewSwitcher viewSwitcher;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
         super .onCreate(savedInstanceState);
         setContentView(R.layout.layout_news_top);
         setTheme(android.R.style.Theme_Translucent_NoTitleBar);
 
         initViews();
 
         rssHandler = new RSSHandler();
         requestRSSFeed();
 
    }
 
    private void initViews() {
         viewSwitcher = (ViewSwitcher) findViewById(R.id.viewswitcher_news_top);
         listView = new MyListView( this );
         listView.setCacheColorHint(Color.argb( 0 , 0 , 0 , 0 ));
         ImageView testView = new ImageView( this );
         testView.setImageResource(R.drawable.temp);
         listView.addHeaderView(testView);
         listView.setonRefreshListener(refreshListener);
         
         viewSwitcher.addView(listView);
         viewSwitcher.addView(getLayoutInflater().inflate(R.layout.layout_progress_page, null ));
         viewSwitcher.showNext();
         listView.setOnItemClickListener(listener);
 
    }
     
    private OnRefreshListener refreshListener = new OnRefreshListener() {
         
         @Override
         public void onRefresh() {
             // TODO Auto-generated method stub
             new AsyncTask<Void, Void, Void>() {
                protected Void doInBackground(Void... params) {
                     try {
                         Thread.sleep( 1000 );
                     } catch (Exception e) {
                         e.printStackTrace();
                     }
                     
                     return null ;
                }
 
                @Override
                protected void onPostExecute(Void result) {
                     adapter.notifyDataSetChanged();
                     listView.onRefreshComplete();
                }
 
             }.execute( null );
         }
    };
 
    private void requestRSSFeed() {
         Thread t = new Thread() {
             @Override
             public void run() {
                super .run();
                try {
                     URL url = new URL(CONST.URL_NEWS_TOP);
                     URLConnection con = url.openConnection();
                     con.connect();
 
                     InputStream input = con.getInputStream();
 
                     SAXParserFactory fac = SAXParserFactory.newInstance();
                     SAXParser parser = fac.newSAXParser();
                     XMLReader reader = parser.getXMLReader();
                     reader.setContentHandler(rssHandler);
                     Reader r = new InputStreamReader(input, Charset.forName( "GBK" ));
                     reader.parse( new InputSource(r));
                     list = rssHandler.getData();
//                    for (RSSItem rss : list) {
//                        System.out.println(rss);
//                    }
                     if (list.size() == 0 ) {
                         handler.sendEmptyMessage(- 1 );
                     } else {
                         handler.sendEmptyMessage( 1 );
                     }
                } catch (Exception e) {
                     e.printStackTrace();
                }
             }
         };
         t.start();
    }
 
    Handler handler = new Handler() {
         public void handleMessage(android.os.Message msg) {
             if (msg.what == 1 ) {
                adapter = new MyAdapter();
                listView.setOnItemClickListener(listener);
                listView.setAdapter(adapter);
                viewSwitcher.showPrevious();
                 
                listView.onRefreshComplete();
             }
         };
    };
 
    private OnItemClickListener listener = new OnItemClickListener() {
 
         @Override
         public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
             if (position== 1 ){
                return ;
             }
             Intent intent = new Intent(TabNewsTopActivity. this , NewsContentActivity. class );
             intent.putExtra( "content_url" , list.get(position- 2 ).getLink());
             TabNewsTopActivity. this .startActivityForResult(intent, position);
         }
    };
 
    private class MyAdapter extends BaseAdapter {
 
         @Override
         public int getCount() {
             return list.size();
         }
 
         @Override
         public Object getItem( int position) {
             return null ;
         }
 
         @Override
         public long getItemId( int position) {
             return 0 ;
         }
 
         @Override
         public View getView( int position, View convertView, ViewGroup parent) {
             ViewHolder holder;
             if (convertView == null ) {
                holder = new ViewHolder();
                convertView = getLayoutInflater().inflate(R.layout.layout_news_top_item, null );
                holder.tv_date = (TextView) convertView.findViewById(R.id.tv_date_news_top_item);
                holder.tv_title = (TextView) convertView.findViewById(R.id.tv_title_news_top_item);
                holder.tv_Description = (TextView) convertView.findViewById(R.id.tv_description_news_top_item);
                convertView.setTag(holder);
             } else {
                holder = (ViewHolder) convertView.getTag();
             }
 
             holder.tv_date.setText(list.get(position).getPubDate());
             holder.tv_title.setText(list.get(position).getTitle());
             holder.tv_Description.setText(list.get(position).getDescription());
 
             return convertView;
         }
 
    }
 
    public static class ViewHolder {
         TextView tv_date;
         TextView tv_title;
         TextView tv_Description;
    }
 
    @Override
    protected void onActivityResult( int requestCode, int resultCode, Intent data) {
         System.out.println( "返回" );
         super .onActivityResult(requestCode, resultCode, data);
         
    }
}


几点说明:

1、细心的你有可能会发现,里面我用的是MyListView,它是自定义的一个ListView,为了实现下拉刷新而写的。如果不需要下拉刷新,直接换成ListView即可。但是,要注意,这里自定义MyListView之后,设置它的OnItemClickListener点击事件,Item里面的position从1开始,而非从0开始,下拉列表是从网上复制一的段,还没怎么研究,我猜的话,估计那个下拉出现的东西,有可能position就是0。这个之后再研究。关于下拉刷新,我会在另外一个博客中说明。现在我也还没弄明白。

2、这个类里面的ListView添加的Header,是一张静态图片,因为RSS代码里面没有提供图片链接,为了达到效果,暂且用一张图片代替。

五、具体新闻内容

具体的新闻内容,暂且用一个WebView来加载它,碍于RSS的限制,具体某一条新闻的Link链接地址,是一个完整的网页。当然请求的时候,照样显示旋转的进度条,跟之前的一个ViewSwitcher一样处理。新闻内容NewsContentActivity.java
?
代码片段,双击复制
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
package com.and.netease;
 
import android.app.Activity;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.ImageButton;
import android.widget.ViewSwitcher;
 
public class NewsContentActivity extends Activity {
 
    ImageButton btn_back;
    WebView webView;
    String content_url;
 
    ViewSwitcher viewSwitcher;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
         super .onCreate(savedInstanceState);
         setContentView(R.layout.layout_newscontent);
         initViews();
    }
 
    private void initViews() {
         btn_back = (ImageButton) findViewById(R.id.btn_newscontent_back);
         btn_back.setOnClickListener(listener);
 
         viewSwitcher = (ViewSwitcher) findViewById(R.id.viewSwitcher);
 
         content_url = getIntent().getStringExtra( "content_url" );
         webView = new WebView( this );
 
         // 向ViewSwitcher中添加两个View,用来切换
         viewSwitcher.addView(webView);
         viewSwitcher.addView(getLayoutInflater().inflate(
                R.layout.layout_progress_page, null ));
         WebSettings settings = webView.getSettings();
         settings.setJavaScriptEnabled( true );
 
         webView.setWebViewClient(client);
         webView.loadUrl(content_url);
    }
 
    private WebViewClient client = new WebViewClient() {
 
         @Override
         public void onPageFinished(WebView view, String url) {
             super .onPageFinished(view, url);
             viewSwitcher.showPrevious();
         }
 
         @Override
         public void onPageStarted(WebView view, String url, Bitmap favicon) {
             super .onPageStarted(view, url, favicon);
             viewSwitcher.showNext();
         }
 
         @Override
         public boolean shouldOverrideUrlLoading(WebView view, String url) {
             return super .shouldOverrideUrlLoading(view, url);
         }
 
    };
 
    private OnClickListener listener = new OnClickListener() {
 
         @Override
         public void onClick(View v) {
             NewsContentActivity. this .setResult(RESULT_OK);
             NewsContentActivity. this .finish();
         }
    };
 
    public void onBackPressed() {
         if (webView.canGoBack()) {
             webView.goBack();
         } else {
             NewsContentActivity. this .setResult(RESULT_OK);
             NewsContentActivity. this .finish();
         }
    };
}


至此,这个版本算是基本上完成了。除了第一个Tab(新闻)页面稍微复杂点之外,其它页面基本一样,因为目前我所发现的RSS所能提供的仅是这些。



模仿网易新闻客户端的开发就告一段落了,如果找到合适的URL再继续吧,目前还有很多功能没有实现,比如天气、分享、跟帖等等很多东西。

六、总结

小小地总结一下,模仿网易新闻客户端,虽然没碰到很大的难点,但是还是有不少 的收获的,比如在自定义TabHost控件的时候,以前也遇到过点击不能切换背景的问题,当时在代码里面控制的。通过做这个东西,现在解决了,还有一个就是ProgressBar的问题,自定义旋转图片的时候,以前总是不知道怎样让它匀速循环转动,现在好像也解决了,反正收获不少吧。此“项目”权当闲来练手,没什么实际用处,希望各位大牛多多指点。

源码下载:    netease_news.zip (2.19 MB, 下载次数: 654)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值