Android学习_Mars老师之Mp3视频开发

news2024/12/24 10:00:59

实现的功能:开发一个可以播放音乐并同步显示歌词文件的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>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/706495.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

信道编码:MATLAB使用卷积编译码函数

信道编码&#xff1a;MATLAB 使用Conv函数 1. 相关函数 在进行卷积编码的过程中&#xff0c;使用的函数是convenc()函数和vitdec()函数&#xff0c;同时需要poly2trellis()函数。 1.1 poly2trellis()函数 先看poly2trellis()函数,用来生成卷积编码所需要的网表。 trellis …

svn commit 用法

转载   原文&#xff1a;https://blog.csdn.net/qq_39790633/article/details/103700391 使用svn进行代码的提交有两种方法&#xff1a;一种是通过TortoiseSVN客户端界面进行提交&#xff0c;另一种是通过svn commit指令提交。 方法一&#xff1a;通过TortoiseSVN客户端界面提…

【Python 随练】文本颜色设置

题目&#xff1a; 文本颜色设置 简介&#xff1a; 在本篇博客中&#xff0c;我们将学习如何在Python中设置文本的颜色。我们将介绍一个常用的库&#xff0c;并提供代码示例来演示不同颜色的设置。 问题分析&#xff1a; 我们需要在Python中设置文本的颜色&#xff0c;以增…

3.2迷宫求解

