您的位置:首页 > 移动开发 > Android开发

Android应用之《宋词三百首》(一)

2014-04-29 11:28 190 查看
今天我们通过一个实际的案例来综合运用一下Android技术中各方面的知识,模仿《宋词三百首》写一个应用,代码里面所有的资源均来自互联网,仅用于学习,请勿作商业用途。

(1)第一步新建Android工程,修改应用图标,将72x72的app icon拷贝到drawable-hdpi文件夹下,将96x96的app icon拷贝到drawable-xhdpi文件夹下,然后修改AndroidManifest.xml文件里的内容如下:

<span style="font-family:Comic Sans MS;font-size:18px;"><application
android:icon="@drawable/icon"</span>


然后修改strings.xml的内容如下:

<span style="font-family:Comic Sans MS;font-size:18px;"><string name="app_name">宋词三百首</string>
<string name="title_activity_main">宋词三百首</string></span>


修改应用名称,将AndroidManifest.xml文件中的内容修改如下:

<span style="font-family:Comic Sans MS;font-size:18px;">android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name=".ui.SplashActivity"
android:label="@string/title_activity_main" ></span>


经过以上的工作,App的图标和名称都已经修改OK;

(2)下面我们来写第一个界面:欢迎界面

首先将背景图片welcome.jpg拷贝到drawable-hdpi下面,然后在layout文件夹下面新建一个activity_splash.xml,内容如下:

<span style="font-family:Comic Sans MS;font-size:18px;"><?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="@drawable/welcome">

</LinearLayout></span>


然后在src下面新建一个SplashActivity.java文件,代码已经详细注释,内容如下:

<span style="font-family:Comic Sans MS;font-size:18px;">package com.example.songcidemo.ui;

import com.example.songcidemo.R;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.view.Window;

/**
*	App欢迎界面
*/
public class SplashActivity extends Activity {

/**
* 启动时最先执行的回调方法
*/
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//设置界面没有标题栏
requestWindowFeature(Window.FEATURE_NO_TITLE);
//指定界面的布局文件
setContentView(R.layout.activity_splash);

//初始化一个Handler
Handler handler = new Handler();

//Runnable是一个线程,在1500毫秒以后执行线程对象
handler.postDelayed(new Runnable() {

@Override
public void run() {
//从SplashActivity跳转到MainActivity
Intent intent = new Intent(SplashActivity.this, MainActivity.class);
startActivity(intent);
//在后台关闭掉SplashActivity
SplashActivity.this.finish();
}
},  1500);
}

}
</span>


运行效果如下图:



(2)接着我们写第二个界面,在写第二个界面之前我们还需要做一些准备工作,就是准备数据,所有的数据都存储在songci.xml这样一个文件中,在这里我截取其一点片段如下:

<span style="font-family:Comic Sans MS;font-size:18px;"><?xml version="1.0" encoding="UTF-8" standalone="no"?>
<root>
<node>
<title><![CDATA[洞仙歌·泗州中秋作]]></title>
<auth><![CDATA[晁补之]]></auth>
<desc><![CDATA[<p>  洞仙歌·泗州①中秋作 </p>
<p>  <strong>晁补之</strong> </p>
<p>  青烟幂②处,碧海飞金镜。永夜闲阶卧桂影。 </p>
<p>  露凉时、零乱多少寒螀③,神京④远,惟有蓝桥⑤路近。 </p>
<p>  水晶帘不下,云母屏⑥开,冷浸佳人⑦淡脂粉。 </p>
<p>  待都将许多明,付与金尊,投晓共、流霞⑧倾尽。 </p>
<p>  更携取、胡床⑨上南楼,看玉做人间,素秋千倾。</p>
<p><br />【注释】<br />  ①泗州:安徽泗县。 </p>
<p>  ②幂(mì):遮盖。 </p>
<p>  ③寒螀(jiāng):寒蝉。 </p>
<p>  ④ 神京:指北宋京城汴梁。 </p>
<p>  ⑤蓝桥:在陕西蓝田县东南,桥架蓝水之上,故名。世传其地有仙窟,唐裴航遇云英于此桥。 </p>
<p>  ⑥ 云母屏:云母为花岗岩主要成分,可作屏风,艳丽光泽。 </p>
<p>  ⑦佳人:这里指席间的女性 </p>
<p>  ⑧流霞:仙酒名。语意双关,既指酒,也指朝霞 </p>
<p>  ⑨胡床:古代一种轻便坐具,可以折叠。</p>
<p>【译文】<br />  青色的烟云,遮住了月影,从碧海般的晴空里飞出一轮金灿灿的明镜。长夜的空阶上卧着挂树的斜影。夜露渐凉之是时,多少秋蝉零乱地嗓鸣思念京都路远,论路近唯有月宫仙境,高卷水晶帘儿,展开云母屏风,美人的淡淡脂粉浸润了夜月的清冷。待我许多月色澄辉,倾入金樽,直到拂晓连同流霞全都倾尽。再携带一张胡床登上南楼,看白玉铺成的人间,领略素白澄洁的千顷清秋。</p>
]]></desc>
</node></span>


