And.He

Miracles happen every day.

  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::
  34 随笔 :: 0 文章 :: 89 评论 :: 0 引用

公告

置顶随笔 #

摘要: 一、摘要继上一篇博客《模仿网易新闻客户端(一)》之后,笔者继续开发我们自己的“网易新闻客户端”,由于找不到现成的url新闻链接地址,所以这里就用RSS订阅所提供的url,这里所用到的链接仍然是网易新闻中心的RSS地址http://www.163.com/rss/,然后通过解析xml内容,以ListView的方式呈现在手机界面上。还有一个问题,因为RSS所提供的xml资源里面,没有对应item的图片,所以,ListView里面每一个Item都没有图片,这点有点遗憾,但也没事,实现其功能就行了。废话不多说,老惯例,先看效果图二、效果截图三、解析RSS首先,先大概地看一下RSS所提供XML的数据结构阅读全文
posted @ 2012-04-02 23:04 And.He 阅读(1437) 评论(13) 编辑

摘要: 来公司快三个月了,因为项目紧,很长一段时间没有发表博文了,做了两个项目,功能差不多,下面简单说明一下有哪些功能就相当于国内的新浪微博或者腾讯微博客户端之类的,请求并解析从网络获取到的特定格式的xml数据,按照一定的格式显示出来,首页是一个ListView,这点不用说吧,点击某个item的时候跳转到详细页面,在详细页面可以发表评论,可以分享到Twitter,这里用到一个控件,叫PopupWindow,记得当时弄这个东西弄了几天才实现,对于分享到Twitter,这也是个难点,之前发表了一篇博文,好像没怎么讲清楚,很多人在问我,大家的问题基本上都是我之前遇到过的,在创建应用的时候一定要设置CALLB阅读全文
posted @ 2011-08-11 16:38 And.He 阅读(1556) 评论(2) 编辑

2012年4月17日 #

关于try...catch...finally里面的return一直是面试的一个热门考点。无非就分以下几个情况:

1、当有finally语句并且try中有return,在执行到return(还未执行)的时候,会先执行finally里面的内容,然后再执行行try中的return。

package com.and.java.demo;

public class 测试 {
    public static void main(String[] args) {
        System.out.println(new 测试().test());
    }

    public String test() {
        try {
            System.out.println("try{...}");
            return "try";
        } catch (Exception e) {
            System.out.println("catch{...}");
            return "catch";
        } finally {
            System.out.println("finally{...}");
        }
    }
}

输出:

try{...}
finally{...}
try

2、在1的基础上,如果finally里面也有return语句,则try代码块中的return被屏蔽(不执行),即在try中遇到return的时候,会先执行finally里面的内容(包括finally里面的return语句)。

package com.and.java.demo;

public class 测试 {
    public static void main(String[] args) {
        System.out.println(new 测试().test());
    }

    public String test() {
        try {
            System.out.println("try{...}");
            return "try";
        } catch (Exception e) {
            System.out.println("catch{...}");
            return "catch";
        } finally {
            System.out.println("finally{...}");
            return "finally";
        }
    }
}

输出:

try{...}
finally{...}
finally

遇到的问题:

这两种情况想必大家已经掌握。但是还有一种情况,也是我不能理解的地方。一般情况下,在finally里面作一些数据的关闭操作(比如文件,输入/输出流,数据库的关闭),试想一下,要是我们在finally里面对要返回的值进行修改,那会反应到最终的结果上去吗?(因为从上面的讲解可以知道,当try里面有return的时候,它不会立刻执行,会先执行finally里面的内容,然后再执行return)。

package com.and.java.demo;

public class 测试 {
    public static void main(String[] args) {
        System.out.println(new 测试().test());
    }

    public String test() {
        String result = "";
        try {
            result = "try";
            return result;
        } catch (Exception e) {
            result = "catch";
            return result;
        } finally {
            result = "finally";
        }
    }
}

试想一下,它会输出"try" 呢还是"finally"呢?

输出:

try

确实只输出try,但是我们在finally里面是改变了result的值呀?
再进一步改进,判断finally里面的赋值语句是否执行

package com.and.java.demo;

public class 测试 {
    public static void main(String[] args) {
        System.out.println(new 测试().test());
    }

    public String test() {
        String result = "";
        try {
            result = "try";
            return result;
        } catch (Exception e) {
            result = "catch";
            return result;
        } finally {
            System.out.println("t1->"+result);
            result = "finally";
            System.out.println("t2->"+result);
        }
    }
}

输出:

t1->try
t2->finally
try

从输出结果可以看出,finally里面的赋值语句是执行了的,但是在return结果中怎么就没变呢?(目前暂时研究到这个地步,仍没搞明白,还望各位高手指点)

