标签:
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服务器软件):
1 package com.himi.news.net; 2 3 import java.io.InputStream; 4 import java.net.HttpURLConnection; 5 import java.net.URL; 6 import java.util.List; 7 8 import org.apache.http.HttpConnection; 9 10 import android.content.Context; 11 12 import com.himi.news.R; 13 import com.himi.news.domain.NewsItem; 14 import com.himi.news.service.NewsInfoParser; 15 16 /** 17 * 新闻的业务类:工具类 18 * @author Administrator 19 * 20 */ 21 22 public class NewsUtils { 23 //设置静态方法,方便外界调用 24 //获取服务器端网页新闻的信息,这里需要写一个新闻信息数据类,新建包com.himi.news.info,新建类NewsItem存储信息 25 26 /** 27 * 得到服务器最新的所有的新闻信息,我们这里路径放在res/values/config.xml中 28 * res资源我们必须使用上下文才能访问,所有getAllNews的参数是Context context 29 * @param context上下文 30 * @return 31 */ 32 public static List<NewsItem> getAllNews(Context context) throws Exception { 33 String path = context.getResources().getString(R.string.serverip); 34 35 URL url = new URL(path); 36 HttpURLConnection httpConnection = (HttpURLConnection) url.openConnection(); 37 httpConnection.setRequestMethod("GET");//请求方法为GET方法 38 httpConnection.setConnectTimeout(5000);//设置连接超时时间 39 httpConnection.setReadTimeout(5000);//前面是连接,这里读取超时时间 40 41 int code = httpConnection.getResponseCode(); 42 if(code == 200) { 43 InputStream is = httpConnection.getInputStream();//得到一个服务器端的输入流,代表RSS数据源,也就是获取了news.xml文件 44 return NewsInfoParser.parseNews(is); 45 }else { 46 return null; 47 } 48 49 50 } 51 52 }
上面代码中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,如下:
1 package com.itheima.news.service; 2 3 import java.io.InputStream; 4 import java.util.ArrayList; 5 import java.util.List; 6 7 import org.xmlpull.v1.XmlPullParser; 8 9 import android.util.Xml; 10 11 import com.itheima.news.domain.NewsItem; 12 13 /** 14 * 新闻信息的解析器 用来解析xml文件 15 * 16 */ 17 public class NewsInfoParser { 18 19 /** 20 * 解析xml数据流返回 新闻数据 21 * 22 * @param is 23 * @return 24 */ 25 public static List<NewsItem> parseNews(InputStream is) throws Exception { 26 //定义一个新闻的集合,用来存放所有的新闻 27 List<NewsItem> newsItems = new ArrayList<NewsItem>(); 28 //代表一条新闻 29 NewsItem newsItem = null; 30 XmlPullParser parser = Xml.newPullParser(); 31 // 设置参数 32 parser.setInput(is, "utf-8"); 33 // 开始解析 34 int type = parser.getEventType(); 35 while (type != XmlPullParser.END_DOCUMENT) { 36 switch (type) { 37 case XmlPullParser.START_TAG://标签开始 38 if("item".equals(parser.getName())){ 39 //一个新闻数据要开始了。 40 newsItem = new NewsItem(); 41 }else if("title".equals(parser.getName())){ 42 String title = parser.nextText(); 43 newsItem.setTitle(title); 44 }else if("description".equals(parser.getName())){ 45 String description = parser.nextText(); 46 newsItem.setDesc(description); 47 }else if("image".equals(parser.getName())){ 48 String image = parser.nextText(); 49 newsItem.setImage(image); 50 }else if("type".equals(parser.getName())){ 51 String newtype = parser.nextText(); 52 newsItem.setType(Integer.parseInt(newtype)); 53 }else if("comment".equals(parser.getName())){ 54 String comment = parser.nextText(); 55 newsItem.setComment(Integer.parseInt(comment)); 56 } 57 break; 58 case XmlPullParser.END_TAG://标签结束 59 if("item".equals(parser.getName())){ 60 newsItems.add(newsItem); 61 } 62 break; 63 } 64 type = parser.next();// 解析下一个事件 65 } 66 return newsItems; 67 } 68 69 }
下面定义一个方便外界调用的static方法: public static List<NewsItem> parseNews(InputStream is) ;接收参数为之前http协议获得的输入流,返回是,这里需要另外定义一个信息数据类NewsItem用来存放每条新闻。
->3:我们需要编写一个信息数据类NewsItem存放数据(存放每条新闻信息),如下:
我们新建一个包com.himi.news.domain,这个包下我们新建一个类为NewsItem:
1 package com.himi.news.domain; 2 3 public class NewsItem { 4 private String title; 5 private String desc; 6 private String image; 7 private int type; 8 private int comment; 9 public String getTitle() { 10 return title; 11 } 12 public void setTitle(String title) { 13 this.title = title; 14 } 15 /** 16 * 它通常只是为了方便输出,比如System.out.println(xx), 17 * 括号里面的“xx”如果不是String类型的话,就自动调用xx的toString()方法 18 * 还有其他所有需要显示字符串的地方全部都需要调用tostring() 19 */ 20 @Override 21 public String toString() { 22 return "NewsItem [title=" + title + ", desc=" + desc + ", image=" 23 + image + ", type=" + type + ", comment=" + comment + "]"; 24 } 25 public String getDesc() { 26 return desc; 27 } 28 public void setDesc(String desc) { 29 this.desc = desc; 30 } 31 public String getImage() { 32 return image; 33 } 34 public void setImage(String image) { 35 this.image = image; 36 } 37 public int getType() { 38 return type; 39 } 40 public void setType(int type) { 41 this.type = type; 42 } 43 public int getComment() { 44 return comment; 45 } 46 public void setComment(int comment) { 47 this.comment = comment; 48 } 49 50 }
上面我们已经获得news.xml新闻文件的数据信息,并且单条新闻信息临时存放在NewsItem中,多条新闻信息组合成List集合存放的。返回List<NewsItem>。
(3)上面我们写了一个复杂的xml解析的业务逻辑类,但是我们不能确定这个业务逻辑类一定是对的,所以我们要编程测试代码测试这个逻辑单元:
新建一个com.himi.news.test,编写一个TestNewsInfoParser测试上面我们编写的NewsInfoParser,这里同时也需要配置AndroidManifest.xml文件,
这里我们把news.xml文件复制到Android工程目录下的assets文件夹下,在测试代码中调用这个news.xml文件进行解析:代码如下:
1 package com.himi.news.test; 2 3 import java.util.List; 4 5 import android.test.AndroidTestCase; 6 7 import com.himi.news.domain.NewsItem; 8 import com.himi.news.service.NewsInfoParser; 9 10 public class TestNewsInfoParser extends AndroidTestCase { 11 public void testparseNews() throws Exception { 12 // 这里是测试parseNews方法解析xml文件的效果,复制news.xml文件到工程assets目录下测试(测试时期:不从服务器获取) 13 List<NewsItem> items = NewsInfoParser.parseNews(getContext() 14 .getAssets().open("news.xml"));// 获取assets文件夹资源需要上下文,这里API提供一个getContext()获取一个虚拟上下文 15 for (NewsItem item : items) { 16 System.out.println(item); 17 } 18 19 } 20 21 }
测试结果说明业务逻辑是正确的;至于具体测试流程 详见Android(java)学习笔记165:Android下编写单元测试代码(Junit Test)
(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>
每条新闻item的布局,自己定义item.xml,如下:
这里我们需要使用Android(java)学习笔记204自定义SmartImageView,
新建一个包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:
1 package com.himi.news; 2 3 import java.util.List; 4 5 import android.app.Activity; 6 import android.graphics.Color; 7 import android.os.Bundle; 8 import android.os.Handler; 9 import android.os.Message; 10 import android.view.View; 11 import android.view.ViewGroup; 12 import android.widget.BaseAdapter; 13 import android.widget.ImageView; 14 import android.widget.ListView; 15 import android.widget.TextView; 16 import android.widget.Toast; 17 18 import com.himi.news.domain.NewsItem; 19 import com.himi.news.net.NewsUtils; 20 import com.himi.news.ui.SmartImageView; 21 22 public class MainActivity extends Activity { 23 protected static final int SUCCESS = 0; 24 protected static final int ERROR = 1; 25 private ListView lv; 26 private Handler handler = new Handler() { 27 public void handleMessage(android.os.Message msg) { 28 switch (msg.what) { 29 case SUCCESS: 30 lv.setAdapter(new NewsAdapter()); 31 break; 32 33 case ERROR: 34 Toast.makeText(MainActivity.this, "请求失败,获取失败", 0).show(); 35 break; 36 } 37 38 }; 39 }; 40 41 /** 42 * 所有的新闻信息 43 */ 44 45 private List<NewsItem> newsItems; 46 @Override 47 protected void onCreate(Bundle savedInstanceState) { 48 super.onCreate(savedInstanceState); 49 setContentView(R.layout.activity_main); 50 51 lv = (ListView)findViewById(R.id.lv); 52 53 new Thread() { 54 public void run() { 55 try { 56 newsItems = NewsUtils.getAllNews(MainActivity.this); 57 Message msg = Message.obtain(); 58 msg.what = SUCCESS; 59 handler.sendMessage(msg); 60 } catch (Exception e) { 61 // TODO 自动生成的 catch 块 62 e.printStackTrace(); 63 Message msg = Message.obtain(); 64 msg.what = ERROR; 65 handler.sendMessage(msg); 66 } 67 }; 68 }.start(); 69 } 70 71 72 private class NewsAdapter extends BaseAdapter { 73 74 public int getCount() { 75 return newsItems.size(); 76 } 77 78 public View getView(int position, View convertView, ViewGroup parent) { 79 View view ; 80 if(convertView == null) { 81 view = View.inflate(MainActivity.this, R.layout.item, null); 82 }else { 83 view = convertView; 84 } 85 86 SmartImageView iv = (SmartImageView) view.findViewById(R.id.iv_item); 87 TextView tv_title = (TextView) view.findViewById(R.id.tv_item_title); 88 TextView tv_desc = (TextView) view.findViewById(R.id.tv_item_desc); 89 TextView tv_type = (TextView) view.findViewById(R.id.tv_item_type); 90 NewsItem item = newsItems.get(position); 91 tv_title.setText(item.getTitle()); 92 tv_desc.setText(item.getDesc()); 93 int type = item.getType(); 94 if(type==1) { 95 tv_type.setText("评论:"+item.getComment()); 96 }else if(type==2) { 97 tv_type.setText("直播"); 98 tv_type.setBackgroundColor(Color.RED); 99 }else if(type==3) { 100 tv_type.setText("视频"); 101 tv_type.setBackgroundColor(Color.BLUE); 102 } 103 104 return view; 105 } 106 107 public Object getItem(int position) { 108 return null; 109 } 110 111 public long getItemId(int position) { 112 return 0; 113 } 114 115 116 } 117 }
(6)布署程序到模拟器上,效果如下:
Android(java)学习笔记205:网易新闻客户端应用编写逻辑过程
标签:
原文地址:http://www.cnblogs.com/hebao0514/p/4779505.html