一、摘要

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

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中来呢?

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。
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

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
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的问题,自定义旋转图片的时候,以前总是不知道怎样让它匀速循环转动,现在好像也解决了,反正收获不少吧。此“项目”权当闲来练手,没什么实际用处,希望各位大牛多多指点。
(附上源代码下载地址,访问密码:qDPzYL)

模仿网易新闻客户端(RSS版)(二)相关推荐

  1. 模仿网易新闻客户端的滚动菜单

    模仿网易新闻客户端的滚动菜单,点击菜单的时候有放大标题的动画效果.demo地址:https://github.com/BigHub/ScrollMenuViewTest 需要用到facebook的开源 ...

  2. 模仿网易新闻客户端(RSS版)(一)

    一.摘要 这几天找工作闲来没事,偶然在一个论坛上面看到有人模拟网易新闻客户端首页顶部那个组件效果,一时兴起,也来自己动手完整地模拟一个,包括顶部的特效组件和底部的类似于TabHost的组件.下面就动手 ...

  3. Android Studio精彩案例(四)《DrawerLayout使用详解仿网易新闻客户端侧边栏 》

    转载本专栏文章,请注明出处,尊重原创 .文章博客地址:道龙的博客 为了提高兴趣,咱们开头先看看最终要实现什么样的效果: 侧拉菜单在Android应用中非常常见,它的实现方式太多了,今天我们就说说使用G ...

  4. android平台上线,网易新闻客户端3.0版iOS、Android平台正式上线

    网易科技讯 12月3日消息,网易新闻客户端3.0版在"2012网易移动媒体高峰论坛"第一次对外亮相,今日3.0版正式在ios和android平台同步上线,目前,用户已可在各大应用商 ...

  5. [转]网易新闻客户端为什么开始在北京市区的地铁、公交站牌大批量投放广告?...

    一.可能是网易新闻客户端取得了不可思议的成功,潜力巨大,引起了网易的重视. 二.可能是京城学生上班族群体的智能手机普及率高,拓展市场. -------------------------------- ...

  6. Android 开源框架ViewPageIndicator 和 ViewPager 仿网易新闻客户端Tab标签

     转载请注明出处:http://blog.csdn.net/xiaanming/article/details/10766053 之前用JakeWharton的开源框架ActionBarSherl ...

  7. Android SlidingMenu 仿网易新闻客户端布局

    前面两篇文章中的SlidingMenu都出现在左侧,今天来模仿一下网易新闻客户端左右两边都有SlidingMenu的效果,以下是网易新闻客户端效果: 不扯闲话了,直接进入正题吧 frame_conte ...

  8. Android高仿网易新闻客户端之首页

    关于实现网易新闻客户端的界面,以前写过很多博客,请参考: Android实现网易新闻客户端效果 Android实现网易新闻客户端侧滑菜单(一) Android实现网易新闻客户端侧滑菜单(二) 今天用V ...

  9. 视频教程-Flutter布局实战网易新闻客户端-flutter

    Flutter布局实战网易新闻客户端 Flutter书籍作者.曾任职于多家互联网大型企业,担任移动端项目技术负责人,主导研发和攻关技术难题,项目涉及教育.金融.医疗.交通.车载设备.智能家居等众多领域 ...

最新文章

  1. WEB开发者八项注意
  2. NodeJS、NPM安装配置与测试步骤(windows版本)
  3. ProtoBuf开发者指南
  4. 函数式编程是啥玩意?map() reduce()(reduce()函数将数字列表转换为x进制数字)闭包、装饰器、偏函数
  5. BZOJ 2957: 楼房重建
  6. list 置顶元素_java集合指定元素排序:最前,按照提供的顺序排序?求算法
  7. 树的遍历-Preorde Traversal,Inorder Traversal,Postoder Traversal
  8. linux下c语言俄罗斯方块,c语言做俄罗斯方块
  9. JDK源码(19)-Package
  10. 关于服务限流的一些思考
  11. 安卓手机管理软件_日程管理软件哪个好?
  12. 【bzoj1085】【SCOI2005】【骑士精神】
  13. mysql对象资源管理器在哪里_文件资源管理器在哪
  14. 硬盘显示无法访问由于IO设备错误的文件找到办法
  15. java 苹果支付(内购)
  16. 大麦网滑块验证码自动识别
  17. 使用Zeplin 过程中 TextView不显示的问题?
  18. 【讲座回顾】不会被AI取代的人
  19. linux18安装anyconnect4.9遇到I/O问题
  20. 计算机网络原理 - 概述

热门文章

  1. 通过Validation框架检查请求参数的基本格式
  2. Go语言基础入门(处理JSON)
  3. 【汇正财经】1.3日盘面回顾和行情解析
  4. Ubuntu linux中怎么查看自己的串口连接情况
  5. 信息大爆炸时代的生存指南(2):我们的基本信息素养
  6. 零基础学平面设计还学美工设计比较有前途?
  7. Unity制作游戏中改名系统(第一次改名免费、改名提示、充值获得改名次数等 )
  8. buu做题笔记——[WUSTCTF2020]朴实无华[BSidesCF 2020]Had a bad day
  9. ModelMapper
  10. openeuler 22.03 制作openssh9.3p1 rpm升级包和升级实战