posted @ 2012-04-17 15:15 And.He 阅读(288) 评论(10) 编辑

2012年4月2日 #

一、摘要

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






 

posted @ 2012-04-02 23:04 And.He 阅读(1437) 评论(13) 编辑

2012年3月31日 #

一、摘要

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

二、效果截图

本来想找个软件动态截图,但是好像没找着。。。这样的话,看不出来点击之后的动态切换效果了。以后找着了再来替换。

三、底部类似TabHost组件切换效果的实现

为了便于大家亲自动手实践,这里的讲解顺序就按照开发的顺序来讲,所以先做这个底部的“TabHost”,然后再具体来实现里面的五个页面布局。
类似于图3到图5三张图片所示,当点击“新闻”或者“话题”或者“投票”的时候,有个稍微透明的红色背景的ImageView做相应的移动。这其实就是给ImageView设置了一个位移动画,当点击事件触发的时候,首先切换点击后的图片(有点类似于按下效果的图片),然后开始移动铺在上面的红色图片,让用户感觉到有移动的过程,增强用户体验。
关于这个位移动画,需要用到TranslateAnimation类,移动的核心代码也就几行,因为这个移动功能不但在底部控件上使用,而且在顶部也使用了,所以,为了以后使用方便,我们把它单独定义在一个类里面MoveBg.java
View Code
package com.and.netease.utils;

import android.view.View;
import android.view.animation.TranslateAnimation;

public class MoveBg {
/**
* 移动方法
*
*
@param v
* 需要移动的View
*
@param startX
* 起始x坐标
*
@param toX
* 终止x坐标
*
@param startY
* 起始y坐标
*
@param toY
* 终止y坐标
*/
public static void moveFrontBg(View v, int startX, int toX, int startY, int toY) {
TranslateAnimation anim = new TranslateAnimation(startX, toX, startY, toY);
anim.setDuration(200);
anim.setFillAfter(true);
v.startAnimation(anim);
}
}
里面的各个参数有相应的说明。
然后就来开发这个带有TabHost功能的组件。根据文档http://developer.android.com/resources/tutorials/views/hello-tabwidget.html说明,在xml中定义TabHost的时候,必须使用TabWidget和FrameLayou两个组件,而且它们的id也应该是android:id="@android:id/tabs"和android:id="@android:id/tabcontent",由于系统提供的TabHost界面不怎么好看,所以这里想到自己来定义它,但是这两个组件是不可以不写的,这里,把TabWidget界面隐藏掉了,取而代之的是RadioGroup组件来实现底部类似于TabHost的控件。具体布局代码如main.xml
View Code
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width
="fill_parent"
android:layout_height
="fill_parent"
android:orientation
="vertical" >

<TabHost
android:id="@android:id/tabhost"
android:layout_width
="fill_parent"
android:layout_height
="fill_parent" >

<LinearLayout
android:layout_width="fill_parent"
android:layout_height
="fill_parent"
android:orientation
="vertical" >

<FrameLayout
android:id="@android:id/tabcontent"
android:layout_width
="fill_parent"
android:layout_height
="0.0dip"
android:layout_weight
="1.0" >
</FrameLayout>

<TabWidget
android:id="@android:id/tabs"
android:layout_width
="fill_parent"
android:layout_height
="wrap_content"
android:visibility
="gone" />

<RelativeLayout
android:id="@+id/layout_bottom"
android:layout_width
="fill_parent"
android:layout_height
="wrap_content" >

<RadioGroup
android:id="@+id/radiogroup"
android:layout_width
="fill_parent"
android:layout_height
="wrap_content"
android:layout_gravity
="bottom"
android:background
="@drawable/bottombg"
android:gravity
="center_vertical"
android:orientation
="horizontal" >

<RadioButton
android:id="@+id/radio_news"
android:layout_width
="wrap_content"
android:background
="@drawable/tab_selector_news"
android:button
="@null"
android:checked
="true" />

<RadioButton
android:id="@+id/radio_topic"
android:layout_width
="wrap_content"
android:background
="@drawable/tab_selector_topic"
android:button
="@null" />

<RadioButton
android:id="@+id/radio_pic"
android:layout_width
="wrap_content"
android:background
="@drawable/tab_selector_pic"
android:button
="@null" />

<RadioButton
android:id="@+id/radio_follow"
android:layout_width
="wrap_content"
android:background
="@drawable/tab_selector_follow"
android:button
="@null" />

<RadioButton
android:id="@+id/radio_vote"
android:layout_width
="wrap_content"
android:background
="@drawable/tab_selector_vote"
android:button
="@null" />
</RadioGroup>
</RelativeLayout>
</LinearLayout>
</TabHost>