我们将songci.xml放在assets文件夹下面,因为xml文件有点大,在打包成apk文件的时候会被压缩,造成读取的时候产生IOException,关于这个问题的更多详细请参考IOEXception while reading from inputstream,所以我们将songci.xml文件的改名为songci.mp3,以避免这样的问题。

其次就是几个知识点的预备工作(如果你已经熟悉这些知识,请跳过):

SAX解析XML

PULL解析XML

那么下面我们开始对XML数据进行解析和封装:

首先创建一个接口ISongCiParser,其内容如下:

<span style="font-family:Comic Sans MS;font-size:18px;">package com.example.songcidemo.data;

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

import com.example.songcidemo.bean.SongCi;

public interface ISongCiParser {

/**
* 解析xml输入流
*
* @param is	输入流
* @param scList	装载容器
* @throws Exception
*/
public void parse(InputStream is,List<SongCi> scList) throws Exception;

}
</span>


这里插入一点写代码时候遇到的问题:因为之前想用SAX解析器去解析XML,但是做到一半的时候发现有问题,就是<desc></desc>之间的内容包含了很多<p></p><strong></strong><br></br>这样的标签对,SAX解析的时候把里面的内容都当作element进行了分割获取值,但是我想要的是<desc></desc>之间的所有内容作为一个值,所以用SAX做到一半的时候就果断改用PULL解析器来解析,解析得很顺利,没有出现问题。继续......

接着我们写一个PULL解析实现类SongCiParserImpl,其内容如下:

<span style="font-family:Comic Sans MS;font-size:18px;">package com.example.songcidemo.data;

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

import org.xmlpull.v1.XmlPullParser;

import android.util.Xml;

import com.example.songcidemo.bean.SongCi;

public class SongCiParserImpl implements ISongCiParser{

//定义XML文件标签常量,常量值与XML文件内的标签名一致
private static final String TAG_NODE = "node";
private static final String TAG_TITLE = "title";
private static final String TAG_AUTH = "auth";
private static final String TAG_DESC = "desc";

/**
* 解析xml文件的方法
*
* is	输入流
* scList	装载数据解析完后并封装成SongCi的链表
*/
@Override
public void parse(InputStream is, List<SongCi> scList) throws Exception {
SongCi sc = null;
if(scList != null){
scList.clear();
}

//获取XmlPullParser实例
XmlPullParser xpp = Xml.newPullParser();
//为XmlPullParser实例设置输入流,并设置输入流的字符集是utf-8
xpp.setInput(is,"utf-8");

//获取当前事件的类型,比如START_TAG,END_TAG,TEXT等等
int eventType = xpp.getEventType();

//如果当前时间的类型不是文件结束的时候执行循环
while(eventType != XmlPullParser.END_DOCUMENT){
switch (eventType) {
case XmlPullParser.START_DOCUMENT:
//do nothing
break;
//如果当前的事件类型是开始元素
case XmlPullParser.START_TAG:

if(xpp.getName().equals(TAG_NODE)){
//如果遇到<node>就新建一个SongCi对象
sc = new SongCi();
}else if(xpp.getName().equals(TAG_TITLE)){
//如果遇到<title>就将<title>后面的text传递给sc
sc.setTitle(xpp.nextText());
}else if(xpp.getName().equals(TAG_AUTH)){
//如果遇到<auth>就将<auth>后面的text传递给sc
sc.setAuth(xpp.nextText());
}else if(xpp.getName().equals(TAG_DESC)){
//如果遇到<desc>就将<desc>后面的text传递给sc
sc.setDesc(xpp.nextText());
}
break;
case XmlPullParser.END_TAG:
if(xpp.getName().equals(TAG_NODE)){
//如果遇到</node>就将sc所关联的对象加入到链表中
scList.add(sc);
sc = null;
}
break;

default:
break;
}
//进入下一个元素并触发相应的事件
eventType = xpp.next();
}
}

}
</span>


这一步写好了,我们就可以在Activity里面去直接使用了,在MainActivity当中我已经把SAX的部分注释掉了,其他的代码也做了详细的注释,内容如下:

