Android(java)学习笔记205:网易新闻RSS客户端应用编写逻辑过程
1.我们的项目需求是编写一个新闻RSS浏览器,RSS(Really Simple Syndication)是一种描述和同步网站内容的格式,是使用最广泛的XML应用。RSS目前广泛用于网上新闻频道,blog和wiki,主要的版本有0.91, 1.0, 2.0。使用RSS订阅能更快地获取信息,网站提供RSS输出,有利于让用户获取网站内容的最新更新。网络用户可以在客户端借助于支持RSS的聚合工具软件,在不打开网站内容页面的情况下阅读支持RSS输出的网站内容。
例如如下的网易RSS订阅:
2.由于我们这里是模拟Android手机APP访问获取服务器里面的数据的工作场景,所以我们这里在电脑上模拟一个服务器环境,这里我们安装Apache(web服务器软件):
package com.himi.news.net; import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.List; import org.apache.http.HttpConnection; import android.content.Context; import com.himi.news.R;
import com.himi.news.domain.NewsItem;
import com.himi.news.service.NewsInfoParser; /**
* 新闻的业务类:工具类
* @author Administrator
*
*/ public class NewsUtils {
//设置静态方法,方便外界调用
//获取服务器端网页新闻的信息,这里需要写一个新闻信息数据类,新建包com.himi.news.info,新建类NewsItem存储信息 /**
* 得到服务器最新的所有的新闻信息,我们这里路径放在res/values/config.xml中
* res资源我们必须使用上下文才能访问,所有getAllNews的参数是Context context
* @param context上下文
* @return
*/
public static List<NewsItem> getAllNews(Context context) throws Exception {
String path = context.getResources().getString(R.string.serverip); URL url = new URL(path);
HttpURLConnection httpConnection = (HttpURLConnection) url.openConnection();
httpConnection.setRequestMethod("GET");//请求方法为GET方法
httpConnection.setConnectTimeout(5000);//设置连接超时时间
httpConnection.setReadTimeout(5000);//前面是连接,这里读取超时时间 int code = httpConnection.getResponseCode();
if(code == 200) {
InputStream is = httpConnection.getInputStream();//得到一个服务器端的输入流,代表RSS数据源,也就是获取了news.xml文件
return NewsInfoParser.parseNews(is);
}else {
return null;
} } }
上面代码中String path = context.getResources().getString(R.string.serverip);
其中R.string.serverip对应于res/values/config.xml(通常命名为config.xml,便于理解和维护),config.xml内容如下:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="serverip">http://49.123.72.40/news.xml</string> </resources>
这里我们编写的是网易新闻客户端测试程序,访问的是PC模拟出来的Apache服务器,所以这里的网址是我个人电脑的IP地址: 49.123.72.40 ,以后我们开发真正的项目则是改为公网IP。这里这个config.xml则是方面我们配置网址的,这里修改网址很方便,而且不容易出错,不这样设置可能在程序中多处修改网址才行,很影响效率。
->2:上面代码中,我们通过Http协议获得是一个服务器端new.xml文件的输入流:
InputStream is = httpConnection.getInputStream();很明显这个输入流不是我们想要的,我们通过这个输入流解析这个news.xml文件,这里必然需要编写一个业务类去实现这个解析xml文件:
这里我们新建一个包com.himi.news.service,包中新建一个类NewsInfoParser,如下:
package com.itheima.news.service; import java.io.InputStream;
import java.util.ArrayList;
import java.util.List; import org.xmlpull.v1.XmlPullParser; import android.util.Xml; import com.itheima.news.domain.NewsItem; /**
* 新闻信息的解析器 用来解析xml文件
*
*/
public class NewsInfoParser { /**
* 解析xml数据流返回 新闻数据
*
* @param is
* @return
*/
public static List<NewsItem> parseNews(InputStream is) throws Exception {
//定义一个新闻的集合,用来存放所有的新闻
List<NewsItem> newsItems = new ArrayList<NewsItem>();
//代表一条新闻
NewsItem newsItem = null;
XmlPullParser parser = Xml.newPullParser();
// 设置参数
parser.setInput(is, "utf-8");
// 开始解析
int type = parser.getEventType();
while (type != XmlPullParser.END_DOCUMENT) {
switch (type) {
case XmlPullParser.START_TAG://标签开始
if("item".equals(parser.getName())){
//一个新闻数据要开始了。
newsItem = new NewsItem();
}else if("title".equals(parser.getName())){
String title = parser.nextText();
newsItem.setTitle(title);
}else if("description".equals(parser.getName())){
String description = parser.nextText();
newsItem.setDesc(description);
}else if("image".equals(parser.getName())){
String image = parser.nextText();
newsItem.setImage(image);
}else if("type".equals(parser.getName())){
String newtype = parser.nextText();
newsItem.setType(Integer.parseInt(newtype));
}else if("comment".equals(parser.getName())){
String comment = parser.nextText();
newsItem.setComment(Integer.parseInt(comment));
}
break;
case XmlPullParser.END_TAG://标签结束
if("item".equals(parser.getName())){
newsItems.add(newsItem);
}
break;
}
type = parser.next();// 解析下一个事件
}
return newsItems;
} }
下面定义一个方便外界调用的static方法: public static List<NewsItem> parseNews(InputStream is) ;接收参数为之前http协议获得的输入流,返回是,这里需要另外定义一个信息数据类NewsItem用来存放每条新闻。
->3:我们需要编写一个信息数据类NewsItem存放数据(存放每条新闻信息),如下:
我们新建一个包com.himi.news.domain,这个包下我们新建一个类为NewsItem:
package com.himi.news.domain; public class NewsItem {
private String title;
private String desc;
private String image;
private int type;
private int comment;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
/**
* 它通常只是为了方便输出,比如System.out.println(xx),
* 括号里面的“xx”如果不是String类型的话,就自动调用xx的toString()方法
* 还有其他所有需要显示字符串的地方全部都需要调用tostring()
*/
@Override
public String toString() {
return "NewsItem [title=" + title + ", desc=" + desc + ", image="
+ image + ", type=" + type + ", comment=" + comment + "]";
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
public String getImage() {
return image;
}
public void setImage(String image) {
this.image = image;
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
public int getComment() {
return comment;
}
public void setComment(int comment) {
this.comment = comment;
} }
上面我们已经获得news.xml新闻文件的数据信息,并且单条新闻信息临时存放在NewsItem中,多条新闻信息组合成List集合存放的。返回List<NewsItem>。
(3)上面我们写了一个复杂的xml解析的业务逻辑类,但是我们不能确定这个业务逻辑类一定是对的,所以我们要编程测试代码测试这个逻辑单元:
新建一个com.himi.news.test,编写一个TestNewsInfoParser测试上面我们编写的NewsInfoParser,这里同时也需要配置AndroidManifest.xml文件,
这里我们把news.xml文件复制到Android工程目录下的assets文件夹下,在测试代码中调用这个news.xml文件进行解析:代码如下:
package com.himi.news.test; import java.util.List; import android.test.AndroidTestCase; import com.himi.news.domain.NewsItem;
import com.himi.news.service.NewsInfoParser; public class TestNewsInfoParser extends AndroidTestCase {
public void testparseNews() throws Exception {
// 这里是测试parseNews方法解析xml文件的效果,复制news.xml文件到工程assets目录下测试(测试时期:不从服务器获取)
List<NewsItem> items = NewsInfoParser.parseNews(getContext()
.getAssets().open("news.xml"));// 获取assets文件夹资源需要上下文,这里API提供一个getContext()获取一个虚拟上下文
for (NewsItem item : items) {
System.out.println(item);
} } }
测试结果说明业务逻辑是正确的;至于具体测试流程 详见 Android(java)学习笔记165:Android的Junit调试
(4)接下来,我们去完成布局UI设计:
由于我们希望同时显示多条新闻数据,所以这里我们主布局文件activity_main.xml中是使用ListView,如下:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.himi.news.MainActivity" > <ListView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/lv" /> </RelativeLayout>
这里我们需要使用Android(java)学习笔记204自定义SmartImageView:(这里的SmartImageView是我们自己定义的,真实项目开发的时候我们可以利用开源项目)
package com.himi.news.ui; import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL; import com.himi.news.R; import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.widget.ImageView; /**
* 实现一个子类,扩展系统的ImageView
* @author Administrator
*
*/
public class SmartImageView extends ImageView { private static final int SUCCESS = 1;
private Handler handler = new Handler() {
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case SUCCESS:
Bitmap bitmap = (Bitmap) msg.obj;
setImageBitmap(bitmap);
break; default:
//其他消息,都是获取图片失败,加载默认失败图片
setImageResource(R.drawable.error);
break;
} };
}; public SmartImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// TODO 自动生成的构造函数存根
} public SmartImageView(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO 自动生成的构造函数存根
} public SmartImageView(Context context) {
super(context);
// TODO 自动生成的构造函数存根
} /**
* 设置一个网络的路径给imageview,imageview会自动的把这个路径对应的图片下载下来
* @param path 图片的路径
*/ public void setImageUrl(final String path) {
new Thread() {
public void run() {
try {
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(5000);
conn.setReadTimeout(5000);
conn.setRequestMethod("GET");
int code = conn.getResponseCode();
if(code ==200) {
InputStream is = conn.getInputStream();//获得服务器端的图片文件的输入流
Bitmap bitmap = BitmapFactory.decodeStream(is);//将服务器端的图片文件的输入流 转化为 Bitmap图片文件
//setImageBitmap(bitmap);子线程不能更新UI,这里要使用消息机制
Message msg = Message.obtain();
msg.obj = bitmap;
msg.what = SUCCESS;
handler.sendMessage(msg);
}else {
handler.sendEmptyMessage(0);
}
} catch (Exception e) {
e.printStackTrace();
handler.sendEmptyMessage(0);
} };
}.start();
} }
如果我们加载新闻数据信息,如果获取的图片路径不对的,现实生活中我们应该是默认显示一个" 加载失败 "的图片如下:
这里我们自己自定义的SmartImageView往往不够成熟,考虑不够周全,幸运的时候我们在网上可以利用开源项目,很方便获取非常成熟的SmartImageView开源代码
每条新闻item的布局,自己定义item.xml,如下:
新建一个包com.himi.news.ui,复制之前Android(java)学习笔记204中自定义的SmartImageView到这个包下:
item.xml文件如下:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content" > <com.himi.news.ui.SmartImageView
android:id="@+id/iv_item"
android:layout_width="100dip"
android:layout_height="80dip" /> <TextView
android:id="@+id/tv_item_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dip"
android:layout_marginTop="5dip"
android:layout_toRightOf="@id/iv_item"
android:singleLine="true"
android:text="我是标题"
android:textColor="#000000"
android:textSize="19sp" /> <TextView
android:id="@+id/tv_item_desc"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/tv_item_title"
android:layout_marginLeft="5dip"
android:layout_marginTop="1dip"
android:layout_toRightOf="@id/iv_item"
android:lines="2"
android:text="我是描述,我们都是好孩子"
android:textColor="#AA000000"
android:textSize="14sp" /> <TextView
android:id="@+id/tv_item_type"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="2dip"
android:layout_alignParentRight="true"
android:layout_below="@id/tv_item_desc"
android:background="#ff0000"
android:text="直播" /> </RelativeLayout>
这里自定义的SmartImageView可以自动获取相应网络路径下的图片,更符合我们的需求,所以我们自定义扩展这个ImageView为SmartImageView。
(5)回到MainActivity.java:
package com.himi.news; import java.util.List; import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast; import com.himi.news.domain.NewsItem;
import com.himi.news.net.NewsUtils;
import com.himi.news.ui.SmartImageView; public class MainActivity extends Activity {
protected static final int SUCCESS = 0;
protected static final int ERROR = 1;
private ListView lv;
private Handler handler = new Handler() {
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case SUCCESS:
lv.setAdapter(new NewsAdapter());
break; case ERROR:
Toast.makeText(MainActivity.this, "请求失败,获取失败", 0).show();
break;
} };
}; /**
* 所有的新闻信息
*/ private List<NewsItem> newsItems;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); lv = (ListView)findViewById(R.id.lv); new Thread() {
public void run() {
try {
newsItems = NewsUtils.getAllNews(MainActivity.this);
Message msg = Message.obtain();
msg.what = SUCCESS;
handler.sendMessage(msg);
} catch (Exception e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
Message msg = Message.obtain();
msg.what = ERROR;
handler.sendMessage(msg);
}
};
}.start();
} private class NewsAdapter extends BaseAdapter { public int getCount() {
return newsItems.size();
} public View getView(int position, View convertView, ViewGroup parent) {
View view ;
if(convertView == null) {
view = View.inflate(MainActivity.this, R.layout.item, null);
}else {
view = convertView;
} SmartImageView iv = (SmartImageView) view.findViewById(R.id.iv_item);
TextView tv_title = (TextView) view.findViewById(R.id.tv_item_title);
TextView tv_desc = (TextView) view.findViewById(R.id.tv_item_desc);
TextView tv_type = (TextView) view.findViewById(R.id.tv_item_type);
NewsItem item = newsItems.get(position);
tv_title.setText(item.getTitle());
tv_desc.setText(item.getDesc());
int type = item.getType();
if(type==1) {
tv_type.setText("评论:"+item.getComment());
}else if(type==2) {
tv_type.setText("直播");
tv_type.setBackgroundColor(Color.RED);
}else if(type==3) {
tv_type.setText("视频");
tv_type.setBackgroundColor(Color.BLUE);
} return view;
} public Object getItem(int position) {
return null;
} public long getItemId(int position) {
return 0;
} }
}
(6)布署程序到模拟器上,效果如下:
(7)最后我们模拟出一个场景,当我们关闭Apache服务器的时候,我们的手机网易新闻客户端是不能再去获得服务器资源的,如下:
这个时候我们再次进入程序,效果如下:
这里访问请求失败的时候,我们是使用Toast土司提示信息,显示项目开发的时候,会现实开发项目中,我们事先设置好的访问失败的提示网页拿来显示。
Android(java)学习笔记205:网易新闻RSS客户端应用编写逻辑过程的更多相关文章
- Android(java)学习笔记148:网易新闻RSS客户端应用编写逻辑过程
1.我们的项目需求是编写一个新闻RSS浏览器,RSS(Really Simple Syndication)是一种描述和同步网站内容的格式,是使用最广泛的XML应用.RSS目前广泛用于网上新闻频道,bl ...
- Android(java)学习笔记206:利用开源SmartImageView优化网易新闻RSS客户端
1.我们自己编写的SmartImageView会有很多漏洞,但是我们幸运的可以在网上利用开源项目的,开源项目中有很多成熟的代码,比如SmartImageView都编写的很成熟的 国内我们经常用到htt ...
- Android(java)学习笔记149:利用开源SmartImageView优化网易新闻RSS客户端
1.我们自己编写的SmartImageView会有很多漏洞,但是我们幸运的可以在网上利用开源项目的,开源项目中有很多成熟的代码,比如SmartImageView都编写的很成熟的 国内我们经常用到htt ...
- Java学习笔记——实现一个简易记事本Notepad的编写
记事本功能介绍 1. 新建:记事本清空. 2. 打开:可打开笔记本上任意文本文件. 3. 保存:将文件保存至当前文件夹. 4. 另存为:将文件保存至任意位置. 5. 退出:退出时确 ...
- 0028 Java学习笔记-面向对象-Lambda表达式
匿名内部类与Lambda表达式示例 下面代码来源于:0027 Java学习笔记-面向对象-(非静态.静态.局部.匿名)内部类 package testpack; public class Test1{ ...
- 《Java学习笔记(第8版)》学习指导
<Java学习笔记(第8版)>学习指导 目录 图书简况 学习指导 第一章 Java平台概论 第二章 从JDK到IDE 第三章 基础语法 第四章 认识对象 第五章 对象封装 第六章 继承与多 ...
- Android动画学习笔记-Android Animation
Android动画学习笔记-Android Animation 3.0以前,android支持两种动画模式,tween animation,frame animation,在android3.0中 ...
- Java学习笔记:语言基础
Java学习笔记:语言基础 2014-1-31 最近开始学习Java,目的倒不在于想深入的掌握Java开发,而是想了解Java的基本语法,可以阅读Java源代码,从而拓展一些知识面.同时为学习An ...
- Android 数字签名学习笔记
Android 数字签名学习笔记 在Android系统中,所有安装到系统的应用程序都必有一个数字证书,此数字证书用于标识应用程序的作者和在应用程序之间建立信任关系,如果一个permission的pro ...
随机推荐
- css3 3D盒子效果
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...
- Tomcat,Weblogic,WebSphere,JBoss四种服务器简单对比
1,tomcat是Servlet容器,支持JSP.Servlet.JDBC等J2EE关键技术,常用于tomcat开发基于数据库.Servlet和JSP页面的Web应用.2,tomcat不是EJB容器, ...
- GetSystemMetrics() 函数的用法
可以用GetSystemMetrics函数可以获取系统分辨率,但这只是其功能之一,GetSystemMetrics函数只有一个参数,称之为「索引」,这个索引有75个标识符,通过设置不同的标识符就可以获 ...
- 【UVA11324】The Largest Clique (SCC)
题意: 给一张有向图G,求一个结点数最大的结点集,使得该结点中任意两个结点 u 和 v满足:要么 u 可以到达 v, 要么 v 可以到达 u(u 和 v 相互可达也可以). 分析: Tarjan求SC ...
- WordPress Simple Login Registration插件’username‘参数跨站脚本漏洞
漏洞名称: WordPress Simple Login Registration插件’username‘参数跨站脚本漏洞 CNNVD编号: CNNVD-201308-519 发布时间: 2013-0 ...
- Scala:(2)控制结构
(1)if else val s=) else -1 (2)循环 ){ r=r*n n-= } ///for 循环 to n) r=r*i //until val s="Hello" ...
- Maven学习(1) - Maven入门
home index:http://maven.apache.org/ download:http://maven.apache.org/download.cgi install: http://ma ...
- 转 -- MVC+EF easyui dataGrid 动态加载分页表格
首先上javascript的代码 <script type="text/javascript"> $(function () { LoadGrid(); }) //加载 ...
- BlogEngine.Net
BlogEngine.Net架构与源代码分析系列part1:开篇介绍 2008-11-05 15:27 by GUO Xingwang, ...阅读, ...评论, 收藏, 编辑 最近我要开始这个系列 ...
- 折腾iPhone的生活——运营商信号显示数据化
iOS7以后iphone的信号都是用5个小圆圈显示的,像这样 但是还有种显示方法可以用数字信号显示信号量,比较适合很专注于生活品质的人和对数字有偏爱的人,像这样: 这样还有个好处是可以节约顶部状态栏的 ...