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

来源:互联网 发布:transmit mac 破解 编辑:程序博客网 时间:2024/04/29 00:49
今天我们通过一个实际的案例来综合运用一下Android技术中各方面的知识,模仿《宋词三百首》写一个应用,代码里面所有的资源均来自互联网,仅用于学习,请勿作商业用途。

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

[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. <application  
  2.         android:icon="@drawable/icon"  

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

[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. <string name="app_name">宋词三百首</string>  
  2.     <string name="title_activity_main">宋词三百首</string>  

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

[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. android:label="@string/app_name"  
  2.         android:theme="@style/AppTheme" >  
  3.         <activity  
  4.             android:name=".ui.SplashActivity"  
  5.             android:label="@string/title_activity_main" >  

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

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

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

[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:layout_width="match_parent"  
  4.     android:layout_height="match_parent"  
  5.     android:orientation="vertical"   
  6.     android:background="@drawable/welcome">  
  7.       
  8. </LinearLayout>  

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

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. package com.example.songcidemo.ui;  
  2.   
  3. import com.example.songcidemo.R;  
  4.   
  5. import android.app.Activity;  
  6. import android.content.Intent;  
  7. import android.os.Bundle;  
  8. import android.os.Handler;  
  9. import android.view.Window;  
  10.   
  11. /** 
  12.  *  App欢迎界面 
  13.  */  
  14. public class SplashActivity extends Activity {  
  15.       
  16.     /** 
  17.      * 启动时最先执行的回调方法 
  18.      */  
  19.     @Override  
  20.     protected void onCreate(Bundle savedInstanceState) {  
  21.         super.onCreate(savedInstanceState);  
  22.         //设置界面没有标题栏  
  23.         requestWindowFeature(Window.FEATURE_NO_TITLE);  
  24.         //指定界面的布局文件  
  25.         setContentView(R.layout.activity_splash);  
  26.           
  27.         //初始化一个Handler  
  28.         Handler handler = new Handler();  
  29.           
  30.         //Runnable是一个线程,在1500毫秒以后执行线程对象  
  31.         handler.postDelayed(new Runnable() {  
  32.               
  33.             @Override  
  34.             public void run() {  
  35.                 //从SplashActivity跳转到MainActivity  
  36.                 Intent intent = new Intent(SplashActivity.this, MainActivity.class);  
  37.                 startActivity(intent);  
  38.                 //在后台关闭掉SplashActivity  
  39.                 SplashActivity.this.finish();  
  40.             }  
  41.         },  1500);  
  42.     }  
  43.   
  44. }  

运行效果如下图:


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

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

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


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

SAX解析XML

PULL解析XML

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

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

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. package com.example.songcidemo.data;  
  2.   
  3. import java.io.InputStream;  
  4. import java.util.List;  
  5.   
  6. import com.example.songcidemo.bean.SongCi;  
  7.   
  8. public interface ISongCiParser {  
  9.       
  10.     /** 
  11.      * 解析xml输入流 
  12.      *  
  13.      * @param is    输入流 
  14.      * @param scList    装载容器 
  15.      * @throws Exception     
  16.      */  
  17.     public void parse(InputStream is,List<SongCi> scList) throws Exception;  
  18.       
  19. }  

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

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

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. package com.example.songcidemo.data;  
  2.   
  3. import java.io.InputStream;  
  4. import java.util.List;  
  5.   
  6. import org.xmlpull.v1.XmlPullParser;  
  7.   
  8. import android.util.Xml;  
  9.   
  10. import com.example.songcidemo.bean.SongCi;  
  11.   
  12. public class SongCiParserImpl implements ISongCiParser{  
  13.       
  14.     //定义XML文件标签常量,常量值与XML文件内的标签名一致  
  15.     private static final String TAG_NODE = "node";  
  16.     private static final String TAG_TITLE = "title";  
  17.     private static final String TAG_AUTH = "auth";  
  18.     private static final String TAG_DESC = "desc";  
  19.       
  20.     /** 
  21.      * 解析xml文件的方法 
  22.      *  
  23.      * is   输入流 
  24.      * scList   装载数据解析完后并封装成SongCi的链表 
  25.      */  
  26.     @Override  
  27.     public void parse(InputStream is, List<SongCi> scList) throws Exception {  
  28.         SongCi sc = null;  
  29.         if(scList != null){  
  30.             scList.clear();  
  31.         }  
  32.           
  33.         //获取XmlPullParser实例  
  34.         XmlPullParser xpp = Xml.newPullParser();  
  35.         //为XmlPullParser实例设置输入流,并设置输入流的字符集是utf-8  
  36.         xpp.setInput(is,"utf-8");  
  37.           
  38.         //获取当前事件的类型,比如START_TAG,END_TAG,TEXT等等  
  39.         int eventType = xpp.getEventType();  
  40.           
  41.         //如果当前时间的类型不是文件结束的时候执行循环  
  42.         while(eventType != XmlPullParser.END_DOCUMENT){  
  43.             switch (eventType) {  
  44.             case XmlPullParser.START_DOCUMENT:  
  45.                 //do nothing  
  46.                 break;  
  47.                 //如果当前的事件类型是开始元素  
  48.             case XmlPullParser.START_TAG:  
  49.                   
  50.                 if(xpp.getName().equals(TAG_NODE)){  
  51.                     //如果遇到<node>就新建一个SongCi对象  
  52.                     sc = new SongCi();  
  53.                 }else if(xpp.getName().equals(TAG_TITLE)){  
  54.                     //如果遇到<title>就将<title>后面的text传递给sc  
  55.                     sc.setTitle(xpp.nextText());  
  56.                 }else if(xpp.getName().equals(TAG_AUTH)){  
  57.                     //如果遇到<auth>就将<auth>后面的text传递给sc  
  58.                     sc.setAuth(xpp.nextText());  
  59.                 }else if(xpp.getName().equals(TAG_DESC)){  
  60.                     //如果遇到<desc>就将<desc>后面的text传递给sc  
  61.                     sc.setDesc(xpp.nextText());  
  62.                 }  
  63.                 break;  
  64.             case XmlPullParser.END_TAG:  
  65.                 if(xpp.getName().equals(TAG_NODE)){  
  66.                     //如果遇到</node>就将sc所关联的对象加入到链表中  
  67.                     scList.add(sc);  
  68.                     sc = null;  
  69.                 }  
  70.                 break;  
  71.   
  72.             default:  
  73.                 break;  
  74.             }  
  75.             //进入下一个元素并触发相应的事件  
  76.             eventType = xpp.next();  
  77.         }  
  78.     }  
  79.   
  80. }  

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

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. package com.example.songcidemo.ui;  
  2.   
  3. import java.io.InputStream;  
  4. import java.util.ArrayList;  
  5.   
  6. import javax.xml.parsers.SAXParser;  
  7. import javax.xml.parsers.SAXParserFactory;  
  8.   
  9. import org.xml.sax.InputSource;  
  10. import org.xml.sax.XMLReader;  
  11.   
  12. import android.app.Activity;  
  13. import android.content.Intent;  
  14. import android.content.res.AssetManager;  
  15. import android.os.Bundle;  
  16. import android.view.View;  
  17. import android.view.Window;  
  18. import android.widget.AdapterView;  
  19. import android.widget.AdapterView.OnItemClickListener;  
  20. import android.widget.ListView;  
  21.   
  22. import com.example.songcidemo.R;  
  23. import com.example.songcidemo.bean.SongCi;  
  24. import com.example.songcidemo.data.MainListViewAdapter;  
  25. import com.example.songcidemo.data.SongCiParserImpl;  
  26. import com.example.songcidemo.data.SongCiSaxHandler;  
  27. import com.example.songcidemo.util.Global;  
  28.   
  29. public class MainActivity extends Activity {  
  30.       
  31.     //声明装载SongCi类型的链表  
  32.     private ArrayList<SongCi> scList;  
  33.       
  34.     //声明了一个ListView变量  
  35.     private ListView mListView;  
  36.   
  37.     @Override  
  38.     public void onCreate(Bundle savedInstanceState) {  
  39.         super.onCreate(savedInstanceState);  
  40.         requestWindowFeature(Window.FEATURE_NO_TITLE);  
  41.         setContentView(R.layout.activity_main);  
  42.         initData();  
  43. //        saxParseXML();  
  44.         pullParseXML();  
  45.         setupViews();  
  46.           
  47.     }  
  48.       
  49.     private void initData(){  
  50.         //初始化scList  
  51.         scList = new ArrayList<SongCi>();  
  52.     }  
  53.       
  54.     /** 
  55.      * 用SAX解析器解析XML文件 
  56.      */  
  57.     private void saxParseXML(){  
  58.         try {  
  59.             //获取一个AssetManager对象  
  60.             AssetManager assetManager = this.getAssets();  
  61.             //通过assetManager的open方法获取到songci.mp3的输入流  
  62.             InputStream inputStream = assetManager.open("songci.mp3");  
  63.             //将inputstream的内容封装成InputSource  
  64.             InputSource inputSource = new InputSource(inputStream);  
  65.             //获取SAXParserFactory实例  
  66.             SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();  
  67.             //获取SAXParser对象  
  68.             SAXParser saxParser = saxParserFactory.newSAXParser();  
  69.             //获取XMLReader对象  
  70.             XMLReader xmlReader = saxParser.getXMLReader();  
  71.             //初始化scSaxHandler  
  72.             SongCiSaxHandler scSaxHandler = new SongCiSaxHandler(scList);  
  73.             //将scSaxHandler传递给xmlReader  
  74.             xmlReader.setContentHandler(scSaxHandler);  
  75.             //开始解析xml文件  
  76.             xmlReader.parse(inputSource);  
  77.               
  78.             //关闭流  
  79.             inputStream.close();  
  80.               
  81.               
  82.         } catch (Exception e) {  
  83.             e.printStackTrace();  
  84.         }  
  85.           
  86.     }  
  87.       
  88.     /** 
  89.      * 用PULL方式解析XML文件 
  90.      */  
  91.     private void pullParseXML(){  
  92.         try {  
  93.             InputStream is = this.getAssets().open("songci.mp3");  
  94.             SongCiParserImpl scpi = new SongCiParserImpl();  
  95.             scpi.parse(is, scList);  
  96.         } catch (Exception e) {  
  97.             e.printStackTrace();  
  98.         }  
  99.     }  
  100.       
  101.     /** 
  102.      * 初始化视图 
  103.      */  
  104.     private void setupViews(){  
  105.         mListView = (ListView) findViewById(R.id.lv_catelog);  
  106.           
  107.         //初始化自定义类型MainListViewAdapter的实例adapter,将scList传递给adapter的构造器  
  108.         MainListViewAdapter adapter = new MainListViewAdapter(this, scList);  
  109.           
  110.         //将adapter传递给mListView  
  111.         mListView.setAdapter(adapter);  
  112.           
  113.         mListView.setOnItemClickListener(new OnItemClickListener() {  
  114.   
  115.             @Override  
  116.             public void onItemClick(AdapterView<?> parent, View view,  
  117.                     int position, long id) {  
  118.                 Global.currentSongCi = scList.get(position);  
  119.                 Intent intent = new Intent(MainActivity.this, ContentActivity.class);  
  120.                 startActivity(intent);  
  121.             }  
  122.         });  
  123.     }  
  124.   
  125. }  

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

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. package com.example.songcidemo.data;  
  2.   
  3. import java.util.ArrayList;  
  4.   
  5. import android.content.Context;  
  6. import android.view.LayoutInflater;  
  7. import android.view.View;  
  8. import android.view.ViewGroup;  
  9. import android.widget.BaseAdapter;  
  10. import android.widget.TextView;  
  11.   
  12. import com.example.songcidemo.R;  
  13. import com.example.songcidemo.bean.SongCi;  
  14.   
  15. public class MainListViewAdapter extends BaseAdapter{  
  16.   
  17.     private ArrayList<SongCi> scList;  
  18.     private Context context;  
  19.       
  20.     public MainListViewAdapter(Context context, ArrayList<SongCi> scList){  
  21.         this.context = context;  
  22.         this.scList = scList;  
  23.     }  
  24.       
  25.     @Override  
  26.     public int getCount() {  
  27.         return scList.size();  
  28.     }  
  29.   
  30.     @Override  
  31.     public Object getItem(int position) {  
  32.         return scList.get(position);  
  33.     }  
  34.   
  35.     @Override  
  36.     public long getItemId(int position) {  
  37.         return position;  
  38.     }  
  39.   
  40.     @Override  
  41.     public View getView(int position, View convertView, ViewGroup parent) {  
  42.         ListViewItemHolder holder;  
  43.         if(convertView == null){  
  44.             LayoutInflater inflater = LayoutInflater.from(context);  
  45.             convertView = inflater.inflate(R.layout.list_item, null);  
  46.             holder = new ListViewItemHolder();  
  47.             holder.titleTextView = (TextView) convertView.findViewById(R.id.tv_title);  
  48.             holder.authTextView = (TextView) convertView.findViewById(R.id.tv_auth);  
  49.               
  50.             convertView.setTag(holder);  
  51.         }else{  
  52.             holder = (ListViewItemHolder) convertView.getTag();  
  53.         }  
  54.           
  55.         SongCi sc = scList.get(position);  
  56.         String title = sc.getTitle();  
  57.         String auth = sc.getAuth();  
  58.         holder.titleTextView.setText(title);  
  59.         holder.authTextView.setText(auth);  
  60.         return convertView;  
  61.     }  
  62.       
  63.     private class ListViewItemHolder{  
  64.         TextView titleTextView;  
  65.         TextView authTextView;  
  66.     }  
  67.   
  68. }  

附上一张MainActivity的界面截图:


更多内容,下回分解......

0 0