上次我们学习如何从网络上获取一张图片,今天我们学习如何从网络上获取文本文件,以XML文件为例子。因为XML文件在实际开发中最为常见。
我们以下面图片为例子学习如何从网络上获取XML文件
我们的xml文件为:
9月起办理手机卡需实名认证
方案要求,从2015年2月1日起,基础电信企业和虚拟运营商的各类营销渠道在为用户办理电话入网手续时,停止人工录入方式,改用专用移动应用程序
15687
http://192.168.1.100:8080/images/6.jpg
富士康50亿美元印度建厂 中国制造成本直逼美国
中国制造”和“印度制造”正展开新一轮的竞争与合作关系。全球最大的代工企业、中国台湾的富士康8月8日在印度签约,它到印度投资设厂的计划变为现实
16359
http://192.168.1.100:8080/images/7.jpg
刘强东也学乔布斯 加入“1元年薪”俱乐部
8月8日,京东集团董事长兼CEO刘强东与奶茶妹妹的结婚证照片在朋友圈里刷屏了。可就在前一天,刘强东还将自己未来十年的年薪降至1元
14112
http://192.168.1.100:8080/images/7.jpg
既然我们要将xml文件中的内容显示到界面上,那必须先要拿到xml文件中的内容。
1: 从网络上获取XML文件的内容
开启一个子线程从网络上获取服务器的数据
public void getNewsInfo()
{
//在子线程中获取服务器的数据
Thread thread = new Thread(){
@Override
public void run() {
//1:确定地址
String path = "http://192.168.1.123:8080/news.xml";
try {
URL url = new URL(path);
//建立连接
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
//设置请求方式
conn.setRequestMethod("GET");
//设置请求超时时间
conn.setConnectTimeout(5000);
//设置读取超时时间
conn.setReadTimeout(5000);
//判断是否获取成功
if(conn.getResponseCode() == 200)
{
//获得输入流
InputStream is = conn.getInputStream();
//解析输入流中的数据
parseXmlInfo(is);
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
//启动线程
thread.start();
}
public void parseXmlInfo(InputStream is)
{
/*我们用pull解析器解析xml文件*/
//1.先拿到pull解析器
XmlPullParser xParser = Xml.newPullParser();
try {
xParser.setInput(is, "utf-8");
//获取事件的类型
int eventType = xParser.getEventType();
News news = null;
while(eventType != XmlPullParser.END_DOCUMENT)
{
switch (eventType) {
case XmlPullParser.START_TAG:
//当事件的开始类型newslist,代表的是xml文件的数据开始
if("newslist".equals(xParser.getName()))
{
//这时候我们就new出来一个list,用于保存数据
newList = new ArrayList();
}//当事件类型是news,说明是一条新闻
else if ("news".equals(xParser.getName())) {
//new出一个news的对象
news = new News();
}
else if ("detail".equals(xParser.getName())) {
String detail = xParser.nextText();
news.setDetail(detail);
}
else if ("title".equals(xParser.getName())) {
String title = xParser.nextText();
news.setTitle(title);
}
else if ("comment".equals(xParser.getName())) {
String comment = xParser.nextText();
news.setComment(comment);
}
else if ("image".equals(xParser.getName())) {
String image = xParser.nextText();
news.setImage(image);
}
break;
case XmlPullParser.END_TAG:
//当结束时间是news时,说明一条news已经解析完成,并且加入到集合中
if("news".equals(xParser.getName()))
{
newList.add(news);
}
break;
}
eventType = xParser.next();
}
//打印
for (News n : newList) {
System.out.println(n.toString());
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
打印为:
可以看到我们是从服务器取到了数据,并且数据是正确的。
2: 既然可以正确的取到数据,那我们就将数据显示到界面上,既然要显示, 那就的用listview,既然要用listview,那就必须为listview的每一个增加一个布局文件
我们找到listview,然后设置Adapter即可
ListView lv = (ListView) findViewById(R.id.lv);
lv.setAdapter(new MyAdapter());
class MyAdapter extends BaseAdapter
{
@Override
public int getCount() {
// TODO Auto-generated method stub
return newList.size();
}
@Override
public Object getItem(int arg0) {
// TODO Auto-generated method stub
return null;
}
@Override
public long getItemId(int arg0) {
// TODO Auto-generated method stub
return 0;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
//取到一条新闻
News news = newList.get(position);
//找到一个view对象
View v = View.inflate(MainActivity.this, R.layout.item_listview, null);
//根据ID找到控件
TextView tv_title = (TextView) v.findViewById(R.id.tv_title);
TextView tv_detail = (TextView) v.findViewById(R.id.tv_detail);
TextView tv_comment = (TextView) v.findViewById(R.id.tv_comment);
ImageView siv = (ImageView)v.findViewById(R.id.iv);
//设置数据
tv_title.setText(news.getTitle());
tv_detail.setText(news.getDetail());
tv_comment.setText(news.getComment() + "条评论");
siv.setImageResource(R.drawable.ic_launcher);
return v;
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getNewsInfo();
ListView lv = (ListView) findViewById(R.id.lv);
lv.setAdapter(new MyAdapter());
}
运行效果为NULL指针异常,这是为什么呢?
既然NULL指针异常,代码是哪行有问题? 仔细一看是
lv.setAdapter(new MyAdapter());
这是因为,我们的获取服务器的数据是在一个子线程中运行的,而我们setAdapter的任务是在主线程中获取的,这时候当我们的服务器数据还没获取完毕,我们就设置数据了,就会导致异常发生。所以我们要当服务器的数据获取完后,才去设置adapter
修改代码:增加消息机制
//当数据接受完成后,发生消息
handler.sendEmptyMessage(1);
//打印
for (News n : newList) {
System.out.println(n.toString());
}
//在消息中设置数据
Handler handler = new Handler()
{
public void handleMessage(android.os.Message msg)
{
ListView lv = (ListView) findViewById(R.id.lv);
lv.setAdapter(new MyAdapter());
};
};
这样的话就出现了刚开始的截图。但是我们还需要优化listview
优化后的listview
public View getView(int position, View convertView, ViewGroup parent) {
News news = newList.get(position);
ViewHolder mHolder;
View v = null;
//当缓冲为空的时候,创建view对象,并将组件封装到view的tag中
if(convertView == null)
{
v = View.inflate(MainActivity.this, R.layout.item_listview, null);
mHolder = new ViewHolder();
mHolder.tv_title = (TextView) v.findViewById(R.id.tv_title);
mHolder.tv_detail = (TextView) v.findViewById(R.id.tv_detail);
mHolder.tv_comment = (TextView) v.findViewById(R.id.tv_comment);
mHolder.siv = (ImageView)v.findViewById(R.id.iv);
v.setTag(mHolder);
}
else {//当缓冲不为空时,就将缓冲的数据取出来用,不用重新创建,这样可节省系统资源
v = convertView;
mHolder =(ViewHolder) v.getTag();
}
//设置数据到组件
mHolder.tv_title.setText(news.getTitle());
mHolder.tv_detail.setText(news.getDetail());
mHolder.tv_comment.setText(news.getComment() + "条评论");
mHolder.siv.setImageResource(R.drawable.ic_launcher);
return v;
}
这样我们的从服务端的获取数据就算完成了,但是我们还没获取服务器的图片资源,图片资源是通过一个地址封装到xml文件中的,我们需要再次拿到xml文件中的图片地址再次请求服务器获取图片资源,关于如何获取服务器图片资源我上节都讲过了,这里就不做了。