一、摘要
繼上一篇博客《模仿網易新聞客戶端(一)》之后,筆者繼續開發我們自己的“網易新聞客戶端”,由於找不到現成的url新聞鏈接地址,所以這里就用RSS訂閱所提供的url,這里所用到的鏈接仍然是網易新聞中心的RSS地址http://www.163.com/rss/,然后通過解析xml內容,以ListView的方式呈現在手機界面上。還有一個問題,因為RSS所提供的xml資源里面,沒有對應item的圖片,所以,ListView里面每一個Item都沒有圖片,這點有點遺憾,但也沒事,實現其功能就行了。廢話不多說,老慣例,先看效果圖
二、效果截圖
三、解析RSS
首先,先大概地看一下RSS所提供XML的數據結構,下面是一個RSS文件結構示例
<?xml version="1.0" encoding="GBK"?>
<?xml-stylesheet type="text/css" href="http://news.163.com/css/allrss.css"?>
<rss version="2.0">
<channel>
<title>網易頭條新聞</title>
<link>http://news.163.com/</link>
<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>http://news.163.com/12/0402/08/7U2TBKQF0001124J.html</link>
<description>......</description>
<pubDate>2012-04-02 09:07:10</pubDate>
</item>
</channel>
</rss>
了解了有哪些節點,下面來編寫元數據類RSSItem.java
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
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

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中來呢?
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);
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

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);
}
}
五、具體新聞內容
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();
}
};
}
六、總結