</LinearLayout>
注意里面的RadioButton组件,当初测试的时候没有设置android:button="@null",只设置了background="@drawable/..."属性(这是一个selector属性,可以在xml文件中定义一些控件的按下效果,或者获取焦点等不同状态下的资源),出现点击不切换图片的问题。
对应的selector文件对应如下tab_selector_news.xml
View Code
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">

<item android:drawable="@drawable/current_news_tab" android:state_checked="true"/>
<item android:drawable="@drawable/back_news_tab" android:state_checked="false"/>

</selector>
其它几个,只是替换不同的图片资源罢了,不再一一列出。这些资源文件放在res目录下的drawable文件夹下(如果没有,则新建)
有了布局文件,还需要在Activity中设置一下,为每个TabHost添加具体的Tab页面,如下
tabHost = getTabHost();
tabHost.addTab(tabHost.newTabSpec("news").setIndicator("News").setContent(new Intent(this, TabNewsActivity.class)));
tabHost.addTab(tabHost.newTabSpec("topic").setIndicator("Topic").setContent(new Intent(this, TabTopicActivity.class)));
tabHost.addTab(tabHost.newTabSpec("picture").setIndicator("Picture").setContent(new Intent(this, TabPicActivity.class)));
tabHost.addTab(tabHost.newTabSpec("follow").setIndicator("Follow").setContent(new Intent(this, TabFollowActivity.class)));
tabHost.addTab(tabHost.newTabSpec("vote").setIndicator("Vote").setContent(new Intent(this, TabVoteActivity.class)));
当然,相应的目标Activity自然暂且随意创建
然后为RadioGroup设置选择改变事件监听器,当选择改变,改变TabHost中当前显示的Activity页面
private OnCheckedChangeListener checkedChangeListener = new OnCheckedChangeListener() {

@Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
switch (checkedId) {
case R.id.radio_news:
tabHost.setCurrentTabByTag("news");
break;
case R.id.radio_topic:
tabHost.setCurrentTabByTag("topic");
break;
case R.id.radio_pic:
tabHost.setCurrentTabByTag("picture");
break;
case R.id.radio_follow:
tabHost.setCurrentTabByTag("follow");
break;
case R.id.radio_vote:
tabHost.setCurrentTabByTag("vote");
break;

default:
break;
}
}
};
至此就实现了一个自定义的“TabHost”,接下来再添加那个移动的特效
页面上的

是一个RelativeLayout布局,我只是在这个layout上面添加了一个ImageView,然后当点击的时候,移动它的位置来实现效果

private OnCheckedChangeListener checkedChangeListener = new OnCheckedChangeListener() {

@Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
switch (checkedId) {
case R.id.radio_news:
tabHost.setCurrentTabByTag("news");
// moveFrontBg(img, startLeft, 0, 0, 0);
MoveBg.moveFrontBg(img, startLeft, 0, 0, 0);
startLeft = 0;
break;
case R.id.radio_topic:
tabHost.setCurrentTabByTag("topic");
MoveBg.moveFrontBg(img, startLeft, img.getWidth(), 0, 0);
startLeft = img.getWidth();
break;
case R.id.radio_pic:
tabHost.setCurrentTabByTag("picture");
MoveBg.moveFrontBg(img, startLeft, img.getWidth() * 2, 0, 0);
startLeft = img.getWidth() * 2;
break;
case R.id.radio_follow:
tabHost.setCurrentTabByTag("follow");
MoveBg.moveFrontBg(img, startLeft, img.getWidth() * 3, 0, 0);
startLeft = img.getWidth() * 3;
break;
case R.id.radio_vote:
tabHost.setCurrentTabByTag("vote");
MoveBg.moveFrontBg(img, startLeft, img.getWidth() * 4, 0, 0);
startLeft = img.getWidth() * 4;
break;

default:
break;
}
}
};

此处要记住移动的初始位置和起始位置就行了。Y坐标轴上不变,只横向移动。至此,这个功能实现完了

四、顶部按下效果实现

顶部和底部那个自定义控件的实现效果大体是一样的,唯一不同的就是,这个移动的不再是ImageView,而是一个TextView,在移动完成之后还需要改变这个TextView上的文字,仅此而已,而已文件如下layout_news.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width
="fill_parent"
android:layout_height
="fill_parent"
android:orientation
="vertical" >

<RelativeLayout
android:id="@+id/layout_top"
android:layout_width
="match_parent"
android:layout_height
="40dip"
android:background
="#990000" >

<ImageView
android:id="@+id/img_netease_top"
android:layout_width
="wrap_content"
android:layout_height
="wrap_content"
android:layout_centerVertical
="true"
android:layout_marginLeft
="10dip"
android:src
="@drawable/netease_top" />