首先我没 看懂数据结构书上写得迷宫 求解 不过 不重要了 迷宫求解 需要先有个 迷宫 游戏 以下 是 Java写的 控制台迷宫游戏 import java.util.Scanner; import java.util.regex.Matcher; import java.util.regex.Pattern;public class MazeGameJ {public static void main(St…

云原生|kubernetes|centos7下离线化部署kubesphere-3.3.2---基于kubernetes-1.22.16(从网络插件开始记录)

前言&#xff1a; kubesphere的离线化部署指的是通过自己搭建的harbor私有仓库拉取镜像&#xff0c;完全不依赖于外部网络的方式部署。 我的kubernetes集群是一个单master节点&#xff0c;双工作节点&#xff0c;总计三个节点的版本为1.22.16的集群。 该集群只是初始化完成了…

在Excel当前窗口显示最后一行数据

大家也许都知道Excel工作表中数据行数较多&#xff0c;使用<Ctrl下箭头>组合键可以快速定位最后一行&#xff0c;但是如果数据不是连续的&#xff08;也就是工作表中包含空行&#xff09;&#xff0c;这个方式就只能定位到当前连续数据区域的最后一行。 如下实例代码可以…

Visual Studio2019更改并下载.Net Framework目标框架

一、问题 当使用.net进行开发时&#xff0c;开发的项目与.net framework目标框架会非常密切相关的&#xff0c;所以当vs本地使用的.net framework框架与该项目工程的框架不一致的时候&#xff0c;就可能打开不了当前项目&#xff0c;解决这个问题的方法有&#xff1a; 第一种…

方向导数和梯度

理性认识的三个阶段&#xff1a;定义、判断、推理。 有位博主说过&#xff0c;数学中&#xff0c;定义占60%的内容。 方向导数定义如下&#xff1a; 注意的一点是&#xff1a; 该处的alpha&#xff0c;beta角度关系是alpha beta pi/2。t*cos alpha &#xff0c;t * cos …

两百行代码实现简易点云标注工具

夏天来了非常热&#xff0c;LZ周末不想出去玩&#xff0c;于是乎继之前的图片标注工具利用两个晚上写了一个简单的点云标注工具。该工具基于Qt5.14.2-msvc2017&#xff08;其实LZ的VS版本是2019&#xff0c;似乎兼容&#xff09;平台C语言开发&#xff0c;用到的第三方库为PCL1…

ModaHub魔搭社区:“百模大战”下,字节跳动选择做一个“大模型商场”

“火山方舟”面向企业提供模型精调、评测、推理等全方位的平台服务&#xff08;MaaS&#xff0c;即Model-as-a-Service&#xff09;&#xff0c;目前集成了百川智能、出门问问、复旦大学MOSS、IDEA研究院、澜舟科技、MiniMax、智谱AI等多家AI科技公司及科研团队的大模型。 这种…

通过elementui的el-table实现table嵌套展示

el-table属性&#xff1a; :expand-row-keys: 可以控制行的展开和关闭&#xff0c;也可以控制只能有一行进行展开 expand-change&#xff1a;是表格行的切换事件&#xff0c;具体方法如下 el-table-column属性&#xff1a; type"expand" &#xff1a;表示如果有子t…

从网上复制shell脚本 到 linux下运行 碰到的各种问题汇总

从网上复制shell脚本 到 linux下运行 碰到的各种问题汇总 快捷键CtrlU查看网页源码 一、报错现象&#xff1a;: No such file or directory 解决方法&#xff1a;在linux系统下执行&#xff1a;dos2unix filename 问题原因&#xff1a;本质是文件中二进制符号^M乱码问题 参考…

JAVA学习(七)

1. JAVA 多线程并发 1.1 JAVA 并发 并发知识库 1.2 JAVA 线程实现/创建 创建方式 1.2.1 承 继承 Thread 类 Thread 类本质上是实现了 Runnable 接口的一个实例&#xff0c;代表一个线程的实例。启动线程的唯一方 法就是通过 Thread 类的 start()实例方法。start()方法是一个…

c++实现httpd服务器

文章目录 网络的初始化端口的分配协议的使用WSAStartup函数参数1&#xff1a;WORD wVersionRequested参数2&#xff1a;LPWSADATA lpWSAData返回值 int代码实现 创建套接字int af表示套接字的类型(网络套接字 文件套接字)int type数据流 数据报int protocol 通信协议 TCP &…

网络空间安全数学基础考试要点

网络空间安全数学基础 阶的计算不要求那个公式&#xff0c;但是Order几次方要求 考试会考原根 Legendre必考 多项式计算必考 扩域多项式计算 同态不考 域元素表示 本元多项式不考 1.整除 3 ≡ \equiv ≡ 4 mod 7不对吧3 ≡ \equiv ≡ 3 mod 74 ≡ \equiv ≡ 4 &#xff08;m…

DITA技巧:给文字加颜色

- 1 - 场景 在文档中&#xff0c;我们有时候会在文字中使用颜色。 比如&#xff1a; 在文档中&#xff0c;使用在文字上加颜色来代表一定意义。使用MS Word编写文档的时候&#xff0c;直接在文字上加颜色就可以了。转换成DITA以后&#xff0c;大家会发现在XML编辑器的工具栏…

css实现大屏效果的背景div

实现大屏效果的背景div, 效果如下: html <div class"box">1111111</div>css .box {width: 200px;height: 80px;background: linear-gradient(270deg, #00cda2, #00cda2) 0 0 no-repeat,linear-gradient(180deg, #00cda2, #00cda2) 0 0 no-repeat,line…

JUC高并发编程-初篇(后续发布高阶篇)

JUC高并发编程 1.JUC概述 1.1 什么是JUC JUC就是java.util.concurrent工具包的简称。这是一个处理线程的工具包&#xff0c;JDK1.5开始出现的。 1.2 线程和进程概念 进程&#xff1a;指在系统中正在运行的一个应用程序&#xff1b;程序一旦运行就是进程&#xff1b;进程—…

数据结构--队列的基本概念

数据结构–队列的基本概念 队列的定义 队列其实是一种受限制的线性表 队列(Queue)&#xff1a;是 只允许在一端进行插入或删除操作 \color{red}只允许在一端进行插入或删除操作 只允许在一端进行插入或删除操作的线性表 重要术语: 队头、队尾、空队列 队列的特点: 先进先出 \…

表格检测识别技术面临的挑战和发展趋势

第四章 表格检测识别技术面临的挑战和发展趋势 现在表格区域检测的准确率已经很高了。但检测和识别是相辅相成的&#xff0c;单独的检测不够完善。如何利用检测和结构识别的结果互相提高效果&#xff0c;是未来的研究方向和重点。 由于表格应用场景较为广泛&#xff0c;表格形…