一、mp3播放器源代码
1、MainActivity.java:在此中主要负责播放器首页的功能,包括服务器上的下载列表,和SD卡上已经下载的mp3文件列表package com.wyt.MP3player;
package com.wyt.MP3player;
import android.app.TabActivity;
import android.content.Intent;
import android.content.res.Resources;
import android.os.Bundle;
import android.widget.TabHost;public class MainActivity extends TabActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.main);// 创建TabHost对象
TabHost tabHost = getTabHost();
// 创建得到自带图片对象
Resources resources = getResources();// remote页面
// 创建Intent对象,并指向显示远程地址上的下载列表
Intent remoteIntent = new Intent();
remoteIntent.setClass(this, RemoteMP3ListActivity.class);
// 创建TabSpec对象,这个对象就代表了显示remote列表的页面
TabHost.TabSpec remoteTabSpec = tabHost.newTabSpec("Remote");
// 设置下载的文字与图片,这里用的是android自带的图片
remoteTabSpec.setIndicator("Remote",
resources.getDrawable(android.R.drawable.stat_sys_download));
// 设置该页的内容
remoteTabSpec.setContent(remoteIntent);
// 将设置好的TabSpec对象加到TabHost当中去
tabHost.addTab(remoteTabSpec);// local页面
// 创建Intent对象,并指向显示远程地址上的下载列表
Intent localIntent = new Intent();
localIntent.setClass(this, LocalMP3ListActivity.class);
// 创建TabSpec对象,这个对象就代表了显示remote列表的页面
TabHost.TabSpec localTabSpec = tabHost.newTabSpec("Local");
// 设置下载的文字与图片,这里用的是android自带的图片
localTabSpec.setIndicator("Local",
resources.getDrawable(android.R.drawable.stat_sys_upload_done));
// 设置该页的内容
localTabSpec.setContent(localIntent);
// 将设置好的TabSpec对象加到TabHost当中去
tabHost.addTab(localTabSpec);
}}
2、RemoteMP3ListActivity.java:在此中主要是负责从服务器上解析xml文件,显示服务器上的下载列表package com.wyt.MP3player;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.InputSource;
import org.xml.sax.XMLReader;import android.app.ListActivity;
import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ListView;
import android.widget.SimpleAdapter;import com.wyt.MP3player.service.DownloaderService;
import com.wyt.download.HttpDownloader;
import com.wyt.model.MP3info;
import com.wyt.xml.Mp3ListContentHandler;public class RemoteMP3ListActivity extends ListActivity {
// 创建常量
private static final int UPDATE = 1;
private static final int ABOUT = 2;private List<MP3info> mp3info = null;
// 点击menu后会调用该方法,在此方法中添加需要的控件
@Override
public boolean onCreateOptionsMenu(Menu menu) {// 添加按钮(更新,关于)
menu.add(0, UPDATE, 1, R.string.mp3List_update);
menu.add(0, ABOUT, 2, R.string.mp3List_about);
return super.onCreateOptionsMenu(menu);
}@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.remote_mp3_list);
updateButton();
}// 当点击任意一个按钮时显示的列表
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// 判断得到的按钮号,并做出相应的动作
// 点击的是更新按钮
if (item.getItemId() == UPDATE) {
updateButton();// 点击的是关于按钮
} else if (item.getItemId() == ABOUT) {}
return super.onOptionsItemSelected(item);
}private void updateButton() {
// 传入本机的地址
String xml = downloadXML("http://10.0.2.2:8080/MP3/resources.xml");
// 调用解析xml文件类,并将解析的结果放到Mp3info对象中,最后将这些结果放到List中
mp3info = parseXml(xml);
// 生成一个List对象,并按照SimpleAdapter的标准将mp3info中的数据添加到List中去SimpleAdapter simpleAdapter = buildSimpleAdapter(mp3info);
// 将SimpleAdapter对象设置到ListActivity中去
setListAdapter(simpleAdapter);
}private SimpleAdapter buildSimpleAdapter(List<MP3info> mp3info) {
List<HashMap<String, String>> list = new ArrayList<HashMap<String, String>>();
// 迭代将mp3info消息循环放到list里面去
for (Iterator iterator = mp3info.iterator(); iterator.hasNext();) {
MP3info mp3info1 = (MP3info) iterator.next();
// 创建HashMap对象,将得到的结果根据键值对的形式放到HashMap中区
HashMap<String, String> hashMap = new HashMap<String, String>();
hashMap.put("id", mp3info1.getId());
hashMap.put("mp3_name", mp3info1.getMp3Name());
hashMap.put("mp3_size", mp3info1.getMp3Size());
list.add(hashMap);
}
// 创建一个SimpleAdapter对象
SimpleAdapter simpleAdapter = new SimpleAdapter(this, list,
R.layout.mp3info_item, new String[] { "mp3_name", "mp3_size" },
new int[] { R.id.mp3_name, R.id.mp3_size });
return simpleAdapter;
}// 调用下载类
private String downloadXML(String urlStr) {
// 创建Download对象
HttpDownloader download = new HttpDownloader();
// 得到本机的URL地址
String result = download.download(urlStr);return result;
}// 解析xml文件
private List<MP3info> parseXml(String xmlStr) {
// 创建一个新的字符串
StringReader read = new StringReader(xmlStr);
// 创建新的输入源SAX 解析器将使用 InputSource 对象来确定如何读取 XML 输入
InputSource source = new InputSource(read);
List<MP3info> info = new ArrayList<MP3info>();
try {
// 创建xml解析器
// 创建SAX工厂
SAXParserFactory sax = SAXParserFactory.newInstance();
// 创建xmlReader
XMLReader xmlReader = sax.newSAXParser().getXMLReader();Mp3ListContentHandler mp3ListContentHandler = new Mp3ListContentHandler(
info);
// 为xmlread设置内容处理器
xmlReader.setContentHandler(mp3ListContentHandler);// 开始解析文件
xmlReader.parse(source);
// 循环输出解析内容
for (Iterator iterator = info.iterator(); iterator.hasNext();) {
MP3info mp3info = (MP3info) iterator.next();
}
} catch (Exception e) {
e.printStackTrace();
}
return info;
}@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
// 编辑用户点击列表中的位置,得到要下载的mp3的名字
MP3info mp3infos = mp3info.get(position);
// 生成Intent对象
Intent intent = new Intent();
// 将mp3infos放到intent中去
intent.putExtra("mp3info", mp3infos);
// 指出要跳转的地方
intent.setClass(this, DownloaderService.class);
// 启动Service
startService(intent);super.onListItemClick(l, v, position, id);
}}
3、HttpDownloader.java:负责下载文件package com.wyt.download;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;import com.wyt.utils.FileUtils;
public class HttpDownloader {
private URL url = null;// 根据url下载文件,前提是这个文件是文本文件,函数的返回值是文件中的内容
public String download(String urlStr) {StringBuffer sb = new StringBuffer();
String line = null;
BufferedReader br = null;try {
// 使用IO流读取数据
br = new BufferedReader(new InputStreamReader(getUrl(urlStr)));while ((line = br.readLine()) != null) {
// 将这些字符添加到缓冲区的末端
sb.append(line);
}// 关闭流
br.close();
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}return sb.toString();
}// 下载任意格式的文件
public int downFile(String urlStr, String path, String fileName) {
InputStream inputStream = null;
FileUtils fileUtils = new FileUtils();
if ((fileUtils.isFileExist(fileName, path))) {
return 1;
} else {
try {
inputStream = getUrl(urlStr);
File resultFile = fileUtils
.writeSD(path, fileName, inputStream);
if (resultFile == null) {
return -1;
}
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
try {
inputStream.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}}
return 0;
}// 封装下载的3个步骤的方法
public InputStream getUrl(String urlStr) throws MalformedURLException,
IOException {
// 创建url
url = new URL(urlStr);
// 创建http
HttpURLConnection urlConn = (HttpURLConnection) url.openConnection();
// 读取数据
InputStream inputStream = urlConn.getInputStream();
return inputStream;
}}
4、Mp3ListContentHandler .java:负责处理xml文件
package com.wyt.xml;
import java.util.List;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;import com.wyt.model.MP3info;
public class Mp3ListContentHandler extends DefaultHandler {
private List<MP3info> info = null;
private MP3info mp3info = null;
private String tagName = null;public Mp3ListContentHandler(List<MP3info> info) {
super();
this.info = info;
}public List<MP3info> getInfo() {
return info;
}public void setInfo(List<MP3info> info) {
this.info = info;
}// 在xml文件中,若遇到某个指定标签就将该文件内容写入到MP3info中
@Override
public void characters(char[] ch, int start, int length)
throws SAXException {
String temp = new String(ch, start, length);
if (tagName.equals("id")) {
mp3info.setId(temp);
} else if (tagName.equals("mp3_name")) {
mp3info.setMp3Name(temp);
} else if (tagName.equals("mp3_size")) {
mp3info.setMp3Size(temp);
} else if (tagName.equals("lrc_name")) {
mp3info.setLrcName(temp);
} else if (tagName.equals("lrc_size")) {
mp3info.setLrcSize(temp);
}
}@Override
public void endDocument() throws SAXException {}
// 在结束时,将在xml中读到的数据放到List中
@Override
public void endElement(String uri, String localName, String name)
throws SAXException {
if (name.equals("resource")) {
info.add(mp3info);
}
// System.out.println("%%%%%%%%%%%%");
tagName = "";
}
@Override
public void startDocument() throws SAXException {
// TODO Auto-generated method stub
super.startDocument();
}
// 在开始的时候,一遇到resource就生成MP3info对象
@Override
public void startElement(String uri, String localName, String name,
Attributes attributes) throws SAXException {
this.tagName = localName;// 遇到"resource"就生成一个MP3info对象
if (tagName.equals("resource")) {
mp3info = new MP3info();
}
}
}
5、DownloaderService .java:在一个service中进行下载文件的完成,应该Service的生命周期比Activity更稳定
package com.wyt.MP3player.service;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;import com.wyt.download.HttpDownloader;
import com.wyt.model.MP3info;public class DownloaderService extends Service {
@Override
public IBinder onBind(Intent intent) {return null;
}// 每次点击ListActivity中的项目时,都会调用此方法
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// 从Intent中取出mp3info
MP3info mp3info = (MP3info) intent.getSerializableExtra("mp3info");
// 每一个下载都要单独在一个线程中进行,以免让整个应用程序都处于等待状态,
// 生成一个线程,并把mp3info传入到线程中去
DownloaderThread downloaderThread = new DownloaderThread(mp3info);
// 启动线程
Thread thread = new Thread(downloaderThread);
thread.start();
return super.onStartCommand(intent, flags, startId);
}class DownloaderThread implements Runnable {
// 创建MP3info对象
private MP3info mp3info = null;// 构造方法
public DownloaderThread(MP3info mp3info) {
this.mp3info = mp3info;
}@Override
public void run() {
// 下载地址和下载名字
String urlStr = "http://10.0.2.2:8080/MP3/" + mp3info.getMp3Name();
String urlStr2 = "http://10.0.2.2:8080/MP3/" + mp3info.getLrcName();
// 生成HttpDownloader对象
HttpDownloader httpDownloader = new HttpDownloader();
// 调用下载方法,并将下载结果存到SD卡上
int result = httpDownloader.downFile(urlStr, "mp3/",
mp3info.getMp3Name());
int result2 = httpDownloader.downFile(urlStr2, "mp3/",
mp3info.getLrcName());
// 告诉用户下载结果
String resultMags = null;
if (result == 0 && result2 == 0) {
resultMags = "下载成功";
System.out.println(resultMags);
} else if (result == 1 && result2 == 1) {
resultMags = "文件已经存在";
System.out.println(resultMags);
} else if (result == -1 && result2 == -1) {
resultMags = "下载失败,请重新下载!";
System.out.println(resultMags);
}
}
}
}
6、FileUtils .java:主要是负责SD卡中的数据和文件,目录
package com.wyt.utils;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;import com.wyt.model.MP3info;
import android.os.Environment;
public class FileUtils {
private String SDPATH;
private String getSDPATH(){
return SDPATH;
}
public FileUtils(){
//得到当前外部存储设备的目录
SDPATH=Environment.getExternalStorageDirectory()+File.separator;
}
//在SD卡上创建文件
public File createSDFile(String fileName,String dir) throws IOException{
File file=new File(SDPATH+dir+File.separator+fileName);
file.createNewFile();
return file;
}
//在SD卡上创建目录
public File createSDDir(String dir){
File dirFile = new File(SDPATH+dir+File.separator);
return dirFile;
}
//判断SD卡上的目录是否存在
public boolean isFileExist(String fileName,String path){
File file = new File(SDPATH+path+File.separator+fileName);
return file.exists();
}
//将InputStream里面的数据写入到SD卡中去
public File writeSD(String path,String fileName,InputStream input){
File file = null;
OutputStream output = null;
try{
//调用创建SD卡目录方法
createSDDir(path);
//调用创建SD卡文件的方法
file = createSDFile(fileName,path);
//创建文件输出流对象
output = new FileOutputStream(file);
//4个字节的读取
byte buffer[] = new byte[4*1024];
int temp;
//当文件的内容不为空的时候就停止输出
while((temp=(input.read(buffer)))!=-1){
output.write(buffer, 0, temp);
}
output.flush();
}catch(Exception e){
e.getMessage();
}finally{
try {
output.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return file;
}
//读取目录中的mp3文件和lrc文件的名字和大小
public List<MP3info> getMP3info(String path){
//创建MP3infoList对象
List<MP3info> mp3info = new ArrayList<MP3info>();
//得到文件的目录
File file = new File(SDPATH+File.separator+path);
//用File的listFiles()得到每个文件,返回的是一个File数组
File[] files = file.listFiles();
//循环将文件内容放到list中去,并判断是mp3结尾的还是一lrc结尾的
for(int i=0;i<files.length;i++){
if(files[i].getName().endsWith("mp3")){
MP3info mp3infos = new MP3info();
mp3infos.setMp3Name(files[i].getName());
mp3infos.setMp3Size(files[i].length()+"");
// mp3infos.setLrcName(files[i].getName());
// mp3infos.setLrcSize(files[i].length()+"");
mp3info.add(mp3infos);
}
}
return mp3info;
}
// public List<MP3info> getLRCinfo(String path){
// //创建MP3infoList对象
// List<MP3info> mp3info = new ArrayList<MP3info>();
// //得到文件的目录
// File file = new File(SDPATH+File.separator+path);
// //用File的listFiles()得到每个文件,返回的是一个File数组
// File[] files = file.listFiles();
// //循环将文件内容放到list中去,并判断是mp3结尾的还是一lrc结尾的
// for(int i=0;i<files.length;i++){
// if(files[i].getName().endsWith("lrc")){
// MP3info mp3infos = new MP3info();
// mp3infos.setLrcName(files[i].getName());
// mp3infos.setLrcSize(files[i].length()+"");
// mp3info.add(mp3infos);
// }
// }
// return mp3info;
// }
}
7、LocalMP3ListActivity .java:负责SD卡上已经下载了的MP3文件列表package com.wyt.MP3player;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;import com.wyt.model.MP3info;
import com.wyt.utils.FileUtils;import android.app.ListActivity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.ListView;
import android.widget.SimpleAdapter;public class LocalMP3ListActivity extends ListActivity{
List<MP3info> mp3info = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.local_mp3_list);
}
@Override
protected void onResume() {
//创建FileUtils对象
FileUtils fileUtils = new FileUtils();
//调用FileUtils中的getMP3info(),并将结果放到mp3info中
mp3info = fileUtils.getMP3info("mp3/");
//创建HashMap对象
List<HashMap<String, String>> list = new ArrayList<HashMap<String,String>>();
//循环将文件以键值对的形式放到HashMap中
for (Iterator iterator = mp3info.iterator(); iterator.hasNext();) {
MP3info mp3infos = (MP3info) iterator.next();
//创建HashMap对象
HashMap<String, String> hashMap = new HashMap<String, String>();
//赋值
hashMap.put("mp3_name", mp3infos.getMp3Name());
hashMap.put("mp3_size", mp3infos.getMp3Size());
list.add(hashMap);
}
// 创建一个SimpleAdapter对象
SimpleAdapter simpleAdapter = new SimpleAdapter(this, list,
R.layout.mp3info_item, new String[] { "mp3_name",
"mp3_size"}, new int[] { R.id.mp3_name,
R.id.mp3_size});
setListAdapter(simpleAdapter);
super.onResume();
}
@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
if(mp3info != null){
//编辑用户点击列表中的位置,得到要下载的mp3的名字
MP3info mp3infos = mp3info.get(position);
//生成Intent对象
Intent intent = new Intent();
//将mp3infos放到intent中去
intent.putExtra("mp3info", mp3infos);
//指出要跳转的地方
intent.setClass(this, PlayerActivity.class);
//启动Service
startActivity(intent);
}
}
}
8、PlayerActivity .java:如果点击一首歌,它将进入到另一个页面,就是所谓的播放器,在播放的同时显示歌词,而负责播放,暂停,停止的代码主要是PlayerService.java来负责,因为放在Service中你就可以实现边放歌边看短信好或其他的事。而歌词的显示又需要一个类来解析歌词,这里用LrcProcessor.javapackage com.wyt.MP3player;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Queue;
import android.app.Activity;
import android.content.Intent;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ImageButton;
import android.widget.TextView;import com.wyt.MP3player.service.AppConstant;
import com.wyt.MP3player.service.PlayerService;
import com.wyt.lrc.LrcProcessor;
import com.wyt.model.MP3info;public class PlayerActivity extends Activity {
ImageButton startButton = null;
ImageButton stopButton = null;
ImageButton overButton = null;
private TextView lrcTextView = null;MediaPlayer mediaPlayer = null;
private MP3info mp3info = null;
private ArrayList<Queue> queue = null;
private Handler handler = new Handler();
private UdateTimeCallback updateTimeCallback = null;
private long begin = 0;
private long nextTimeMill = 0;
private long currentTimeMill = 0;
private String message = null;
private long pauseTimeMills = 0;
private boolean isPlaying = false;@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.player);
// 创建Intent对象
Intent intent = getIntent();
// 得到传入Intent中的mp3info值
mp3info = (MP3info) intent.getSerializableExtra("mp3info");// 得到开始,暂停,结束按钮
startButton = (ImageButton) findViewById(R.id.start);
stopButton = (ImageButton) findViewById(R.id.stop);
overButton = (ImageButton) findViewById(R.id.over);
lrcTextView = (TextView) findViewById(R.id.lrcTextView);// 设置监听器
startButton.setOnClickListener(new StartButtonListener());
stopButton.setOnClickListener(new StopButtonListener());
overButton.setOnClickListener(new OverButtonListener());
}// 开始按钮的监听器
class StartButtonListener implements OnClickListener {
@Override
public void onClick(View v) {
// 创建一个Intent对象,用于同时Service开始播放MP3
Intent intent = new Intent();
intent.setClass(PlayerActivity.this, PlayerService.class);
intent.putExtra("mp3info", mp3info);
intent.putExtra("MSG", AppConstant.PlayerMsg.PLAY_MSG);
// 读取LRC文件
prepareLrc(mp3info.getLrcName());
// 启动Service
startService(intent);
// 将begin的值设置为当前毫秒数
begin = System.currentTimeMillis();
// 执行歌词更新
handler.postDelayed(updateTimeCallback, 5);
isPlaying = true;
}
}// 根据歌词文件的名字来读取歌词文件中的信息
private void prepareLrc(String lrcName) {
try {
// 创建文件输出流
InputStream inputStream = new FileInputStream(Environment
.getExternalStorageDirectory().getAbsolutePath()+File.separator+"mp3"+File.separator+lrcName);
// 创建歌词处理类对象
LrcProcessor lrcProcessor = new LrcProcessor();
// 将得到的歌词文件放到歌词文件处理方法中,并赋给一个队列
queue = lrcProcessor.process(inputStream);
// 创建一个UpdateTimeCallback对象,并将处理过的歌词放到
// UdateTimeCallback()中,将歌词显示在对应的时间
updateTimeCallback = new UdateTimeCallback(queue);
begin = 0;
currentTimeMill = 0;
nextTimeMill = 0;
} catch (FileNotFoundException e) {
System.out.println(e.getMessage());
System.out.println("meiy");
}
}// 暂停按钮的监听器
class StopButtonListener implements OnClickListener {
@Override
public void onClick(View v) {
// 通知Service暂停播放MP3
Intent intent = new Intent();
intent.setClass(PlayerActivity.this, PlayerService.class);
intent.putExtra("MSG", AppConstant.PlayerMsg.PAUSE_MSG);
// 启动Service
startService(intent);
// 如果是处于播放就移除updateTimeCallback,并且记录当时暂停的时间
if (isPlaying) {
handler.removeCallbacks(updateTimeCallback);
pauseTimeMills = System.currentTimeMillis();
} else {
/*
* 否则就播放,重新更新歌词,并把: 系统当前的时间-刚刚暂停时的时间=暂停了多久的时间
* ,然后将这结果+开始的时间begin=现在需要歌对应的歌词时间, 并将正确的歌词显示出来
*/
handler.postDelayed(updateTimeCallback, 5);
begin = System.currentTimeMillis() - pauseTimeMills + begin;
}
isPlaying = isPlaying ? false:true;
}
}// 结束按钮的监听器
class OverButtonListener implements OnClickListener {
@Override
public void onClick(View v) {
// 让Service停止播放MP3文件
Intent intent = new Intent();
intent.setClass(PlayerActivity.this, PlayerService.class);
intent.putExtra("MSG", AppConstant.PlayerMsg.STOP_MSG);
// 启动Service
startService(intent);
// 从Handler中移除updateTimeCallback
handler.removeCallbacks(updateTimeCallback);
}
}// 更新歌词,在一个新线程中完成
class UdateTimeCallback implements Runnable {
Queue time = null;
Queue messages = null;public UdateTimeCallback(ArrayList<Queue> queue) {
// 从ArrayList当中取出相应的队列对象
time = queue.get(1);
messages = queue.get(1);
}@Override
public void run() {
// 计算不断变化的时间,并以毫秒为单位
long offset = System.currentTimeMillis() - begin;
// 如果时间为零,也就是说还没有时间变化,这首歌刚刚开
// 始播放的时候,就直接得到时间和与之对应的歌词
if (currentTimeMill == 0) {
nextTimeMill = (Long) time.poll();
message = (String) messages.poll();
}
// 如果时间变化到大于下一次要更新歌词的时间就重新设置歌词文本
if (offset >= nextTimeMill) {
lrcTextView.setText(message);
nextTimeMill = (Long) time.poll();
message = (String) messages.poll();
}
// 每隔10毫秒就执行此类,检查时间是否大于下次显示歌词的时间
currentTimeMill = currentTimeMill + 10;
handler.postDelayed(updateTimeCallback, 10);
}}
}
9、PlayerService .java
package com.wyt.MP3player.service;
import java.io.File;
import com.wyt.model.MP3info;
import android.app.Service;
import android.content.Intent;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Environment;
import android.os.IBinder;public class PlayerService extends Service {
private boolean isPlaying = false;
private boolean isStop = false;
private boolean isReleased = false;
MediaPlayer mediaPlayer = null;@Override
public IBinder onBind(Intent intent) {
return null;
}@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// 得到传入Intent中的mp3info值
MP3info mp3info = (MP3info) intent.getSerializableExtra("mp3info");
int MSG = intent.getIntExtra("MSG", 0);
if (mp3info != null) {
if (MSG == AppConstant.PlayerMsg.PLAY_MSG) {
play(mp3info);
}
} else {
if (MSG == AppConstant.PlayerMsg.PAUSE_MSG) {
pause();
} else if (MSG == AppConstant.PlayerMsg.STOP_MSG) {
stop();
}
}
return super.onStartCommand(intent, flags, startId);
}// 播放MP3
private void play(MP3info mp3info) {
String path = getMP3Path(mp3info);
// 点击列表,并根据路径找到播放的文件
mediaPlayer = MediaPlayer.create(this, Uri.parse("file://" + path));
// 设置不能重复播放
mediaPlayer.setLooping(false);
// 开始播放
mediaPlayer.start();
isPlaying = true;
isReleased = false;
}// 暂停MP3
private void pause() {
if (mediaPlayer != null) {
if (!isReleased) {
// 播放时点击暂停
if (!isStop) {
mediaPlayer.pause();
isStop = true;
isPlaying = true;
// 否则,暂停时可以播放
} else {
mediaPlayer.start();
isStop = false;
}
}
}
}// 结束播放MP3
private void stop() {
if (mediaPlayer != null) {
if (isPlaying) {
if (!isReleased) {
mediaPlayer.stop();
// 释放播放器中不用的资源
mediaPlayer.release();
isReleased = true;
}
isPlaying = false;
}
}
}// 得到文件的目录
private String getMP3Path(MP3info mp3info) {
String SDPATH = Environment.getExternalStorageDirectory()
.getAbsolutePath();
String path = SDPATH + File.separator + "mp3" + File.separator
+ mp3info.getMp3Name();
return path;
}
}
10、LrcProcessor .java:解析lrc文件package com.wyt.lrc;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Queue;
import java.util.regex.Matcher;
import java.util.regex.Pattern;public class LrcProcessor {
public ArrayList<Queue> process(InputStream inputStream) {
// 存放时间数据
Queue<Long> timeMills = new LinkedList<Long>();
// 存放时间点所对应的歌词
Queue<String> messages = new LinkedList<String>();
ArrayList<Queue> queue = new ArrayList<Queue>();
try {
// 创建BufferedReader对象
BufferedReader br = new BufferedReader(new InputStreamReader(
inputStream));
String temp = null;
int i = 0;// 创建正则表达式
Pattern pattern = Pattern.compile("\\[([^\\]]+)\\]");
boolean b = true;
String result = null;
// 根据正则表达式来找出时间和时间相应的歌词
while ((temp = br.readLine()) != null) {
i++;
Matcher matcher = pattern.matcher(temp);
if (matcher.find()) {
if (result != null) {
messages.add(result);
}
String timeStr = matcher.group();
Long timeMill = time2Long(timeStr.substring(1,
timeStr.length() - 1));
if (b) {
timeMills.offer(timeMill);
}
String msg = temp.substring(10);
result = "" + msg + "\n";
} else {
result = result + temp + "\n";
}
}
messages.add(result);
queue.add(timeMills);
queue.add(messages);
} catch (Exception e) {
// TODO: handle exception
}
return queue;
}// 将时间转化为毫秒
public Long time2Long(String timeStr) {
String str1[] = timeStr.split(":");
int min = Integer.parseInt(str1[0]);
String str2[] = str1[1].split("\\.");
int sec = Integer.parseInt(str2[0]);
int mill = Integer.parseInt(str2[1]);
return min * 60 * 1000 + sec * 1000 + mill * 10L;
}
}
12、MP3info .java
package com.wyt.model;
import java.io.Serializable;
public class MP3info implements Serializable{
private String id;
private String mp3Name;
private String mp3Size;
private String lrcName;
private String lrcSize;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getMp3Name() {
return mp3Name;
}
public void setMp3Name(String mp3Name) {
this.mp3Name = mp3Name;
}
public String getMp3Size() {
return mp3Size;
}
public void setMp3Size(String mp3Size) {
this.mp3Size = mp3Size;
}
public String getLrcName() {
return lrcName;
}
public void setLrcName(String lrcName) {
this.lrcName = lrcName;
}
public String getLrcSize() {
return lrcSize;
}
public void setLrcSize(String lrcSize) {
this.lrcSize = lrcSize;
}
public MP3info(String id, String mp3Name, String mp3Size, String lrcName,
String lrcSize) {
super();
this.id = id;
this.mp3Name = mp3Name;
this.mp3Size = mp3Size;
this.lrcName = lrcName;
this.lrcSize = lrcSize;
}
public MP3info() {
super();
}
@Override
public String toString() {
return "MP3info [id="+id+",mp3_name=" + mp3Name + ", mp3_size=" + mp3Size + ", lrc_name=" + lrcName + ",lrc_size="+lrcSize+"]";
}
}
13、main.xml
<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="5dip"> <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="5dip"/> </LinearLayout> </TabHost>
remote_mp3_list.xml
<?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:id="@+id/ListLinearLayout" android:layout_width="fill_parent" android:layout_height="wrap_content" android:scrollbars="vertical" > <ListView android:id="@id/android:list" android:layout_width="fill_parent" android:layout_height="wrap_content" android:scrollbars="vertical" android:drawSelectorOnTop="false" android:padding="1px" /> </LinearLayout> </LinearLayout>
mp3info_item.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="horizontal" android:paddingLeft="10dip" android:paddingRight="10dip" android:paddingTop="1dip" android:paddingBottom="1dip" > <TextView android:id="@+id/mp3_name" android:layout_height="30dip" android:layout_width="180dip" android:textSize="10pt"/> <TextView android:id="@+id/mp3_size" android:layout_height="30dip" android:layout_width="180dip" android:textSize="10pt"/> <TextView android:id="@+id/lrc_name" android:layout_height="30dip" android:layout_width="180dip" android:textSize="10pt"/> <TextView android:id="@+id/lrc_size" android:layout_height="30dip" android:layout_width="180dip" android:textSize="10pt"/> </LinearLayout>
local_mp3_list.xml
<?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:id="@+id/ListLinearLayout" android:layout_width="fill_parent" android:layout_height="wrap_content" android:scrollbars="vertical" > <ListView android:id="@id/android:list" android:layout_width="fill_parent" android:layout_height="wrap_content" android:scrollbars="vertical" android:drawSelectorOnTop="false" android:padding="1px" /> </LinearLayout> </LinearLayout>
player.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" android:paddingLeft="10dip" android:paddingRight="10dip" android:paddingTop="1dip" android:paddingBottom="1dip"> <TextView android:id="@+id/lrcTextView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="welcome to you!!" android:padding="1dip" android:layout_weight="1"/> <ImageButton android:id="@+id/start" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/start"/> <ImageButton android:id="@+id/stop" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/stop"/> <ImageButton android:id="@+id/over" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/over"/> </LinearLayout>