代码已经托管到码云上,有兴趣的小伙伴可以下载看看

        https://git.oschina.net/joy_yuan/MobilePlayer

 一 EventBus 3.0   ---利用eventbus代替广播来获取音乐的数据。

    EventBus是一款针对Android优化的发布/订阅事件总线。主要功能是替代Intent,Handler,BroadCast在Fragment,Activity,Service,线程之间传递消息.优点是开销小,代码更优雅。以及将发送者和接收者解耦。

        1、下载EventBus的类库
                源码:https://github.com/greenrobot/EventBus

            在Android Studio里使用EventBus 的话,只需要在build.gradle里加入下面这句,然后sync一下即可。

compile 'org.greenrobot:eventbus:3.0.0'

        2、EventBus 的用法

            a 、注册EventBus,在需要订阅eventbus的activity中,注册eventbus即可

                如在AudioPlayerActivity中的onCreate里注册

                EventBus.getDefault().register(this);

            b、取消注册。在onDestroy里取消注册eventbus

                EventBus.getDefault.unregister(this);

            c、订阅事件

                  在Activity里订阅事件,当发布者发布相关的事件后,即可在此接收到

            这里要注意的是,订阅的方法,一定是public的,然后上面用注解说明订阅事件在哪个线程执行,以及优先级priority,,这个优先级类似有序广播的优先级。

/**
 * 订阅eventbus
 */
@Subscribe(threadMode=ThreadMode.MAIN,sticky = false,priority = 99)
public void showData(MediaItem item) {
    showViewData();
    checkPlayMode();
}

        d、发布事件.

            在AudioPlayerService里的准备播放音乐时,发布事件,将要播放的音乐的对象传过去,那么activity里订阅了该信息的即可接受到        

    /**
     * 准备好播放时回调
     */
    class MyOnPreparedListener implements MediaPlayer.OnPreparedListener {
        @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
        @Override
        public void onPrepared(MediaPlayer mp) {
           startAudio();
            //在这里发送广播,通知activity,播放的进度、音乐名称、歌唱家等信息
//            notifyChange(OPENAUDIOPLAYER);
            
            //EventBus发布信息
            EventBus.getDefault().post(item);
        }
    }



二、显示歌词

        1、布局修改。

                先在音乐播放页面的布局中,添加一个textview控件来显示歌词区域

            activity_audioplayer.xml, 这里textview用的是自定义的类,下面会讲解


当前的歌词部分的效果图如下,因为还没有具体的显示的歌词,只是做了个测试的歌词,实际的歌词也是照着这个做的。

手机影音第十六天,集成eventbus代替广播 ;在音乐播放页面中间部分显示歌词_第1张图片

    




    

        


        

        
    

    

        

        


        

            


2、自定义歌词的实体类

        其实根据现有的歌词文件,分析下其构成,可以看到,没行歌词前都有个时间,表示这句歌词在哪个时间点会唱,然后时间点后面是歌词内容

    

[00:08.17]歌曲名:北京北京
[00:15.00]演唱:汪峰
[00:23.84]www.666cc.com
[00:31.16]当我走在这里的每一条街道
[00:37.32]我的心似乎从来都不能平静
[00:45.23]就让花朵妒忌红名和电气致意
[00:51.66]我似乎听到了他这不慢的心跳
[00:59.74]我在这里欢笑我在这里哭泣
[01:06.93]我在这里活着也在这死去
[01:14.09]我在这里祈祷 我在这里迷惘
[01:21.25]我在这里寻找 在这里寻求
[04:11.76][04:04.59][02:31.70][01:27.19]北京 北京

    据此我们可以定义歌词类Lyrc.java,其又3个属性,

            String content; 歌词内容。

             String long timePosition;  歌词显示的时间段

            String long sleepTime ;      每句歌词都有一个高亮的时间,这个就是代表此


package com.yuanlp.mobileplayer.bean;

/**
 * Created by 原立鹏 on 2017/7/30.
 *
 * 歌词类
 * 例如一句歌词
 * [02:21.35]我在这里寻找
 */

public class Lyrc {

    //一句歌词由时间点+歌词内容组成

    /**
     * 歌词内容
     */
    private String content;

    /**
     * 时间点
     */
    private long timePosition;

    /**
     * 高亮显示时间

     */
    private long sleepTime;


