我们关于 android 的学习已经基本学习到了一阶段了。
晚上喝了一点酒,现在还不是很有精力来写。本来是想写一点学习的心得的。
虽然也只有几十集的内容,但知识点还是很多的,这些琐屑的知识点要梳理起来还是很麻烦的。所以在这里我先把自己最后做的一个工程来简单的总结一下。
我们最后完成的一个工程是一个关于 mp3 的一个工程。在这里我们实现了关于在服务器上对文件的下载,还有对于 xml 文件的解析。当然还有对于 mp3 文件的播放功能。由于对于歌词文件,我们在这里没有编辑器打开,所以我们在这里暂时没有办法进行对歌词文件的同步实行。当然在这部分涉及到了很多的关心 java 基础知识的内容。所以这方面对于 android 的到是没有什么多大的影响。对于这个工程,当然最首先的我们要是先介绍一下我们的 android , android 的技术是一款基于手机开发的一项技术。对于这里面最重要的就是 activity. 当然每一个 activity 都对就一个 layout 文件夹下面的布局文件。我们这个 activity 就可以利用这个布局文件在手机屏幕上进行布局。显示相应的控件。
如上图我们把在 eclispe 中自动生成的一些文件结构打开给大家看。对 src 这个文件夹就不用多说了,这里面都是一些源文件。而 res 下面则是一些对应的资源文件。这其中有 drawable, 之所以分成三个文件夹,这是因为对于不同的手机,他的分辨率是不同的,在这三个里面我们会定义分辨率不同的三个图片,那么 android 系统会自动根据你手机的分辨率来自动的调用相应的图片对象。从而最大化发挥我们手机的显示效果。而 layout ,顾名思义这是一个布局文件夹,这里面是对你的 activity 的布局,再明显一点说的话,是你手机上该显示什么,都会在这个文件夹的文件中进行设置或编写。然后就是一个 values 这个文件夹了,这里面都是一个 string 对象或者一些进行数据设置的一些文件,这个文件夹的设置很好的解决了我们所写的 android 程序的国际化的实现功能。还有一个单独的文件就是 AndroidManifest.xml 这个文件,这在每一个所谓的 android 项目中我们都会有一个文件。然而这个文件的功能是什么呢。这是对于我们整个 android 的项目的配置文件。在这里我们可以设置 activity,service, 甚至一些操作的权限。当然这以上我们说的只是 android 的一些简单的用法也是入门的必须的知道。
好了,我们还是开始我的 Mp3 程序的介绍下吧,我们一开始要实现的功能是什么呢?是对于我们服务器上的 xml 文件进行解析,因为我们的所有的一些 Mp3 和对应的歌词文件都是通过 xml 文件存储的。所以我们需要对 xml 进行解析,然后这个 xml 文件是在服务器上的,好我们就要对这个文件进行下载,然后下来我们也要进行解析,那我们不如直接得到这个文件然后进行解析了。对于这个 xml 文件的下载我们在一个 HttpDownload.java 文件中实现了。那么在对于一个文本文件的字符的得到我们应该如何来做?我想我在这里有必要进行一定的说明吧。对于一个网络上或者是服务器上的一个文件,首先我们有已知条件肯定有他的 url 地址。我们可能通过他的字符串对象得到一个 URL 对象。然后我们可能打开这个 URL 地址我们可以得到 HttpURLConnection 这个对象。在有了这个对象后我们就能通它得到这个地址中的那个流对象了,然后通过对于这个流的对象的适当包装,然后我们可以简单的进行操作这个流对象了。然后得到这个文本文件的字条串对象应该是不难的。那么接下来我们就是对这个字符串对象进行 xml 解析了。对于 xml 文件的解析,我们也有点想要说的。
SAXParserFactory saxparsefactory=SAXParserFactory.newInstance ();
XMLReader xmlreader=saxparsefactory.newSAXParser().getXMLReader();
Mp3ListContentHandler mp3listcontenthandler= new Mp3ListContentHandler(infos);
xmlreader.setContentHandler(mp3listcontenthandler);
xmlreader.parse( new InputSource( new StringReader(xml)));
看上面的代码,我们可以知道对于这个 xml 解析应该如何去做。通过一个 android 里一个工厂的对象,得到他的实例。得到 SAXParserFactory 对象,然后可能得到 XMLReader 对象,然后我们就可以设置他的 handler 了,然后实现解析了。所以在这里我们可以了解到对于 xml 解析的真正工作还是存在于 handler 之中的。在我们的真正工作的那个 handler 可以继承 DefaultHandler ,然后实现相应的处理方法。
下一步就是,对于 mp3, 歌词文件的下载了,这是指下载到本地文件中去,对了在上面中我们为了能够进行 internet 的权限在配置文件中应该声明这个权限:
< uses-permission android:name = "android.permission.INTERNET" ></ uses-permission >
当然如果要把下载的文件写入到我们手机的 sdcard 中去,我们也要有访问这个 sdcard 的权限,他们是:
< uses-permission android:name = "android.permission.WRITE_EXTERNAL_STORAGE" ></ uses-permission >
对于文件的下载这本也不是 android 的知识,但这里也有必要说:
我们是采用的 service 来实现下载的,实现这个我们需要 intent 对象。代码如下:
Intent intent= new Intent(Mp3ListActivity. this ,DownLoadService. class );
intent.putExtra( "mp3Info" , mp3Info);
startService(intent);
真正下载的功能在 service 之中。然后根据在 service 之中的相应方法中我们可以来下载,当然在这之前应该有文件名,和相应的 url 地址。当然为了不重复下载最好,把存放的文件地址也告知,当然也为了这个文件下载好了放在哪里了?那们这里就存在两个问题了,一个是下载文件,一个是写入 sdcard, 然后,我们在下载文件中还是要判断这个文件是否已经存在的问题的。
有了以上的这些,我们就可以成功得到了我们可以根据我们可以更新列表了,也就是根据我们服务器上的一个 xml 的文件,我们可以下载我们的要求的文件。在这其中我们有一个叫 tab 的控件安排,在这个控件中,我们可以有一个布局文件,关于这个布局文件的使用,我们在这里不作过多的说明,我们只是把代码粘在了下面:
<? xml version = "1.0" encoding = "utf-8" ?>
< TabHost
xmlns:android = "http://schemas.android.com/apk/res/android"
android:id = "@android:id/tabhost"
android:layout_width = "fill_parent"
android:layout_height = "fill_parent" >
< LinearLayout
android:orientation = "vertical"
android:layout_width = "fill_parent"
android:layout_height = "fill_parent"
android:padding = "5dp" >
< TabWidget
android:id = "@android:id/tabs"
android:layout_width = "fill_parent"
android:layout_height = "wrap_content"
/>
< FrameLayout
android:id = "@android:id/tabcontent"
android:layout_width = "fill_parent"
android:layout_height = "fill_parent"
android:padding = "5dp" />
</ LinearLayout >
</ TabHost >
这里我们要注意的是,我们所用到的一些控件的 id 都是系统默认的一些 Id. 在布局文件有了之后,我们使用的是在一个 activity 中实现几个不同的 activity ,关于这个我们又该如何来添加呢,这个有必要来说明一下的。
TabHost tabHost=getTabHost();
Intent remoteIntent= new Intent(MainActivity. this ,Mp3ListActivity. class );
TabHost.TabSpec remotetabSpec=tabHost.newTabSpec( "remote" );
Resources res=getResources();
remotetabSpec.setIndicator( "remote" ,res.getDrawable(android.R.drawable. stat_sys_download ));
remotetabSpec.setContent(remoteIntent);
tabHost.addTab(remotetabSpec);
上面是我们的代码,我们可以在 Oncreate 方法中,来得到我们的那个 tabhost 对象,这个是在我们实现了继承了,我们的 tabActivity 之后那我们的布局文件就一定要有 tabhost 这个标签了,那我们的系统这默认可以直接有一个静态的方法来得到这个 tabhost, 得到这个之后我们来设置我们所需要的相应的 intent ,然后,我们使用了一个内部类, TabHost.TabSpec, 这个类是用来声明一个 tab 的,至于对这个 tab 设置相应的文本和图片,我们要用他的一个方法,就是 setIndicator 来设置,同时我们要为这个 tab 的 content 设置一个用显示的 activity 而这个 activity 也是在之前我们声明的 intent 中就声明过的。最我们把这个 tab 加放到我们的 tabhost 之中去,那我们的程序就可以显示这个 tab 了。
之后我们得到了这个,我们的另一个本地的 activity 中的功能是读出我们本地 sdcard 中这有的文件,这个我们又用到了基于 file 的操作。我们来看一下:
但是我们的程序与书中的一个不同点是,我书中找到了只是歌曲文件,并没有歌词文件,但我们要想把歌词文件与我们相对应的歌曲文件绑定在一起,这个问题我还是想了一下的。好的,我们先来想一下简单的对于歌词的文件的读取然后直接来显示的功能是如何实现的呢?
看下面的代码:
我们来由于用了对应的值对象的功能,然后我们只要把我们的的一个歌曲和歌词的文件对成一个对象,会传给我们的那个相应的本地的 activity 就可以了。
所以我们用一用一个方法来返回对应的我们的本地 sdcard 中对应的歌曲 MUSCI 中的歌曲,我想这还是很简单的,只要我们创建这个目录下的文件,然后 list 也就可以,因为复习我们也把这些代码给了出来:
File file= new File( SDCardRoot +path);
File[] files=file.listFiles();
那我们的对应的数组中也就可以有了这个目录下面的所有的文件了。但是如何如上我们提出的问题如何把这个文件数组中的相应的 mp3 和 krc 文件组成一个对应呢?我们用了下面的一大段代码其实主要的思路很简单,就是在我们遍历一个文件我们先把这个文件判断成 mp3 还是 krc, 然后如果是其中之一,我们遍历我们已经存在的那个对象 list ,看里面是否已经存在了另一个对象了,如果存在我们只要找到把其它的属性设置一下,如果没有,则创建这个对象。要注意的是第一次进行判断的时候要特别处理一下的。因为我们的 list 这时还是空的,我们遍历是会出错的。
在里的处理要特别小心,我在写这段代码的时候就发生了好多因为不细心而产生的许多的错误。好了,这也完成了,在这里我们突然发现了一个问题,也就是我们来实现显示布局的时候采用的 list 这个方法还没有介绍呢,那我们就来介绍一下这个 list 布局的使用。首先要用这个布局的类要实现 ListActivity 这个对象,然后实现这个对象,我们就必须在布局文件中包含一个标签,为了方便我们直接把这个 activity 文件的布局文件写在下面了:
<? xml version = "1.0" encoding = "utf-8" ?>
< LinearLayout xmlns:android = "http://schemas.android.com/apk/res/android"
android:orientation = "vertical" android:layout_width = "fill_parent"
android:layout_height = "fill_parent" >
< LinearLayout android:orientation = "vertical"
android:layout_width = "fill_parent" android:layout_height = "wrap_content" >
< ListView android:id = "@id/android:list" android:layout_width = "fill_parent"
android:layout_height = "wrap_content" android:drawSelectorOnTop = "false"
android:scrollbars = "vertical" />
</ LinearLayout >
</ LinearLayout >
如果没有上面的那个黄色的代码,我们的 listactivity 是会报错的。有了这个了。那我们的 list 里面究竟放什么呢?我们用到了一个方法 setListAdapter(adapter); ,通过这个方法,我们可以为这个 listactivity 提供我们想到显示的对象文本。当然这里面我们也有理由来说明一下呢?
ArrayList<HashMap<String, String>> data = new ArrayList<HashMap<String, String>>();
for (Iterator iterator = infos.iterator(); iterator.hasNext();) {
Mp3Info mp3Info = (Mp3Info) iterator.next();
HashMap<String, String> map1 = new HashMap<String, String>();
map1.put( "Mp3Name" , mp3Info.getMp3Name());
map1.put( "Mp3Size" , mp3Info.getMp3Size());
map1.put( "LrcName" , mp3Info.getLrcName());
map1.put( "LrcSize" , mp3Info.getLrcSize());
data.add(map1);
}
// 个人感觉最重要的就是这个适配器,当然这来是 android 中最简单的适配器的模型,
// 下面来简单介绍一下这个简单适配器的参数的意义
// 首先:第一个参数是上下文对象,我们这里用的是 this
// 第二个是一个数据对象,是一个 arraylist , 但要注意的是这个 arraylist 里面装的的对象
// 是一个个 hasmap ,然后为这个对象添加相应的键值对
// 第三个是一个 id, 这个 id 指的是一个 xml 的布局文件,里面设定了你的 listview 中的第一行放置的
// 形式
// 第四个是一个字符串数组,来表明你 list 数据的中对应的键,
// 第五个是取出的值放在哪里,哪个控件上
SimpleAdapter adapter = new SimpleAdapter( this , data, R.layout. listresource ,
new String[] { "Mp3Name" , "Mp3Size" , "LrcName" , "LrcSize" }, new int [] {
R.id. Mp3Name , R.id. Mp3Size ,R.id. LrcName ,R.id. LrcSize });
在这里我们的 adapter 对象,我们用到了另一个布局文件,也是我们用来显示我们的文本对象的一些控件啊,有人可能要说,我们在之前不是定义过了一个布局文件,的确是的,但我们那个定义的布局文件如果你再看会发现其实里面根本没有什么东西的。只有一个 listview, 而这个是不能满足我们的要求的,现在把我们的的那个布局文件显示在下 :
<? xml version = "1.0" encoding = "utf-8" ?>
< LinearLayout
xmlns:android = "http://schemas.android.com/apk/res/android"
android:orientation = "horizontal"
android:layout_width = "fill_parent"
android:layout_height = "wrap_content"
android:padding = "4dip" >
< TextView
android:id = "@+id/Mp3Name"
android:layout_width = "fill_parent"
android:layout_height = "wrap_content"
android:layout_weight = "1" />
< TextView
android:id = "@+id/Mp3Size"
android:layout_width = "fill_parent"
android:layout_height = "wrap_content"
android:layout_weight = "1"
/>
< TextView
android:id = "@+id/LrcName"
android:layout_width = "fill_parent"
android:layout_height = "wrap_content"
android:layout_weight = "1"
/>
< TextView
android:id = "@+id/LrcSize"
android:layout_width = "fill_parent"
android:layout_height = "wrap_content"
android:layout_weight = "1"
/>
</ LinearLayout >
这个文件比较长,我一直在想一个好方法来描述这里面的关系。我们想那个 listview 是一个大的套子,而我们该显示的东西,应该如何布局,还没有决定,所以要另一个布局文件,这个可以对每一组我们要显示的数据都采用相应的控件来显示。当然以上我们的代码应该是可以看懂的吧!
在这完成之后,我们就要实现对于我们的文件的下载了。在下载中,我们把这项功能放到了一个 service 之中。关于下载文件,我想如的完果你看到现在,应该也是可以明白的吗,因为只要我们有一个 url 我们都可以把这个 url 中的文件的流对象得到,那们对于这个流对象的 read 到组中,然后在我们想下载的地方创建一个文件,把这个读出的现写到这个文件中去,我们也就完成了我们意义上的所谓文件下载了。代码还是给大家吗?防止有些小的问题吧!
public InputStream getInputStreamFormUrl(String urls) throws IOException{
URL url= null ;
HttpURLConnection urlcon= null ;
InputStream input = null ;
try {
url = new URL(urls);
urlcon = (HttpURLConnection) url.openConnection();
input=urlcon.getInputStream();
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// System.out.println(urlcon.getURL());
// 通过我的测试我发现这一行的代码并没有运行,为什么?
return input;
}
public File write2SDFormInput (String path,String filename,InputStream input){
File file= null ;
FileOutputStream fos= null ;
createSDDir(path);
file=createFileInSDCard(filename, path);
try {
fos= new FileOutputStream(file);
byte buffer[]= new byte [4*1024];
int temp=0;
while ((temp=input.read(buffer))!=-1){
fos.write(buffer, 0, temp);
}
fos.flush();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
finally {
try {
fos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} return file;
}
在这些功能完成了,也就基本到了我们这个项目的结束时候了,还有一个功能就是完成这个我们本地文件的播放功能,我这也是非常简单的,我们依然把这个播放功能放到了 service 之中去了。这里我就不作太多的说明了,我只是给出代码吧!相应 service 的使用,也应该清楚了,吧,我们还是说一下吧,关于 service 我们一定要在配置文件中声明,在开始的时候也是使用的 intent 对象的。
private void play(){
if (! isPlaying ) {
String path=getFilePath( mp3Info );
System. out .println(path);
player = MediaPlayer.create ( this , Uri.parse ( "file://" +path));
player .setLooping( false );
player .start();
isPause = false ;
isPlaying = true ;
isRelease = false ;
}
}
我们给出了一个 play 的方法,里面对于文件的的地址,我们用不用了一个 uri, 关于这个我是把 sdcard 中的文件路径,然后转型成一个 uri, 要注意的是我们对于这个文件,我们要有文件头。然后也就可以播放了。当然这里面还有一些小的知识点,比如说对于常量的使用等,比如在接口中的内部类用来定义一些常量啊。还有 intent 中我们可以放入一些叫做值对象的值,关于这个值对象,我在以前到是特意学习过,这个值对象的类要实现一个接口 java.io.Serializable 然后对于每个对象我们都应该有一个 get 和 set 方法,然后还有缺省的构造函数。如果可以的话再实现我们的 toString 方法。暂时就这么多了。好了我们要关于这个要说的就是这么多了,我知道如何对于这些我们如果不加强复习的话,我们对于这些的功能就会很快的忘记了。好了!不再啰嗦了,就到这里吧!