实现的功能:开发一个可以播放音乐并同步显示歌词文件的APP.
成果展示:
总体设计图:
实现流程图
代码展示:
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.example.mp3player">
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Mp3Player"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".Mp3ListActivity"
tools:ignore="Instantiatable"
android:label="@string/app_name" />
<activity
android:name=".PlayerActivity"
tools:ignore="DuplicateActivity,Instantiatable"
android:label="@string/app_name" />
<activity
android:name=".LocalMp3ListActivity"
tools:ignore="DuplicateActivity,Instantiatable"
android:label="@string/app_name" />
<service android:name="com.example.service.DownloadService" />
</application>
<uses-permission android:name="android.permission.INTERNET"
tools:ignore="ManifestOrder" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
tools:ignore="ScopedStorage" />
</manifest>
MainActivity:
监听到用户点击Menu---->弹出选项框“Update”、“About”按钮---->onOptionsItemSelected()监听到用户点击更新按钮后调用updateListView()下载Mp3文件,采用SAX解析器解析文件生成一个Mp3Info对象,通过buildSimpleAdapter()将mp3文件的名字和大小返回给TextView显示。onListItemClick()监听用户点击哪个mp3文件,开启下载文件服务DownloadService。
package com.example.mp3player;
import androidx.appcompat.app.AppCompatActivity;
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.example.download.HttpDownloader;
import com.example.model.Mp3Info;
import com.example.service.DownloadService;
import com.example.xml.Mp3ListContentHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
public class MainActivity extends ListActivity {
private static final int UPDATE = 1;
private static final int ABOUT = 2;
private List<Mp3Info> mp3Infos = null;
//after user click Menu button ,the function is called,so wo can monitor in this function
@Override
public boolean onCreateOptionsMenu(Menu menu){
//Distinguish Button of Update and About
menu.add(0,UPDATE,1,"update list");
menu.add(0,ABOUT,2,"about");
return super.onCreateOptionsMenu(menu);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
public boolean onOptionsItemSelected(MenuItem item){
//user click Update button
if(item.getItemId() == UPDATE){
updateListView();
}else if(item.getItemId() == ABOUT){
//user click button about
}
System.out.println("itemId----->"+item.getItemId());
return super.onOptionsItemSelected(item);
}
//The smaller the function,the better! because Strongly Reusability
//function of downloading xml
private String downloadXml(String urlStr){
HttpDownloader httpDownloader = new HttpDownloader();
String result = httpDownloader.download(urlStr);
return result;
}
//Convert an xml file into an array of object
private List<Mp3Info> parse(String xmlStr){
/*
use SAX parser to parse xml document
SAXParserFactory.newInstance():get an SAXParserFactory new instance。
*/
SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
//define an arraylist of mp3Info
List<Mp3Info> infos = new ArrayList<Mp3Info>();
try{
/*
saxParserFactory是一个SAX解析器,用于解析xml文档
newSAXParser将解析器工厂实例化
getXMLReader()生成一个解析器实例
*/
XMLReader xmlReader = saxParserFactory.newSAXParser().getXMLReader();
//定义一个将xml转换成Mp3Info对象数组的生成器
Mp3ListContentHandler mp3ListContentHandler = new Mp3ListContentHandler(infos);
//解析器实例捆绑生成器
xmlReader.setContentHandler(mp3ListContentHandler);
//将xml文件转换成一个Java可以处理的InputStream流
xmlReader.parse(new InputSource(new StringReader(xmlStr)));
//Iterator跌倒infos的数据
for(Iterator iterator = infos.iterator();iterator.hasNext();){
Mp3Info mp3Info = (Mp3Info) iterator.next();
System.out.println(mp3Info);
}
} catch (Exception e) {
e.printStackTrace();
}
return infos;
}
private SimpleAdapter buildSimpleAdapter(List<Mp3Info> mp3Infos){
List<HashMap<String,String>> list = new ArrayList<HashMap<String,String>>();
//iterate mp3Infos and assign to TextView to show to user
for(Iterator iterator= mp3Infos.iterator();iterator.hasNext();){
Mp3Info mp3Info = (Mp3Info) iterator.next();
HashMap<String ,String> map = new HashMap<String,String>();
map.put("mp3_name",mp3Info.getMp3Name());
map.put("mp3_size",mp3Info.getMp3Size());
list.add(map);
}
//create an simpleAdapter object
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 void updateListView(){
//download mp3 information xml file
String xml = downloadXml("http://192.168.1.34:8081/MP3/resources.xml");
//analysis xml file ane put the consequence in the Object mp3Info,last add Object in the List
List<Mp3Info> mp3Infos = parse(xml);
//generate Object of List ,put mp3Info Object in the List according to standard "SimpleAdapter"
SimpleAdapter simpleAdapter = buildSimpleAdapter(mp3Infos);
//set simpleAdapter into ListActivity
setListAdapter(simpleAdapter);
}
//after you click the activity file button ,the function is called ,you can know which textview is clicked and then you can download it.
protected void onListItemClick(ListView l, View v, int position, long id){
//position specifies which mp3Info object is called
Mp3Info mp3Info = mp3Infos.get(position);
//use service to download file in network
Intent intent = new Intent();
intent.putExtra("mp3Info",mp3Info);
intent.setClass(MainActivity.this, DownloadService.class);
startService(intent);
super.onListItemClick(l,v,position,id);
}
}
DownloadService
创建一个Mp3Info实体对象,接受从MainActivity中传来的数据,DownloadThread 方法开启新线程调用HttpDownloader 下载文件。并根据HttpDownloader.download()返回结果采用Notification方式提示用户文件下载情况
package com.example.service;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import androidx.annotation.Nullable;
import com.example.download.HttpDownloader;
import com.example.model.Mp3Info;
public class DownloadService extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
//acquire which file or mp3Info Object is clicked ,then we can call DownloadThread function.
Mp3Info mp3Info = (Mp3Info) intent.getSerializableExtra("mp3Info");
DownloadThread downloadThread = new DownloadThread(mp3Info);
Thread thread = new Thread(downloadThread);
thread.start();
return super.onStartCommand(intent, flags, startId);
}
//download specified(mp3Info appointed) files in a thread over the network
//create a new thread.
class DownloadThread implements Runnable{
private Mp3Info mp3Info = null;
public DownloadThread(Mp3Info mp3Info) {
this.mp3Info = mp3Info;
}
public void run(){
String mp3Url = "http://192.168.1.34:8081/MP3"+mp3Info.getMp3Name();
HttpDownloader httpDownloader = new HttpDownloader();
String result = httpDownloader.download(mp3Url);
int resultInt = Integer.parseInt(result);
//notify user the consequence of downloading file
String resultMessage = null;
if(resultInt == -1){
resultMessage = "download file failed";
}else if(resultInt == 0){
resultMessage = "file existed,can't download repeatly";
}else if(resultInt == 1){
resultMessage = "file download successsfully";
}
}
}
}
Mp3Info
package com.example.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 Mp3Info() {
super();
}
@Override
public String toString() {
return "Mp3Info{" +
"id='" + id + '\'' +
", mp3Name='" + mp3Name + '\'' +
", mp3Size='" + mp3Size + '\'' +
", lrcName='" + lrcName + '\'' +
", 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 void setId(String id) {
this.id = id;
}
public void setMp3Name(String mp3Name) {
this.mp3Name = mp3Name;
}
public void setMp3Size(String mp3Size) {
this.mp3Size = mp3Size;
}
public void setLrcName(String lrcName) {
this.lrcName = lrcName;
}
public void setLrcSize(String lrcSize) {
this.lrcSize = lrcSize;
}
public String getId() {
return id;
}
public String getMp3Name() {
return mp3Name;
}
public String getMp3Size() {
return mp3Size;
}
public String getLrcName() {
return lrcName;
}
public String getLrcSize() {
return lrcSize;
}
}
HttpDownloader
package com.example.download;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
public class HttpDownloader {
public String download(String urlStr){
StringBuffer sb = new StringBuffer();
String line = null;
BufferedReader buffer = null;
try{
//创建一个url对象
URL url = new URL(urlStr);
//创建一个http连接
HttpURLConnection urlConn = (HttpURLConnection) url.openConnection();
//使用io流读取数据
buffer = new BufferedReader(new InputStreamReader((urlConn.getInputStream())));
while((line = buffer.readLine()) != null){
sb.append(line);
}
} catch (Exception e) {
e.printStackTrace();
}finally {
try{
buffer.close();
}catch (Exception e){
e.printStackTrace();
}
}
return sb.toString();
}
}
Mp3ListActivity
采用tabHost布局显示远程文件和本地文件,点击“remote”按钮,选择文件下载,即可在“local”按钮下的页面中查看到下载的文件。页面布局采用layout.mp3_list_activity。
package com.example.mp3player;
import android.app.TabActivity;
import android.content.Intent;
import android.content.res.Resources;
import android.os.Bundle;
import android.widget.TabHost;
public class Mp3ListActivity extends TabActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.mp3_list_activity);
//obtain a TabHost object
TabHost tabHost = getTabHost();
//generate a Intent object and point to MainActivity
Intent remoteIntent = new Intent();
remoteIntent.setClass(this,MainActivity.class);
//generate TabSpec object ,it assign a page includes Remote button and file list
TabHost.TabSpec remoteSpec = tabHost.newTabSpec("Reomte");
//define the remote button text and image form android library
Resources res = getResources();
remoteSpec.setIndicator("Remote",res.getDrawable(android.R.drawable.stat_sys_download));
//set remoteSpec object text and picture
remoteSpec.setContent(remoteIntent);
//add remoteSpec in tabHost
tabHost.addTab(remoteSpec);
Intent localIntent = new Intent();
localIntent.setClass(this,LocalMp3ListActivity.class);
TabHost.TabSpec localSpec = tabHost.newTabSpec("Local");
localSpec.setIndicator("Local",res.getDrawable(android.R.drawable.stat_sys_download));
localSpec.setContent(localIntent);
tabHost.addTab(localSpec);
}
}
LocalMp3ListActivity
读取到下载的文件的名字和尺寸,显示在页面上,监听点击事件。如果监听到用户点击某文件,调用PlayerActivity播放音乐。
package com.example.mp3player;
import android.app.ListActivity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import com.example.model.Mp3Info;
import com.example.utils.FileUtils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
public class LocalMp3ListActivity extends ListActivity {
private List<Mp3Info> mp3Infos = null;
@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.local_mp3_list_activity);
}
@Override
protected void onResume() {
FileUtils fileUtils = new FileUtils();
List<Mp3Info> mp3Infos = fileUtils.getMp3File("mp3/");
List<HashMap<String, String>> list = new ArrayList<HashMap<String, String>>();
for (Iterator iterator = mp3Infos.iterator(); iterator.hasNext(); ) {
Mp3Info mp3Info = (Mp3Info) iterator.next();
HashMap<String, String> map = new HashMap<String, String>();
map.put("mp3_name", mp3Info.getMp3Name());
map.put("mp3_size", mp3Info.getMp3Size());
list.add(map);
}
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();
}
protected void onListItemClick(ListView l, View v, int position, long id){
if(mp3Infos != null){
Mp3Info mp3Info = mp3Infos.get(position);
Intent intent = new Intent();
intent.putExtra("mp3Info",mp3Info);
intent.setClass(this,PlayerActivity.class);
startActivity(intent);
}
}
}
FileUtils
package com.example.utils;
import android.os.Environment;
import com.example.model.Mp3Info;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
public class FileUtils {
private String SDCardRoot;
//get Directory of external device
public FileUtils(){
SDCardRoot = Environment.getExternalStorageDirectory().getAbsolutePath();
}
//create document in SDCard
//function file.seperator is equal to '/'
public File createFileInSDCard(String fileName, String dir) throws IOException {
File file = new File(SDCardRoot+dir+File.separator+fileName);
file.createNewFile();
return file;
}
//create folder in SDCard
public File createSDDir(String dir){
File dirFile = new File(SDCardRoot+dir+File.separator);
dirFile.mkdir();
return dirFile;
}
//check whether the file exists on the sd card
public boolean isFileExist(String fileName,String dir){
File file = new File(SDCardRoot+dir+File.separator+fileName);
return file.exists();
}
//write datas from InputStream in sd card
public File write2SDFromInput(String path, String fileName, InputStream input){
File file = null;
OutputStream output = null;
try{
createSDDir(path);
file = createFileInSDCard(fileName,path);
output = new FileOutputStream(file);
byte buffer[] = new byte[4*1024];
int temp;
while((temp = input.read(buffer)) != -1){
output.write(buffer,0,temp);
}
output.flush();
} catch (IOException e) {
e.printStackTrace();
}finally {
try{
output.close();
}catch (Exception e){
e.printStackTrace();
}
}
return file;
}
//read files name and size
public List<Mp3Info> getMp3File(String path){
List<Mp3Info> mp3InfoList = new ArrayList<Mp3Info>();
File file = new File(SDCardRoot+File.separator+path);
//traverse directory
File[] files = file.listFiles();
for(int i=0;i<files.length;i++){
if(files[i].getName().endsWith("mp3")){
Mp3Info mp3Info = new Mp3Info();
mp3Info.setMp3Name(files[i].getName());
mp3Info.setMp3Size(files[i].length()+"");
mp3InfoList.add(mp3Info);
}
}
return mp3InfoList;
}
}
PlayerActivity
创建播放、暂停、停止三个按钮;如果监听到调用PlayService 服务,并开启线程更新歌词文件。
package com.example.mp3player;
import android.app.Activity;
import android.content.Intent;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.view.View;
import android.widget.ImageButton;
import android.widget.TextView;
import com.example.lrc.LrcProcess;
import com.example.model.AppConstant;
import com.example.model.Mp3Info;
import com.example.service.PlayService;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Queue;
public class PlayerActivity extends Activity {
private ImageButton beginButton = null;
private ImageButton pauseButton = null;
private ImageButton stopButton = null;
private MediaPlayer mediaPlayer = null;
private boolean isPlaying = false;
private boolean isPause = false;
private boolean isReleased = false;
private Mp3Info mp3Info = null;
private Handler handler = new Handler();
private UpdateTimeCallback updateTimeCallback = null;
private long begin = 0;
private long nextTimeMill = 0;
private long currentTimeMill = 0;
private String message = null;
private long pauseTimeMills = 0;
private ArrayList<Queue> queues = null;
private TextView lrcTextView = null;
@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.player);
Intent intent = new Intent();
mp3Info = (Mp3Info) intent.getSerializableExtra("mp3Info");
beginButton = (ImageButton) findViewById(R.id.begin);
pauseButton = (ImageButton) findViewById(R.id.pause);
stopButton = (ImageButton) findViewById(R.id.stop);
lrcTextView = (TextView) findViewById(R.id.lrcText);
beginButton.setOnClickListener(new beginButtonListener());
pauseButton.setOnClickListener(new pauseButtonListener());
stopButton.setOnClickListener(new stopButtonListener());
}
class beginButtonListener implements View.OnClickListener{
@Override
public void onClick(View v){
//用于使用Service播放Mp3音乐
Intent intent = new Intent();
intent.setClass(PlayerActivity.this,PlayService.class);
intent.putExtra("mp3Info",mp3Info);
intent.putExtra("MSG",AppConstant.PlayMsg.PLAY_MSG);
//读取lrc文件
prepareLrc(mp3Info.getLrcName());
//启动service
startService(intent);
//begin当前毫秒数
begin = System.currentTimeMillis();
//延后5s执行updateTimeCallback函数
handler.postDelayed(updateTimeCallback,5);
isPlaying = true;
}
}
class pauseButtonListener implements View.OnClickListener{
@Override
public void onClick(View v){
Intent intent = new Intent();
intent.setClass(PlayerActivity.this, PlayService.class);
intent.putExtra("MSG", AppConstant.PlayMsg.PAUSE_MSG);
startService(intent);
if(isPlaying){
handler.removeCallbacks(updateTimeCallback);
pauseTimeMills = System.currentTimeMillis();
}else {
handler.postDelayed(updateTimeCallback,5);
begin = System.currentTimeMillis() - pauseTimeMills+begin;
}
isPlaying = isPlaying ? false : true;
}
}
class stopButtonListener implements View.OnClickListener{
@Override
public void onClick(View v){
//通知Service停止播放Mp3文件
Intent intent = new Intent();
intent.setClass(PlayerActivity.this,PlayService.class);
intent.putExtra("MSG",AppConstant.PlayMsg.STOP_MSG);
startService(intent);
//从handler中移除updateTimeCallback--->更新lyric函数。
handler.removeCallbacks(updateTimeCallback);
}
}
//更新歌词文件
class UpdateTimeCallback implements Runnable{
ArrayList<Queue> queues = null;
Queue times = null;
Queue messages = null;
public UpdateTimeCallback(ArrayList<Queue> queues){
this.queues = queues;
times = queues.get(0);
messages = queues.get(1);
}
@Override
public void run() {
//计算开始播放到现在的时长,以毫秒为单位。
long offset = System.currentTimeMillis()-begin;
System.out.println(offset);
//currentTimeMill == 0---->第一次调用UpdateTimeCallback函数
if(currentTimeMill == 0){
nextTimeMill = (Long)times.poll();
message = (String) messages.poll();
}
//现在时间已经大于下一次要播放音乐的时间了,把下一次要重新的时间点和歌词信息保存起来
if(offset >= nextTimeMill){
lrcTextView.setText(message);
message = (String)messages.poll();
nextTimeMill = (Long)times.poll();
}
currentTimeMill = currentTimeMill + 10;
//每隔10ms检查当前时间是否超过要重现加载歌词的时间
handler.postDelayed(updateTimeCallback,10);
}
}
//acquire lyric file information by lyric file name.
private void prepareLrc(String lrcName){
try{
InputStream inputStream = new FileInputStream(Environment.getExternalStorageDirectory().getAbsolutePath());
LrcProcess lrcProcess = new LrcProcess();
//obtain lyric by inputStream.
//得到事件队列和歌词队列
queues = lrcProcess.process(inputStream);
updateTimeCallback = new UpdateTimeCallback(queues);
begin = 0;
currentTimeMill = 0;
nextTimeMill = 0;
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
PlayService
package com.example.service;
import android.app.Service;
import android.content.Intent;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Environment;
import android.os.IBinder;
import androidx.annotation.Nullable;
import com.example.model.AppConstant;
import com.example.model.Mp3Info;
import com.example.mp3player.PlayerActivity;
import java.io.File;
public class PlayService extends Service {
private MediaPlayer mediaPlayer = null;
private boolean isPlaying = false;
private boolean isPause = false;
private boolean isReleased = false;
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
public int onStartCommand(Intent intent,int flags,int startId){
Mp3Info mp3Info = (Mp3Info) intent.getSerializableExtra("mp3Info");
int MSG = intent.getIntExtra("MSG",0);
if(mp3Info != null){
if(MSG == AppConstant.PlayMsg.PLAY_MSG){
play(mp3Info);
}
}else{
if(MSG == AppConstant.PlayMsg.PAUSE_MSG){
pause();
}else if(MSG == AppConstant.PlayMsg.STOP_MSG){
stop();
}
}
return super.onStartCommand(intent,flags,startId);
}
private void play(Mp3Info mp3Info){
String path = getMp3Path(mp3Info);
//function uri.parese compulsive require having prefix "file://"
mediaPlayer = MediaPlayer.create(this, Uri.parse("file://"+path));
//turn off looply playing music
mediaPlayer.setLooping(false);
//begin playing music
mediaPlayer.start();
isPlaying = true;
isPause = false;
}
private void pause(){
//if we don't know whether mediaPlayer equals to null,we will meet problem of null pointer exception after we directly used it.
if(mediaPlayer != null){
if(!isReleased){
if(!isPause){
//state is playing music,click the pause button,we will pause the music.
mediaPlayer.pause();
isPause = true;
isPlaying = false;
}else{
//state is pausing,click the pause button,we will play the music.
mediaPlayer.start();
isPause = false;
isPlaying = true;
}
}
}
}
private void stop(){
if(mediaPlayer != null){
if(isPlaying){
if(!isReleased){
mediaPlayer.stop();
//release all resources mediaPlayer used
mediaPlayer.release();
isReleased = true;
}
isPlaying = false;
}
}
}
//acquire mp3Path
private String getMp3Path(Mp3Info mp3Info){
String SDCardRoot = Environment.getExternalStorageDirectory().getAbsolutePath();
String path = SDCardRoot+ File.separator+"mp3/"+File.separator+mp3Info.getMp3Name();
return path;
}
}
LrcProcess
package com.example.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 LrcProcess {
public ArrayList<Queue> process(InputStream inputStream){
//time list
Queue<Long> timeMills = new LinkedList<Long>();
//message list
Queue<String> messages = new LinkedList<>();
ArrayList<Queue> queues = new ArrayList<Queue>();
try{
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String temp = null;
int i = 0;
Pattern p = Pattern.compile("\\[([^\\]]+)\\]");
String result = null;
boolean b = true;
while((temp = bufferedReader.readLine()) != null){
i++;
Matcher m = p.matcher(temp);
if(m.find()){
if(result != null){
messages.add(result);
}
String timeStr = m.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);
queues.add(timeMills);
queues.add(messages);
}
}catch (Exception e){
e.printStackTrace();
}
return queues;
}
//turn time to millisecond
public Long time2Long(String timeStr){
String s[]= timeStr.split(":");
int min = Integer.parseInt(s[0]);
String ss[] = s[1].split("\\.");
int sec = Integer.parseInt(ss[0]);
int mill = Integer.parseInt(ss[1]);
return min * 60 * 100 + sec * 1000 + mill * 10L;
}
}
AppConstant
package com.example.model;
public interface AppConstant {
public class PlayMsg{
public static final int PLAY_MSG = 1;
public static final int PAUSE_MSG = 2;
public static final int STOP_MSG = 3;
}
public class URL{
public static final String BASE_URL = "http://192.168.1.34:8081/MP3/";
}
}
Mp3ListContentHandler
package com.example.xml;
import com.example.model.Mp3Info;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import java.util.List;
//读取xml文件并生成一个对象数组
public class Mp3ListContentHandler extends DefaultHandler {
private List<Mp3Info> infos = null;
public Mp3ListContentHandler(List<Mp3Info> infos) {
this.infos = infos;
}
private Mp3Info mp3Info = null;
private String tagName = null;
@Override
public void startDocument() throws SAXException {
super.startDocument();
}
@Override
public void endDocument() throws SAXException {
super.endDocument();
}
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
super.startElement(uri, localName, qName, attributes);
this.tagName = localName;
//以resource为节点设置成一个Mp3Info对象
if(tagName.equals("resource")){
mp3Info = new Mp3Info();
}
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
if(qName.equals("resource")){
infos.add(mp3Info);
}
tagName = "";
}
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
super.characters(ch, start, length);
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);
}
}
}
activity_main.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:orientation="vertical"
android:width="fill_parent"
android:height="wrap_content">
<!--suppress AndroidDomInspection -->
<ListView android:id="@id/android:list"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:drawSelectorOnTop="false"
android:scrollbars="vertical"/>
</LinearLayout>
</LinearLayout>
local_mp3_list_activity.xml
<TabHost
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="@+id/tabhost">
<LinearLayout android:orientation="vertical"
android:layout_width="fill_parent"
android:lauout_height="wrap_content"
android:padding="5dip">
<TabWidget
android:id="@+id/tabs"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
<FrameLayout
android:id="@+id/tabContent"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="5dip"/>
</LinearLayout>
</TabHost>
mp3_list_activity.xml
<TabHost
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="@+id/tabhost">
<LinearLayout android:orientation="vertical"
android:layout_width="fill_parent"
android:lauout_height="wrap_content"
android:padding="5dip">
<TabWidget
android:id="@+id/tabs"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
<FrameLayout
android:id="@+id/tabContent"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="5dip"/>
</LinearLayout>
</TabHost>
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"/>
</LinearLayout>
player.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="horizontal"
android:padding="10dip">
<ImageButton
android:id="@+id/begin"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/begin"/>
<ImageButton
android:id="@+id/pause"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/pause"/>
<ImageButton
android:id="@+id/stop"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/stop"/>
<TextView
android:id="@+id/lrcText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>