<span style="font-family:Comic Sans MS;font-size:18px;">package com.example.songcidemo.ui;

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

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

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

import android.app.Activity;
import android.content.Intent;
import android.content.res.AssetManager;
import android.os.Bundle;
import android.view.View;
import android.view.Window;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ListView;

import com.example.songcidemo.R;
import com.example.songcidemo.bean.SongCi;
import com.example.songcidemo.data.MainListViewAdapter;
import com.example.songcidemo.data.SongCiParserImpl;
import com.example.songcidemo.data.SongCiSaxHandler;
import com.example.songcidemo.util.Global;

public class MainActivity extends Activity {

//声明装载SongCi类型的链表
private ArrayList<SongCi> scList;

//声明了一个ListView变量
private ListView mListView;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main);
initData();
//        saxParseXML();
pullParseXML();
setupViews();

}

private void initData(){
//初始化scList
scList = new ArrayList<SongCi>();
}

/**
* 用SAX解析器解析XML文件
*/
private void saxParseXML(){
try {
//获取一个AssetManager对象
AssetManager assetManager = this.getAssets();
//通过assetManager的open方法获取到songci.mp3的输入流
InputStream inputStream = assetManager.open("songci.mp3");
//将inputstream的内容封装成InputSource
InputSource inputSource = new InputSource(inputStream);
//获取SAXParserFactory实例
SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
//获取SAXParser对象
SAXParser saxParser = saxParserFactory.newSAXParser();
//获取XMLReader对象
XMLReader xmlReader = saxParser.getXMLReader();
//初始化scSaxHandler
SongCiSaxHandler scSaxHandler = new SongCiSaxHandler(scList);
//将scSaxHandler传递给xmlReader
xmlReader.setContentHandler(scSaxHandler);
//开始解析xml文件
xmlReader.parse(inputSource);

//关闭流
inputStream.close();

} catch (Exception e) {
e.printStackTrace();
}

}

/**
* 用PULL方式解析XML文件
*/
private void pullParseXML(){
try {
InputStream is = this.getAssets().open("songci.mp3");
SongCiParserImpl scpi = new SongCiParserImpl();
scpi.parse(is, scList);
} catch (Exception e) {
e.printStackTrace();
}
}

/**
* 初始化视图
*/
private void setupViews(){
mListView = (ListView) findViewById(R.id.lv_catelog);

//初始化自定义类型MainListViewAdapter的实例adapter,将scList传递给adapter的构造器
MainListViewAdapter adapter = new MainListViewAdapter(this, scList);

//将adapter传递给mListView
mListView.setAdapter(adapter);

mListView.setOnItemClickListener(new OnItemClickListener() {

@Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
Global.currentSongCi = scList.get(position);
Intent intent = new Intent(MainActivity.this, ContentActivity.class);
startActivity(intent);
}
});
}

}
</span>


因为这里面有一个自定义的Adapter,所以这里给出MainListViewAdapter的定义,方便大家阅读:(这个类我没有加注释,如果读者感觉阅读困难,建议先看一下这篇文章自定义ListView)

<span style="font-family:Comic Sans MS;font-size:18px;">package com.example.songcidemo.data;

import java.util.ArrayList;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;

import com.example.songcidemo.R;
import com.example.songcidemo.bean.SongCi;

public class MainListViewAdapter extends BaseAdapter{

private ArrayList<SongCi> scList;
private Context context;

public MainListViewAdapter(Context context, ArrayList<SongCi> scList){
this.context = context;
this.scList = scList;
}

@Override
public int getCount() {
return scList.size();
}

@Override
public Object getItem(int position) {
return scList.get(position);
}

@Override
public long getItemId(int position) {
return position;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
ListViewItemHolder holder;
if(convertView == null){
LayoutInflater inflater = LayoutInflater.from(context);
convertView = inflater.inflate(R.layout.list_item, null);
holder = new ListViewItemHolder();
holder.titleTextView = (TextView) convertView.findViewById(R.id.tv_title);
holder.authTextView = (TextView) convertView.findViewById(R.id.tv_auth);

convertView.setTag(holder);
}else{
holder = (ListViewItemHolder) convertView.getTag();
}

SongCi sc = scList.get(position);
String title = sc.getTitle();
String auth = sc.getAuth();
holder.titleTextView.setText(title);
holder.authTextView.setText(auth);
return convertView;
}

private class ListViewItemHolder{
TextView titleTextView;
TextView authTextView;
}

}
</span>


附上一张MainActivity的界面截图:



更多内容,下回分解......
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: