android音乐播放器之歌词下载、处理、开始、同步

android音乐播放器之歌词下载、处理、开始、同步

**

程序源代码在底部

**

先来看看效果

这里写图片描述

下载

/**
     * 自定义下载方法,调用系统DownloadManager下载
     * 
     * @param myUrl
     *            下载所需要的链接
     * @param path
     *            本地SDcard中的文件夹
     * @param fileName
     *            保存到文件名
     * @return void
     * */
    public void download(final String myUrl, final String path,
            final String fileName) {
        try {
            Runnable networkTask = new Runnable() {
                @Override
                public void run() {         
                    // 创建下载任务
                    DownloadManager.Request request = new DownloadManager.Request(
                            Uri.parse(myUrl));
                    // 设置为通知栏和下载界面皆不显示,需要添加权限DOWNLOAD_WITHOUT_NOTIFICATION

                    request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_HIDDEN);
                    request.setVisibleInDownloadsUi(false);

                    // sdcard的目录下的mymusicplayer文件夹
                    request.setDestinationInExternalPublicDir(path, fileName);
                    // 将下载请求加入下载队列
                    DownloadManager downloadManager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
                    // 加入下载队列后会给该任务返回一个long型的id,
                    long id = downloadManager.enqueue(request);

                    // 根据不同类型赋值id
                    if (isDownloadLrc) {
                        Log.i("下载歌词开始", String.valueOf(id));
                        Log.i("歌词地址", myUrl);
                        isDownloadLrc = false;
                        lrcReference = id;
                    } 
                }
            };

            // 开启一个子线程,进行网络操作
            new Thread(networkTask).start();
        } catch (Exception e) {
            e.printStackTrace();        
        }
    }

下载完成后处理:

    /**
     * 初始化下载等待,下载任务完进行处理
     * 
     * @return void
     * */
    private void initDownload() {
        IntentFilter filter = new IntentFilter(
                DownloadManager.ACTION_DOWNLOAD_COMPLETE);
        receiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                long reference = intent.getLongExtra(
                        DownloadManager.EXTRA_DOWNLOAD_ID, -1);
                downloadSuccess(reference);
            }
        };
        registerReceiver(receiver, filter);
    }

    /**
     * 下载完成处理
     * 
     * @param reference
     *            下载序号id
     * */
    public void downloadSuccess(long reference) {
        try {
            if (reference == lrcReference) { // 处理歌词
                Log.i("歌词下载完", String.valueOf(reference));
                File file = new File(LrcPath);
                if (!file.exists()) { // 本地文件不存在
                    return;
                }
                initLrc(LrcPath);
                int temp = mMediaPlayer01.getCurrentPosition();
                currentIndex = findIndexOfTime(temp);
                lrcExits = true;
            } 
        } catch (RuntimeException e) {
        }
    }

我调用了系统的DownloadManager下载,之前是自己写的,但有时候会出错,感觉系统这个下载也挺好用的,这函数用来下载其他文件也可以。定义一个广播接收器接收,下载完后,就进行处理.

接下来是处理,读取Lrc文件,这是一个类,在别人的基础上修改的,文末给上链接,希望大神莫怪

package com.example.musicplayer;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import android.util.Log;

/**
 * 歌词处理
 * */
public class LrcRead {
    private List<Map<String, Object>> word_Time_List = new ArrayList<Map<String, Object>>();

    /**
     * 处理歌词文件
     * 
     * @param path
     *            歌词路径
     * @return void  throws IndexOutOfBoundsException
     * */
    public void readLRC(String path) throws IndexOutOfBoundsException{
        File file = new File(path);

        try {
            FileInputStream fileInputStream = new FileInputStream(file);
            //这里的编码,我用utf-8,全部乱码,用gb2312和gbk中英文不会乱码,不知道咋滴
            InputStreamReader inputStreamReader = new InputStreamReader(
                    fileInputStream, "gbk");
            BufferedReader bufferedReader = new BufferedReader(
                    inputStreamReader);
            String s = "";
            //读取歌词
            while ((s = bufferedReader.readLine()) != null) {                
                int time = addTimeToList(s); //添加歌词时间间                  
                if(s.length()==0 || time == -1)
                    continue;
                if ((s.indexOf("[ar:") != -1) || (s.indexOf("[ti:") != -1)
                        || (s.indexOf("[by:") != -1)) {
                    s = s.substring(s.indexOf(":") + 1, s.indexOf("]"));                    
                } else {    
                    String ss = s.substring(s.indexOf("["), s.indexOf("]") + 1);                    
                    s = s.replace(ss, "");                  
                }

                Map<String, Object> map4 = new HashMap<String, Object>();
                map4.put("time", time);
                map4.put("word", s);
                word_Time_List.add(map4);

            }
            bufferedReader.close();
            inputStreamReader.close();
            fileInputStream.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();            
        } catch (IOException e) {
            e.printStackTrace();
        } catch (IndexOutOfBoundsException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取歌词+时间列表
     * 
     * @return 所有歌词
     * */
    public List<Map<String, Object>> getWordTimeList() {
        return word_Time_List;
    }

    /**
     * 分离出时间
     * 
     * @param string
     *            时间
     * @return 毫秒
     * */
    private int timeHandler(String string) {
        string = string.replace(".", ":");
        String timeData[] = string.split(":");
        // 分离出分、秒并转换为整型
        int minute = Integer.parseInt(timeData[0]);
        int second = Integer.parseInt(timeData[1]);
        int millisecond = Integer.parseInt(timeData[2]);

        // 计算上一行与下一行的时间转换为毫秒数
        int currentTime = (minute * 60 + second) * 1000 + millisecond * 10;

        return currentTime;
    }

    /**
     * 添加时间到列表
     */
    private int addTimeToList(String string) {
        int t = -1;
        Matcher matcher = Pattern.compile(
                "\\[\\d{1,2}:\\d{1,2}([\\.:]\\d{1,2})?\\]").matcher(string);
        if (matcher.find()) {
            String str = matcher.group();   
            t = timeHandler(str.substring(1, str.length() - 1));
        }

        return t;

    }

    /**
     * 清空列表
     */
    public void reset(){
        word_Time_List.clear();
    }
}

读取歌词时,原文是把歌词和时间文件分开成两个列表的,这样做,在歌词同步的时候挺麻烦的,特别是有些歌词,前面几行是作者、歌名、广告等,又没标注时间,导致两个列表长度也不一致,同步时添加了难度,所以我把他们合并在一起了。

调用方法:

    /**
     * 初始化歌词,调用LrcRead类
     * 
     * @param LrcPath
     *            歌词路径
     * 
     * @return void
     */
    public void initLrc(String LrcPath) {
        try {
            lrcHandler.reset(); // 先清空原有的数据
            lrcHandler.readLRC(LrcPath);
            word_Time_List = lrcHandler.getWordTimeList();
            tvCurrentWord.setText("歌词准备完毕");
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (IllegalStateException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

歌词的同步

/**
     * 开始或继续SeekBar
     * 
     * @return void
     * */
    private void startSeekBar() {
        seekBar.setEnabled(true);
        try {
            mRunnable = new Runnable() {
                public void run() {
                    // 每3秒执行一次,执行进度条
                    mHandler.postDelayed(mRunnable, 100); // 给自己发送消息,自运行
                    int temp = mMediaPlayer01.getCurrentPosition();
                    seekBar.setProgress(temp);
                    String time = formatTime(temp); // 格式化时间
                    tvCurrentTime.setText(time);

                    //歌词同步
                    if (lrcExits) { // 歌词存在
                        // 防止下标越界

                        if (currentIndex != word_Time_List.size()) {
                            int lrcTime = Integer.parseInt(word_Time_List
                                    .get(currentIndex).get("time").toString());
                            // 如果当前播放时间和歌词时间在一定范围,这里设置0到200毫秒,切换歌词
                            if (lrcTime >= temp && lrcTime <= temp + 200) {
                                String Currentword = word_Time_List
                                        .get(currentIndex).get("word")
                                        .toString();
                                String nextWord = null;
                                if (currentIndex + 1 != word_Time_List.size() - 1)
                                    nextWord = word_Time_List
                                            .get(currentIndex + 1).get("word")
                                            .toString();
                                else
                                    lrcExits = false;
                                tvCurrentWord.setText(Currentword);
                                tvNextWord.setText(nextWord);//写到textview上
                                currentIndex++;

                                if (currentIndex > word_Time_List.size() - 1)
                                    lrcExits = false;
                            }
                        }
                    }
                }
            };
            // 通过Handler启动线程
            mHandler.post(mRunnable); // 发送消息,启动线程运行
        } catch (Exception e) {
        }
    }

歌词的同步,获取刚刚解析到的歌词列表中的时间,与当前播放的时间比较,相差200ms,就把当前歌词和下一句替换上去两个textview。歌词同步还有一个方法比较重要的:

/**
     * 在mTimeList中查找歌词序号
     * 
     * @param targetTime
     *            当前播放的时间
     * @return index 当前时间附近的歌词序号
     */
    public int findIndexOfTime(int targetTime) {
        int index = 0;
        int currentTime;
        if (word_Time_List.size() <= 1)
            return 1;
        for (int i = 0; i < word_Time_List.size() - 1; i++) {
            currentTime = Integer.parseInt(word_Time_List.get(i).get("time")
                    .toString());
            if (targetTime <= currentTime) {  //列表时间大于当前时间
                index = i;
                break;
            }
        }

        return index;
    }

该方法时遍历歌词列表的时间,如果列表时间大于当前播放时间,就把这个序号返回去,在拉动播放进度条时调用这个同步歌词,歌词需要下载时也调用,毕竟下载和处理歌词也是需要时间的,有一些歌曲一开始就播放了,所以需要跳过已经播放的歌词。

调用方法:

int temp = mMediaPlayer01.getCurrentPosition();
String time = formatTime(temp);  //把获取到的时间转换一下,转换成毫秒的字符串

currentIndex = findIndexOfTime(temp);  //currentIndex 为当前歌词列表的序号,int,全局变量

(函数非原创,忘了从哪里拿来改的了)
formatTime():

/**
     * 时间转换
     * 
     * @param ms
     *            需要转化的时间,单位毫秒
     * @return String 返回00:00格式的分秒时间
     */
    public static String formatTime(int ms) {
        int ss = 1000; // 秒
        int mi = ss * 60; // 分

        int minute = ms / mi;// 计算分
        int second = (ms - minute * mi) / ss; // 计算秒

        StringBuffer sb = new StringBuffer();
        if (minute > 0) {
            sb.append(minute + ":");
        } else {
            sb.append("00:");
        }
        if (second > 0 && second <= 9) {
            sb.append("0" + second);
        } else if (second > 9) {
            sb.append(second);
        } else {
            sb.append("00");
        }
        return sb.toString();
    }

程序源代码-MusicPlayer

歌词处理类来源:http://www.cnblogs.com/wenjiang/archive/2013/05/06/3063259.html

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值