<TextView
android:layout_width="wrap_content"
android:layout_height
="wrap_content"
android:layout_centerVertical
="true"
android:layout_toRightOf
="@+id/img_netease_top"
android:text
="@string/news_top_left_text"
android:textColor
="@android:color/white"
android:textSize
="20sp" />

<ImageView
android:layout_width="wrap_content"
android:layout_height
="wrap_content"
android:layout_alignParentRight
="true"
android:src
="@drawable/duoyun" />
</RelativeLayout>

<RelativeLayout
android:id="@+id/layout_title_bar"
android:layout_width
="fill_parent"
android:layout_height
="35dip"
android:background
="@android:color/white"
android:paddingLeft
="10dip"
android:paddingRight
="10dip" >

<LinearLayout
android:layout_width="fill_parent"
android:layout_height
="match_parent"
android:orientation
="horizontal" >

<RelativeLayout
android:id="@+id/layout"
android:layout_width
="match_parent"
android:layout_height
="match_parent"
android:layout_weight
="1" >

<TextView
android:id="@+id/tv_title_bar_news"
android:layout_width
="wrap_content"
android:layout_height
="wrap_content"
android:layout_centerInParent
="true"
android:text
="@string/title_news_category_tops" />
</RelativeLayout>

<RelativeLayout
android:layout_width="match_parent"
android:layout_height
="match_parent"
android:layout_weight
="1" >

<TextView
android:id="@+id/tv_title_bar_sport"
android:layout_width
="wrap_content"
android:layout_height
="wrap_content"
android:layout_centerInParent
="true"
android:text
="@string/title_news_category_sport" />
</RelativeLayout>

<RelativeLayout
android:layout_width="match_parent"
android:layout_height
="match_parent"
android:layout_weight
="1" >

<TextView
android:id="@+id/tv_title_bar_play"
android:layout_width
="wrap_content"
android:layout_height
="wrap_content"
android:layout_centerInParent
="true"
android:text
="@string/title_news_category_play" />
</RelativeLayout>

<RelativeLayout
android:layout_width="match_parent"
android:layout_height
="match_parent"
android:layout_weight
="1" >

<TextView
android:id="@+id/tv_title_bar_finance"
android:layout_width
="wrap_content"
android:layout_height
="wrap_content"
android:layout_centerInParent
="true"
android:text
="@string/title_news_category_finance" />
</RelativeLayout>

<RelativeLayout
android:layout_width="match_parent"
android:layout_height
="match_parent"
android:layout_weight
="1" >

<TextView
android:id="@+id/tv_title_bar_science"
android:layout_width
="wrap_content"
android:layout_height
="wrap_content"
android:layout_centerInParent
="true"
android:text
="@string/title_news_category_science" />
</RelativeLayout>

<RelativeLayout
android:layout_width="match_parent"
android:layout_height
="match_parent"
android:layout_weight
="1" >

<TextView
android:id="@+id/tv_title_bar_more"
android:layout_width
="wrap_content"
android:layout_height
="wrap_content"
android:layout_centerInParent
="true"
android:text
="@string/title_news_category_more" />
</RelativeLayout>
</LinearLayout>
</RelativeLayout>

</LinearLayout>

对应的Activity代码TabNewsActivity.java

package com.and.netease;

import com.and.netease.utils.MoveBg;

import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.view.Gravity;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.RelativeLayout;
import android.widget.RelativeLayout.LayoutParams;
import android.widget.TextView;

public class TabNewsActivity extends Activity {
RelativeLayout layout;
TextView tv_front;//需要移动的View

TextView tv_bar_news;
TextView tv_bar_sport;
TextView tv_bar_play;
TextView tv_bar_finance;
TextView tv_bar_science;
TextView tv_bar_more;

int avg_width = 0;// 用于记录平均每个标签的宽度,移动的时候需要

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.layout_news);

initViews();
}

private void initViews() {
layout = (RelativeLayout) findViewById(R.id.layout_title_bar);

tv_bar_news = (TextView) findViewById(R.id.tv_title_bar_news);
tv_bar_sport = (TextView) findViewById(R.id.tv_title_bar_sport);
tv_bar_play = (TextView) findViewById(R.id.tv_title_bar_play);
tv_bar_finance = (TextView) findViewById(R.id.tv_title_bar_finance);
tv_bar_science = (TextView) findViewById(R.id.tv_title_bar_science);
tv_bar_more = (TextView) findViewById(R.id.tv_title_bar_more);

tv_bar_news.setOnClickListener(onClickListener);
tv_bar_sport.setOnClickListener(onClickListener);
tv_bar_play.setOnClickListener(onClickListener);
tv_bar_finance.setOnClickListener(onClickListener);
tv_bar_science.setOnClickListener(onClickListener);
tv_bar_more.setOnClickListener(onClickListener);

tv_front = new TextView(this);
tv_front.setBackgroundResource(R.drawable.slidebar);
tv_front.setTextColor(Color.WHITE);
tv_front.setText("头条");
tv_front.setGravity(Gravity.CENTER);
RelativeLayout.LayoutParams param = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
param.addRule(RelativeLayout.CENTER_VERTICAL, RelativeLayout.TRUE);
layout.addView(tv_front, param);

}

private OnClickListener onClickListener = new OnClickListener() {
int startX;//移动的起始位置

@Override
public void onClick(View v) {
avg_width = findViewById(R.id.layout).getWidth();
switch (v.getId()) {
case R.id.tv_title_bar_news:
MoveBg.moveFrontBg(tv_front, startX, 0, 0, 0);
startX = 0;
tv_front.setText(R.string.title_news_category_tops);
break;
case R.id.tv_title_bar_sport:
MoveBg.moveFrontBg(tv_front, startX, avg_width, 0, 0);
startX = avg_width;
tv_front.setText(R.string.title_news_category_sport);
break;
case R.id.tv_title_bar_play:
MoveBg.moveFrontBg(tv_front, startX, avg_width * 2, 0, 0);
startX = avg_width * 2;
tv_front.setText(R.string.title_news_category_play);
break;
case R.id.tv_title_bar_finance:
MoveBg.moveFrontBg(tv_front, startX, avg_width * 3, 0, 0);
startX = avg_width * 3;
tv_front.setText(R.string.title_news_category_finance);
break;
case R.id.tv_title_bar_science:
MoveBg.moveFrontBg(tv_front, startX, avg_width * 4, 0, 0);
startX = avg_width * 4;
tv_front.setText(R.string.title_news_category_science);
break;
case R.id.tv_title_bar_more:
MoveBg.moveFrontBg(tv_front, startX, avg_width * 5, 0, 0);
startX = avg_width * 5;
tv_front.setText(R.string.title_news_category_more);
break;

default:
break;
}

}
};

}

五、总结

通过这种例子,我个人总结有两点需要掌握,一个是TranslateAnimation类的使用,另一个就是布局文件的嵌套使用,经验多了,慢慢就会有感觉了。以上仅代表我个人的一点点想法和总结,还请各位多多指教。(另外附上源代码下载地址





posted @ 2012-03-31 00:40 And.He 阅读(2798) 评论(12) 编辑

2012年3月2日 #

本篇博客重点介绍Android中三种解析XML的方式,包括PULL、SAX、DOM,当然不止这些,还可以用第三方的jar包提供的解析,只是这三种在Android中比较常用吧。再顺便介绍一下AndroidTestCase的用法,用来测试所写的解析业务逻辑是否正确。

本篇博客使用的xml文件如下:

student.xml

 

<?xml version="1.0" encoding="UTF-8"?>  
<students>
<student id="1003">
<name>ZhangSan</name>
<age>23</age>
<score>89</score>
</student>
<student id="1004">
<name>LiSi</name>
<age>24</age>
<score>72</score>
</student>
<student id="1005">
<name>WangWu</name>
<age>25</age>
<score>79</score>
</student>
</students>

各字体属性应该很清楚了,这里不再介绍,也不是重点。

此xml文件放在Android工程下面的assets目录下面,等待解析。。。

再建一个类Student.java

package com.and.xml;

public class Student {

private int id;
private String name;
private int age;
private float score;

public Student() {
super();
}

public Student(int id, String name, int age, float score) {
super();
this.id = id;
this.name = name;
this.age = age;
this.score = score;
}

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

public float getScore() {
return score;
}

public void setScore(float score) {
this.score = score;
}

@Override
public String toString() {
// TODO Auto-generated method stub
return "Id:" + this.id + ",Name:" + this.name + ",Age" + this.age
+ ",Score:" + this.score;
}
}

下面分别介绍三种解析方式。

第一种:PULL解析

PullParseService.java

package com.and.xml;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserFactory;
/**
* PULL解析示例
*
@author Administrator
*
*/
public class PullParseService {
public static List<Student> getStudents(InputStream input) throws Exception {
List<Student> data = null;
Student stu = null;

XmlPullParserFactory fac = XmlPullParserFactory.newInstance();
fac.setNamespaceAware(true);
XmlPullParser parser = fac.newPullParser();
parser.setInput(input, "UTF-8");
int eventType = parser.getEventType();
while (eventType != XmlPullParser.END_DOCUMENT) {
switch (eventType) {
case XmlPullParser.START_DOCUMENT:
System.out.println("START_DOCUMENT");
data = new ArrayList<Student>();
break;
case XmlPullParser.START_TAG:
if ("student".equals(parser.getName())) {
stu = new Student();
stu.setId(Integer.parseInt(parser.getAttributeValue(0)));
}
if (stu != null) {
if ("name".equals(parser.getName())) {
stu.setName(parser.nextText());
} else if ("age".equals(parser.getName())) {
stu.setAge(Integer.parseInt(parser.nextText()));
} else if ("score".equals(parser.getName())) {
stu.setScore(Float.parseFloat(parser.nextText()));
}
}
break;
case XmlPullParser.END_TAG:
if ("student".equals(parser.getName())) {
if (data != null && stu != null) {
data.add(stu);
stu = null;
}
}
break;
}
eventType = parser.next();// 注意:此处勿要写成parser.next();不要理解成指针
}

return data;
}
}

至此,PULL解析的核心业务完成了,怎样来测试有没有问题呢?一般情况下,都是在Activity输出调试日志,根据调试日志判断是否解析成功。这里换一种方式,用Android的测试用例来测试一下。

 

TestParseService.java

 

package com.and.test;

import java.io.InputStream;
import java.util.List;

import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.InputSource;
import org.xml.sax.XMLReader;

import com.and.xml.DomParseService;
import com.and.xml.PullParseService;
import com.and.xml.SaxParserService;
import com.and.xml.Student;

import android.test.AndroidTestCase;
import android.util.Log;

/**
* 测试三种解析方式(Pull、SAX、Dom)
*
*
@author And 2012-02-29
*/
public class TestParseService extends AndroidTestCase {
private static final String TAG = "testService";
InputStream input;
List<Student> students;

public void init() throws Exception {
input = this.getContext().getAssets().open("students.xml");
}

// 测试Pull解析方式
public void testPull() throws Exception {
init();
students = PullParseService.getStudents(input);
for (Student stu : students) {
Log.i(TAG, stu.toString());
}
}

// 测试SAX解析方式
public void testSAX() throws Exception {
init();
SAXParserFactory fac = SAXParserFactory.newInstance();
XMLReader reader = fac.newSAXParser().getXMLReader();
SaxParserService saxHandler = new SaxParserService();
reader.setContentHandler(saxHandler);
reader.parse(new InputSource(input));
students = saxHandler.getParseData();
for (Student stu : students) {
Log.i(TAG, stu.toString());
}
}

// 测试DOM解析方式
public void testDom() throws Exception {
init();
students = DomParseService.getPersonsByParseXml(input);
for (Student stu : students) {
Log.i(TAG, stu.toString());
}
}

}

注意一定要继承自AndroidTestCase这个类。这个文件中写了所有三种解析的测试方法,其它的忽视吧,只看testPull方法,它是用来测试上面所写的PULL解析业务的 。

那么怎样测试呢?

鼠标选中testPull方法名——>右键——>Run As——>Anroid JUnit Test

会提示以下错误:

大概意思就是没有配置running tests.控制台输出:

XmlParseDemo does not specify a android.test.InstrumentationTestRunner instrumentation or does not declare uses-library android.test.runner in its AndroidManifest.xml

从上面的提示信息可知,需要在AndroidManifest.xml中作一些配置,包括instrumentation和uses-library的配置

在AndroidManifest.xml文件中添加如下两行

<instrumentation android:targetPackage="com.and.xml" android:name="android.test.InstrumentationTestRunner"></instrumentation>
<uses-library android:name="android.test.runner"/>

注意添加位置:

第一句跟application节点同级。

第二句跟activity同级。

上面介绍的方法是手动代码添加,下面介绍一种图形化的方式,只需要点击鼠标就可以搞定。

打开AndroidManifest.xml文件

点击Add...

这样,use-library就添加好了

同样的方法添加instrumentation属性

注意Target package后面的内容:com.and.xml包

整个工程目录结构如图:

然后查看一下AndroidManifest.xml的内容,已经包含了刚才添加的那两句了:

 

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package
="com.and.xml"
android:versionCode
="1"
android:versionName
="1.0" >

<uses-sdk android:minSdkVersion="7" />
<instrumentation android:targetPackage="com.and.xml" android:name="android.test.InstrumentationTestRunner"></instrumentation>

<application
android:icon="@drawable/ic_launcher"
android:label
="@string/app_name" >
<activity
android:label="@string/app_name"
android:name
="com.and.xml.MainActivity" >
<intent-filter >
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<uses-library android:name="android.test.runner"/>
</application>

</manifest>

 

OK,然后继续之前的操作“鼠标选中testPull方法名——>右键——>Run As——>Anroid JUnit Test

如果出现类似这样的页面,就表示测试用例建立成功,并且测试方法通过

左上角的绿色条,表示测试方法通过,右下角的调试日志输出,通过判断可以知道解析成功。

第二种:SAX解析

SaxParserService.java

 

package com.and.xml;

import java.util.ArrayList;
import java.util.List;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

/**
* SAX解析示例
*
*
@author Administrator
*
*/
public class SaxParserService extends DefaultHandler {

List<Student> data = null;
Student stu = null;
String tag = "";

@Override
public void characters(char[] ch, int start, int length) throws SAXException {
super.characters(ch, start, length);
if (stu != null) {
String str = new String(ch, start, length);
if (tag.equals("name")) {
stu.setName(str);
} else if (tag.equals("age")) {
stu.setAge(Integer.parseInt(str));
} else if (tag.equals("score")) {
stu.setScore(Float.parseFloat(str));
}
}
}

@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("student") && stu != null) {
data.add(stu);
stu = null;
}
tag = "";
}

