Android应用之《宋词三百首》(一)
时间:2014-04-29 13:15:20
收藏:0
阅读:536
今天我们通过一个实际的案例来综合运用一下Android技术中各方面的知识,模仿《宋词三百首》写一个应用,代码里面所有的资源均来自互联网,仅用于学习,请勿作商业用途。
(1)第一步新建Android工程,修改应用图标,将72x72的app icon拷贝到drawable-hdpi文件夹下,将96x96的app icon拷贝到drawable-xhdpi文件夹下,然后修改AndroidManifest.xml文件里的内容如下:
<application android:icon="@drawable/icon"
然后修改strings.xml的内容如下:
<string name="app_name">宋词三百首</string> <string name="title_activity_main">宋词三百首</string>
修改应用名称,将AndroidManifest.xml文件中的内容修改如下:
android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name=".ui.SplashActivity" android:label="@string/title_activity_main" >
经过以上的工作,App的图标和名称都已经修改OK;
(2)下面我们来写第一个界面:欢迎界面
首先将背景图片welcome.jpg拷贝到drawable-hdpi下面,然后在layout文件夹下面新建一个activity_splash.xml,内容如下:
<?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>
然后在src下面新建一个SplashActivity.java文件,代码已经详细注释,内容如下:
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); } }
运行效果如下图:
(2)接着我们写第二个界面,在写第二个界面之前我们还需要做一些准备工作,就是准备数据,所有的数据都存储在songci.xml这样一个文件中,在这里我截取其一点片段如下:
<?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>
我们将songci.xml放在assets文件夹下面,因为xml文件有点大,在打包成apk文件的时候会被压缩,造成读取的时候产生IOException,关于这个问题的更多详细请参考IOEXception while reading from inputstream,所以我们将songci.xml文件的改名为songci.mp3,以避免这样的问题。
其次就是几个知识点的预备工作(如果你已经熟悉这些知识,请跳过):
那么下面我们开始对XML数据进行解析和封装:
首先创建一个接口ISongCiParser,其内容如下:
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; }
这里插入一点写代码时候遇到的问题:因为之前想用SAX解析器去解析XML,但是做到一半的时候发现有问题,就是<desc></desc>之间的内容包含了很多<p></p><strong></strong><br></br>这样的标签对,SAX解析的时候把里面的内容都当作element进行了分割获取值,但是我想要的是<desc></desc>之间的所有内容作为一个值,所以用SAX做到一半的时候就果断改用PULL解析器来解析,解析得很顺利,没有出现问题。继续......
接着我们写一个PULL解析实现类SongCiParserImpl,其内容如下:
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(); } } }
这一步写好了,我们就可以在Activity里面去直接使用了,在MainActivity当中我已经把SAX的部分注释掉了,其他的代码也做了详细的注释,内容如下:
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); } }); } }
因为这里面有一个自定义的Adapter,所以这里给出MainListViewAdapter的定义,方便大家阅读:(这个类我没有加注释,如果读者感觉阅读困难,建议先看一下这篇文章自定义ListView)
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; } }
附上一张MainActivity的界面截图:
更多内容,下回分解......
评论(0)