    public long getSleepTime() {
        return sleepTime;
    }

    public void setSleepTime(long sleepTime) {
        this.sleepTime = sleepTime;
    }




    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public long getTimePosition() {
        return timePosition;
    }

    public void setTimePosition(long timePosition) {
        this.timePosition = timePosition;
    }

    @Override
    public String toString() {
        return "Lyrc{" +
                "content='" + content + '\'' +
                ", timePosition=" + timePosition +
                ", sleepTime=" + sleepTime +
                '}';
    }
}


3、自定的歌词显示view,继承自textview。

        在这里先定义了一个假的歌词,不是真正的歌曲的歌词,只是用来示例

        先定义一个ArrayList lyrcs;

        然后通过for循环,往list里插入数据。

        

for (int i=0;i<1000;i++){
    Lyrc lyrc=new Lyrc();

    lyrc.setTimePosition(i*1000);
    lyrc.setContent("aaaaaa"+i);
    lyrc.setSleepTime(1500+i);

    lyrcs.add(lyrc);
}


    数据完成后,开始绘制歌词,在此先定义2个画笔,主要是绘制高亮的歌词的一个画笔,一个绘制其他歌词的画笔,除了颜色不同外,其他都一样。

    

//创建画笔----当前高亮的画笔
paint=new Paint();
paint.setColor(Color.GREEN); //高亮颜色
paint.setTextSize(20);
paint.setAntiAlias(true); //抗锯齿
paint.setTextAlign(Paint.Align.CENTER);  //对齐方式,居中显示

//白色画笔
whitepaint=new Paint();
whitepaint.setColor(Color.WHITE);
whitepaint.setTextAlign(Paint.Align.CENTER);
whitepaint.setTextSize(20);
whitepaint.setAntiAlias(true);


    在绘制歌词时,先绘制中间的高亮的歌词,因为这个歌词的位置确定时在布局的中间位置;绘制完成高亮的后,在绘制高亮歌词的上边的歌词,下面的歌词,每句歌词高度20.  

if (lyrcs!=null&&lyrcs.size()>0){
    //先绘制当前歌词
    String currentContent=lyrcs.get(index).getContent();
    canvas.drawText(currentContent,getWidth()/2,getHeight()/2,paint);  //开始绘制该句歌词

    //绘制前面部分歌词
    int tempY=getHeight()/2;  //当前高亮歌词的Y轴坐标
    for (int i=index-1;i>=0;i--){  //循环来得到每句上面歌词的Y轴坐标
        String preContent=lyrcs.get(i).getContent();
        tempY=tempY-textHeight;   //循环来得到每句上面歌词的Y轴坐标

        if (tempY<0){  //当最上面的一行歌词已经隐藏了,不显示,那么就不再处理
            break;
        }
        
        canvas.drawText(preContent,getWidth()/2,tempY,whitepaint);  
    }

    //绘制后面部分的歌词
    tempY=getHeight()/2;
    for (int i=index+1;igetHeight()){  //超出控件的高度,就不处理
            break;
        }
        canvas.drawText(nextContent,getWidth()/2,tempY,whitepaint);
    }

}else {
    canvas.drawText("没有歌词",getWidth()/2,getHeight()/2,paint);
}

    通过获取当前歌曲播放进度,对比每句歌词的时间戳,来高亮显示哪句歌词

    

public void setShowNextLyrc(int currentPosition) {
    this.currentPosition=currentPosition;
    if (lyrcs==null&&lyrcs.size()==0){
        return;
    }else{
        for (int i=1;i=lyrcs.get(tempIndex).getTimePosition()){
                    //当前正在播放的歌词
                    index=tempIndex;
                    sleepTime = lyrcs.get(index).getSleepTime();  //歌词的休眠时间,即高亮时间
                    timePosition = lyrcs.get(index).getTimePosition(); //歌词的时间戳
                }
            }
        }
    }

    //重新绘制,在主线程执行
    invalidate();
}

具体的这个类的代码如下:

    ShowlyrcView.java

package com.yuanlp.mobileplayer.view;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.support.annotation.Nullable;
import android.util.AttributeSet;

import com.yuanlp.mobileplayer.bean.Lyrc;

import java.util.ArrayList;

/**
 * Created by 原立鹏 on 2017/7/30.
 *
 * 自定义歌词显示控件
 */

public class ShowlyrcView extends android.support.v7.widget.AppCompatTextView {
    private ArrayList lyrcs;
    private Paint paint;  //当前显示的歌词的画笔
    private Paint whitepaint;  //白色画笔,用来绘制不是当前高亮的部分

    private int width;  //控件的宽
    private int height;  //控件的高

    private int index;  //当前歌词的索引
    private int textHeight=20;  //每行歌词的高度

    /**
     * 设置歌词列表
     * @param lyrcs
     */
    public void setLyrcs(ArrayList lyrcs){
        this.lyrcs=lyrcs;
    }

    public ShowlyrcView(Context context) {
        this(context,null);
    }

    public ShowlyrcView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs,0);
    }

    public ShowlyrcView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        initView();
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        width=w;
        height=h;
    }

    private void initView() {

        //创建画笔----当前高亮的画笔
        paint=new Paint();
        paint.setColor(Color.GREEN); //高亮颜色
        paint.setTextSize(20);
        paint.setAntiAlias(true); //抗锯齿
        paint.setTextAlign(Paint.Align.CENTER);  //对齐方式,居中显示

        //白色画笔
        whitepaint=new Paint();
        whitepaint.setColor(Color.WHITE);
        whitepaint.setTextAlign(Paint.Align.CENTER);
        whitepaint.setTextSize(20);
        whitepaint.setAntiAlias(true);

        /**
         * 暂时先设置一个假的歌词列表
         */
        lyrcs=new ArrayList<>();


        for (int i=0;i<1000;i++){
            Lyrc lyrc=new Lyrc();

            lyrc.setTimePosition(i*1000);
            lyrc.setContent("aaaaaa"+i);
            lyrc.setSleepTime(1500+i);

            lyrcs.add(lyrc);
        }
    }


    //绘制歌词
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (lyrcs!=null&&lyrcs.size()>0){
            //先绘制当前歌词
            String currentContent=lyrcs.get(index).getContent();
            canvas.drawText(currentContent,getWidth()/2,getHeight()/2,paint);

            //绘制前面部分歌词
            int tempY=getHeight()/2;  //当前高亮歌词的Y轴坐标
            for (int i=index-1;i>=0;i--){
                String preContent=lyrcs.get(i).getContent();
                tempY=tempY-textHeight;

                if (tempY<0){  //当最上面的一行歌词已经隐藏了,不显示,那么就不再处理
                    break;
                }
                canvas.drawText(preContent,getWidth()/2,tempY,whitepaint);
            }

            //绘制后面部分的歌词
            tempY=getHeight()/2;
            for (int i=index+1;igetHeight()){  //超出控件的高度,就不处理
                    break;
                }
                canvas.drawText(nextContent,getWidth()/2,tempY,whitepaint);
            }

        }else {
            canvas.drawText("没有歌词",getWidth()/2,getHeight()/2,paint);
        }
    }
    
 public void setShowNextLyrc(int currentPosition) {
    this.currentPosition=currentPosition;
    if (lyrcs==null&&lyrcs.size()==0){
        return;
    }else{
        for (int i=1;i=lyrcs.get(tempIndex).getTimePosition()){
                    //当前正在播放的歌词
                    index=tempIndex;
                    sleepTime = lyrcs.get(index).getSleepTime();
                    timePosition = lyrcs.get(index).getTimePosition();
                }
            }
        }
    }

    //重新绘制,在主线程执行
    invalidate();
}
}


四、更新歌词

        在AudioPlayerActivity中的接收到eventbus的订阅事件里,发一个handler消息,来更新歌词

/**
 * 订阅eventbus
 */
@Subscribe(threadMode=ThreadMode.MAIN,sticky = false,priority = 99)
public void showData(MediaItem item) {
    handler.sendEmptyMessage(SHOW_LYRC);
    showViewData();
    checkPlayMode();
}

        然后在handler里来处理更新歌词

    

case SHOW_LYRC:

    //1、获取当前进度
    try {
        int currentPosition=mservice.getCurrentPosition();
        //2、根据当前进度,获取歌词
        showlyrcView.setShowNextLyrc(currentPosition);
        //3、实时发消息去更新歌词
        handler.removeMessages(SHOW_LYRC);
        handler.sendEmptyMessage(SHOW_LYRC);
    } catch (RemoteException e) {
        e.printStackTrace();
    }


    break;