@Override
public void startDocument() throws SAXException {
super.startDocument();
data = new ArrayList<Student>();
}

@Override
public void startElement(String uri, String localName, String qName,
Attributes attributes) throws SAXException {
super.startElement(uri, localName, qName, attributes);
tag = localName;
if (localName.equals("student")) {
stu = new Student();
}
if (attributes.getValue(0) != null) {
stu.setId(Integer.parseInt(attributes.getValue(0)));
}
}

public List<Student> getParseData() {
return data;
}

}

 

注意一定要继承自DefaultHandler,然复写里面的方法,这些方法名字根据字面意思很容易理解它的作用。

 

然后通过上面的的测试文件,按照类似的方法测试一下testSAX()方法,如果出现绿条和日志输出的话,表明解析业务逻辑成功。

第三种:DOM解析

DomParseService.java

 

package com.and.xml;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
* DOM解析示例
*/
/**
* DOM解析器在解析XML文档时,会把文档中的所有元素,按照其出现的层次关系,解析成一个个Node对象(节点)。Node对象提供了一系列常量来代表结点的类型
* ,当开发人员获得某个Node类型后,就可以把Node节点转换成相应节点对象(Node的子类对象),以便于调用其特有的方法。
* Node对象提供了相应的方法去获得它的父结点或子结点。编程人员通过这些方法就可以读取整个XML文档的内容、或添加、修改、删除XML文档的内容.
*
* 缺点: 一次性的完全加载整个xml文件,需要消耗大量的内存。
*/
public class DomParseService {

public static List<Student> getPersonsByParseXml(InputStream is) throws Exception {
List<Student> persons = new ArrayList<Student>();
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
try {
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse(is);
Element root = document.getDocumentElement();
NodeList items = root.getElementsByTagName("student");// 得到所有person节点
for (int i = 0; i < items.getLength(); i++) {
Student Student = new Student();
Element personNode = (Element) items.item(i);
Student.setId(new Integer(personNode.getAttribute("id")));
// 获取person节点下的所有子节点(标签之间的空白节点和name/age元素)
NodeList childsNodes = personNode.getChildNodes();
for (int j = 0; j < childsNodes.getLength(); j++) {
Node node = (Node) childsNodes.item(j); // 判断是否为元素类型
if (node.getNodeType() == Node.ELEMENT_NODE) {
Element childNode = (Element) node;
// 判断是否name元素
if ("name".equals(childNode.getNodeName())) {
// 获取name元素下Text节点,然后从Text节点获取数据
Student.setName(childNode.getFirstChild().getNodeValue());
} else if ("age".equals(childNode.getNodeName())) {
Student.setAge(new Short(childNode.getFirstChild().getNodeValue()));
} else if ("score".equals(childNode.getNodeName())) {
Student.setScore(Float.parseFloat(childNode.getFirstChild().getNodeValue()));
}
}
}
persons.add(Student);
}
is.close();
} catch (Exception e) {
e.printStackTrace();
}
return persons;
}
}

 

类似的测试方法。。。

 

至此,三种解析方式全部完成了,如果分别测试这三种方法的时候,一路绿条的话,那么恭喜,解析业务逻辑成功。否则,可能还有哪里有问题,请仔细检查。

对比这三种解析方式,我个人认为PULL和SAX解析方式类似,都是事件触发型的,就是当解析到某个节点的时候触发相应的事件。说明一下DOM解析,会把文档中的所有元素,按照其出现的层次关系,解析成一个个Node对象(节点),可见它会有点占内存,但是如果待解析的xml文件相对较小的话,使用DOM解析 优点还是很明确的。

posted @ 2012-03-02 13:19 And.He 阅读(509) 评论(0) 编辑

2012年3月1日 #

在Android程序中使用广播,有两种注册广播接收器的方式,区别如下:

