模仿网易新闻客户端(RSS版)(二)
一、摘要
继上一篇博客《模仿网易新闻客户端(一)》之后,笔者继续开发我们自己的“网易新闻客户端”,由于找不到现成的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();
}
};
}
六、总结