Android新闻客户端(仿网易)--下
一、摘要继上一篇博客《模仿网易新闻客户端(上)》之后,笔者继续开发我们自己的“网易新闻客户端”,由于找不到现成的url新闻链接地址,所以这里就用RSS订阅所提供的url,这里所用到的链接仍然是网易新闻中心的RSS地址http://www.163.com/rss/,然后通过解析xml内容,以ListView的方式呈现在手机界面上。还有一个问题,因为RSS所提供的xml资源里面,没有对应item的图片,所以,ListView里面每一个Item都没有图片,这点有点遗憾,但也没事,实现其功能就行了。废话不多说,老惯例,先看效果图
[img]http://www.eoeandroid.com/data/attachment/forum/201212/25/135448umt5valrithrcvm9.png[/img][img]http://www.eoeandroid.com/data/attachment/forum/201212/27/152058mjwj3jf3hw73awl7.png[/img][img]http://www.eoeandroid.com/data/attachment/forum/201212/27/152202q11y16c2cyvmn1sx.png[/img][img]http://www.eoeandroid.com/data/attachment/forum/201212/27/152315oc19xdsn5w3ha5ha.png[/img]
三、解析RSS
首先,先大概地看一下RSS所提供XML的数据结构,下面是一个RSS文件结构示例
<?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
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
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的问题,自定义旋转图片的时候,以前总是不知道怎样让它匀速循环转动,现在好像也解决了,反正收获不少吧。此“项目”权当闲来练手,没什么实际用处,希望各位大牛多多指点。
Android新闻客户端(仿网易)--下相关推荐
- Android新闻客户端实训-Day1类Day2接口基操
结束了一些事情,本以为可以好好玩一段时间,结果紧接着又来了Android实训,因为这个实训已经开了一段时间了,我中途加入已经不赶趟了,但出于兴趣,准备今天开始跟着老师的笔记的源码从头开始自己摸索着学一 ...
- android新闻客户端报告,简易的Android新闻客户端
学完Android基础之后不知道该怎么办?现在开始实战吧! 现在来看看一款简易的Android新闻客户端是怎么做的,当然,获取网络数据的这一部分我是使用别人做好的本地客户端,然后通过组建本地数据库来使 ...
- android 新闻频道,GitHub - xiyy/TopNews: 一款Android新闻客户端,并提供电视台直播功能...
TopNews 一款Android新闻客户端,独立开发完成,主要功能包括: 1 新闻频道分类,头条.社会.国内.娱乐.体育.军事.科技.财经.时尚 使用ViewPager+FragmentPagerA ...
- android新闻客户端的实现
制作一个基于聚合新闻数据简单的新闻APP 制作简易新闻App 导航篇 [1.使用Fragment+ViewPager +TabLayout自制简易新闻 app主要框架(一)](https://blog ...
- android新闻客户端(有源码)
有些功能还没实现 源码今天起不再免费提供,评论区有联系方式 目录 一. 题目及要求 2 二. 功能设计 3 三. 详细设计 3 1. 启动页面 3 1)页面显示完跳转 3 2) 启动页面图片全屏化 3 ...
- android新闻客户端发展趋势,基于Android平台的新闻客户端设计与实现
杨苏雯 摘 要 在Android开发平台上,并在JavaWeb开发的PC端新闻网站的基础上结合现在的需求开发了移动版的新闻客户端App,这个系统设计主要分为用户登录验证模块.新闻列表的显示功能模块以及 ...
- android新闻客户端实验报告,基于Android平台的新闻客户端设计与实现
摘 要 在Android开发平台上,并在JavaWeb开发的PC端新闻网站的基础上结合现在的需求开发了移动版的新闻客户端App,这个系统设计主要分为用户登录验证模块.新闻列表的显示功能模块以及新闻内容 ...
- android新闻客户端实验报告,Android 新闻客户端学习笔记
学习了新闻客户端开发后,有很多是通用的,就把客户端用到的一些做个笔记,已备以后使用,主要用到的知识点有: 1. Android网络编程 2. Android中BaseAdapter的使用 3. ...
- Android 新闻客户端案例
实现新闻客户端案例首先需要注意XML文件的在线解析,适配器问题和ListView控件的使用. 根据自己的需要和要求,准备好素材. 1.NewsInfo.xml文件如下: <?xml versio ...
最新文章
- 使用yum快速搭建LAMP和配置phpMyAdmin
- 乐视云监控数据存放到influxdb中
- 从源码角度深入分析log4j配置文件使用
- cpu不支持虚拟装linux,linux 查看cpu是不是支持虚拟化
- linux c多进程多线程,linux下的C\C++多进程多线程编程实例详解
- Symbian编程VC开发环境设置
- android ascii 比较大小写,为什么可以通过ASCII中的字母排序规则来进行字母的大小写转换?...
- wdos相关问题解答
- JsonHelper(Json帮助类)
- Linux常用基本命令总结
- 《UNIX系统编程》
- 计算机开机后黑屏 只有鼠标,电脑开机后一直黑屏只有一个鼠标,重启也是.怎么处理...
- Pytorch TextCNN实现中文文本分类(附完整训练代码)
- java-常量和变量
- (六)高德地图之驾车路线规划
- crh寄存器_STM32的GPIO的寄存器介绍和设置
- 没人谈论的 3 种从 ChatGPT 赚钱的方法,我测试过的建立被动收入流的行之有效的方法
- 申请TC交易证书以下4点请注意
- 【Sanic】Sanic应用 2
- JavaScript实现春节倒计时
热门文章
- 微信域名拦截检测API源码 检测域名是否能在微信正常打开
- 如何使用Photoshop在PSD文件上切图
- 如何设置计算机自动连接宽带,Win10怎么设置开机自动连接宽带?设置宽带自动联网方法...
- 使用html和css进行红米案例、学成在线开发
- (linux自学笔记)linux系统初体验与编程基础
- 如何判断一个数是不是素数?
- Virtuoso仿真错误 ---- input.scs
- 【过关斩将】运维老鸟带教你如何精通运维
- day 5 文字溢出处理 背景图片处理 开发经验
- Objective-C向面向对象编程中添加了一个新概念:类别(categor)。