第一种方式:在AndroidManifest.xml文件中注册

<receiver android:name="MyReceiver" >
<intent-filter >
<action android:name="com.and.android.intent.action.TEST" />
<action android:name="android.net.wifi.WIFI_STATE_CHANGED" />
</intent-filter>
</receiver>

这里注册了自己的广播接收器,可以接收两种类型的广播,第一个是自定义的动作,第二个是系统的,当WIFI状态改变。都是为了测试,随便添加了一个。

这种注册方式是常驻型,当应用程序关闭后(没有卸载前),如果有相应广播信息来,程序也会被系统调用自动运行。 

第二种方式:在程序代码中动态注册

BroadcastReceiver receiver = new MyReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_SCREEN_OFF);//这里添加系统广播,当屏幕关闭的时候发送
filter.addAction(ACTION_INTENT_TEST);
registerReceiver(receiver, filter);

这种注册方式不是常驻型,广播接收器跟随Activity的生命周期。注意在Activity结束前解除注册广播接收器

unregisterReceiver(receiver);

 假设忘记解除注册,系统中也不会保留。即当有广播来,程序也不会处理。

posted @ 2012-03-01 17:20 And.He 阅读(181) 评论(0) 编辑

2012年2月24日 #

摘要: 定义整型数组,例如int a[]={1,2,3,6,7,4,5,2,1}再定义一个整型int sum = 0,sum的值为数组a中所有加起来和为sum的所有元素集合。例如当sum=5时,输出{5},{1,4},{2,3},{2,2,1},{1,1,3}实现函数体(Java或者C语言)void function(int sum){}备注:function函数的形参可以自定义-----------------------割一下-----------------------各位大神,如果有思路,参考一下啊,拜谢。。。阅读全文
posted @ 2012-02-24 10:13 And.He 阅读(335) 评论(2) 编辑

2012年1月6日 #

摘要: 在开发应用程序的过程中我们有很大的机会需要用到参数设置功能,那么在Android应用中,我们如何实现参数设置界面及参数存储呢,下面我们来介绍一下Android中的一个特殊Activity–PreferencesActivity。PreferencesActivity是Android中专门用来实现程序设置界面及参数存储的一个Activity,我们用一个实例来简介如何使用PreferencesActivity。通过查看SDK自带的ApiDemo可以发现,里面有一个类叫PreferenceActivity,里面的第一个例子是通过xml文件来实现的,其它的还没研究(首先通过Eclipse把ApiDem阅读全文
posted @ 2012-01-06 18:57 And.He 阅读(1279) 评论(0) 编辑

2012年1月5日 #

摘要: 本篇博客继上一篇:在Ubuntu下获取Android4.0源代码并编译(一)经过半天加一晚上的下载,今天早上终于下载完了(期间经常下载着就没有速度了,感觉不是网络的问题,可能是服务器不稳定造成的。我的处理办法就是先按ctrl+z中断,然后执行repo sync,就又恢复正常下载了。。。)如图:刚看了一下,有7G。接下来就准备编译吧。。。敬请期待阅读全文
posted @ 2012-01-05 09:56 And.He 阅读(481) 评论(0) 编辑

2012年1月4日 #

摘要: 搞了几个月的Android应用开发,勉强算是个Android开发者了吧,Android本就是开源的,还是把源代码下载下来自己编译一下,看看是个什么东西,出于好奇,和以后的职业发展,开始了无休止的Android研究过程。。。这篇连载博客用来记录我从下载源代码到编译的整个过程,开始百度和谷歌了无数资料,遇到了不少的问题,以总结出来,希望对后来者有所帮助,于我也算是一种总结吧接下来首先开发获取最新的Android源代码,目前最新版本是4.0要获取源代码,当然还得先看官方文档怎么说http://source.android.com/source/initializing.html第一步:初始化编译环境阅读全文
posted @ 2012-01-04 14:52 And.He 阅读(3637) 评论(10) 编辑

2011年10月24日 #

摘要: 在Android项目开发中,经常会用到Toast这个控件,但是系统的默认式样太难看,有时需要改变一下,比如背景图片,还有上面的提示文字,有的时候还需要动态改变提示的文字,比如颜色之类的还有一个问题,在一个TextView上面,怎样让它显示的内容有不同的颜色,比如“今天天气好吗?挺好的”,如果想让“今天今天好吗?”这一句显示红色,“挺好的”这三个字显示绿色呢?下面就两个问题一并做解答,有图有真相接下来看代码:先是主布局文件main.xml<?xml version="1.0" encoding="utf-8"?><LinearLayout阅读全文
posted @ 2011-10-24 18:08 And.He 阅读(1101) 评论(2) 编辑

仅列出标题  下一页