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

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)@Overridepublic void onPrepared(MediaPlayer mp) {startAudio();//在这里发送广播,通知activity,播放的进度、音乐名称、歌唱家等信息
//            notifyChange(OPENAUDIOPLAYER);//EventBus发布信息EventBus.getDefault().post(item);}}

二、显示歌词

1、布局修改。

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

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

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

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:background="@drawable/base_bg"android:layout_width="match_parent"android:layout_height="match_parent"><RelativeLayoutandroid:id="@+id/rl_top"android:layout_width="match_parent"android:layout_height="wrap_content"><ImageViewandroid:id="@+id/iv_play_icon"android:src="@drawable/now_playing_matrix_01"android:layout_centerHorizontal="true"android:layout_width="wrap_content"android:layout_height="wrap_content"/><TextViewandroid:layout_below="@+id/iv_play_icon"android:id="@+id/tv_signer"android:textSize="14sp"android:layout_centerHorizontal="true"android:textColor="#ffffff"android:text="演唱者"android:layout_width="wrap_content"android:layout_height="wrap_content"/><TextViewandroid:id="@+id/tv_song"android:layout_marginTop="5dp"android:layout_below="@+id/tv_signer"android:layout_centerHorizontal="true"android:textSize="14sp"android:textColor="#ffffff"android:text="吻别"android:layout_width="wrap_content"android:layout_height="wrap_content"/></RelativeLayout><LinearLayoutandroid:id="@+id/ll_bottom"android:layout_marginBottom="5dp"android:layout_alignParentBottom="true"android:orientation="vertical"android:layout_width="match_parent"android:layout_height="wrap_content"><TextViewandroid:id="@+id/tv_time"android:textSize="14sp"android:layout_gravity="right"android:textColor="@android:color/white"android:text="00:00/30:00"android:layout_width="wrap_content"android:layout_height="wrap_content"/><SeekBarandroid:id="@+id/seekbar_audio"android:layout_marginLeft="8dp"android:layout_marginRight="8dp"android:maxHeight="6dp"android:minHeight="6dp"android:progressDrawable="@drawable/audio_progress_horizontal"android:thumb="@drawable/seek_thumb"android:layout_width="match_parent"android:layout_height="wrap_content"/><LinearLayoutandroid:gravity="center_vertical"android:orientation="horizontal"android:layout_width="match_parent"android:layout_height="wrap_content"><Buttonandroid:layout_weight="1"android:id="@+id/bt_audio_mode"android:background="@drawable/bt_audio_mode_normal_selector"android:layout_width="wrap_content"android:layout_height="wrap_content"/><Buttonandroid:layout_weight="1"android:id="@+id/bt_audio_pre"android:background="@drawable/bt_audio_pre_selector"android:layout_width="wrap_content"android:layout_height="wrap_content"/><Buttonandroid:layout_weight="1"android:id="@+id/bt_audio_start_pause"android:background="@drawable/bt_audio_pause_selector"android:layout_width="wrap_content"android:layout_height="wrap_content"/><Buttonandroid:layout_weight="1"android:id="@+id/bt_audio_next"android:background="@drawable/bt_audio_next_selector"android:layout_width="wrap_content"android:layout_height="wrap_content"/><Buttonandroid:layout_weight="1"android:id="@+id/bt_lyrc"android:background="@drawable/bt_lyrc_selector"android:layout_width="wrap_content"android:layout_height="wrap_content"/></LinearLayout></LinearLayout><com.yuanlp.mobileplayer.view.ShowlyrcViewandroid:id="@+id/showlyrcView"android:layout_below="@+id/rl_top"android:layout_above="@+id/ll_bottom"android:layout_width="match_parent"android:layout_height="match_parent"/></RelativeLayout>

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;}@Overridepublic String toString() {return "Lyrc{" +"content='" + content + '\'' +", timePosition=" + timePosition +", sleepTime=" + sleepTime +'}';}
}

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

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

先定义一个ArrayList<Lyrc> 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;i<lyrcs.size();i++){String nextContent=lyrcs.get(i).getContent();tempY=tempY+textHeight;if (tempY>getHeight()){  //超出控件的高度,就不处理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.size();i++){if (currentPosition<lyrcs.get(i).getTimePosition()){int tempIndex=i-1;if (currentPosition>=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<Lyrc> 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<Lyrc> 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();}@Overrideprotected 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);}}//绘制歌词@Overrideprotected 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;i<lyrcs.size();i++){String nextContent=lyrcs.get(i).getContent();tempY=tempY+textHeight;if (tempY>getHeight()){  //超出控件的高度,就不处理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.size();i++){if (currentPosition<lyrcs.get(i).getTimePosition()){int tempIndex=i-1;if (currentPosition>=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;

转载于:https://blog.51cto.com/cm0425/1952143

手机影音第十六天,集成eventbus代替广播 ;在音乐播放页面中间部分显示歌词相关推荐

  1. 手机影音第十二天,集成vitamio万能播放器

    Vitamio 是一款 Android 与 iOS 平台上的全能多媒体开发框架,全面支持硬件解码与 GPU 渲染.Vitamio 凭借其简洁易用的 API 接口赢得了全球众多开发者的青睐.到目前,全球 ...

  2. android 播放器集成,手机影音第十二天,集成vitamio万能播放器

    Vitamio 是一款 Android 与 iOS 平台上的全能多媒体开发框架,全面支持硬件解码与 GPU 渲染.Vitamio 凭借其简洁易用的 API 接口赢得了全球众多开发者的青睐.到目前,全球 ...

  3. 仿手机酷狗-音乐播放页面拖动效果

    第一次写博客,希望内容大家能喜欢. 今天要写的内容是仿酷狗音乐播放页面.在最新版的酷狗里,当手指向右移动时,页面会旋转,并将上一个页面的内容显示出来. 先上效果图 下面上代码 public class ...

  4. Android视频《手机影音_项目实战》-杨光福-专题视频课程

    Android视频<手机影音_项目实战>-49877人已学习 课程介绍         手机影音项目是真实的上线项目,本视频在原项目基础上,进行了新技术的更新和优化.该项目包括本地音乐播放 ...

  5. 基于EasyNVR二次开发实现业务需求:直接集成EasyNVR播放页面到自身项目

    EasyNVR着重点是立足于视频能力层,但是自身也是可以作为一个产品使用的.这就更加方便了应用层的使用. 由于业务需求的缘故,无法使用实体项目展示. 案例描述 该业务系统是国内某大型显示屏生产企业内部 ...

  6. Java学习 第十六天(泛型)

    Java学习 第十六天---泛型与集合 第一章 泛型 1.1 泛型概述 1.2 泛型类 1.3 泛型类练习---抽奖问题 1.4 泛型类的子类 1.5 泛型接口 1.6 重写泛型父类或泛型接口中方法的 ...

  7. Android 手机影音 开发过程记录(六)

    Android 手机影音 开发过程记录(六) 前一篇已经将音乐播放及切换的相关逻辑弄好了,今天主要理一下剩余的部分,包含: 1. 自己定义通知栏的布局及逻辑处理 2. 滚动歌词的绘制 3. 歌词解析 ...

  8. 自学Python第二十六天- Tornado 框架

    自学Python第二十六天- Tornado 框架 安装及基础引用 创建.配置.初始化应用及简单运行服务 创建应用 对 app 进行设置 另一种设置方法 一些其他的配置 关于调试模式 设置路由处理器 ...

  9. 手把手教你搭建SpringCloud项目(十)集成Hystrix之服务降级

    Spring Cloud全集文章目录: 零.什么是微服务?一看就会系列! 一.手把手教你搭建SpringCloud项目(一)图文详解,傻瓜式操作 二.手把手教你搭建SpringCloud项目(二)生产 ...

  10. 大白话5分钟带你走进人工智能-第二十八节集成学习之随机森林概念介绍(1)

                                                          第二十八节集成学习之随机森林概念介绍(1) 从本系列开始,我们讲解一个新的算法系列集成学习. ...

最新文章

  1. 史上最全的 Linux Shell 文本处理工具集锦,快收藏!
  2. 2020 新兴技术炒作周期曲线:这 5 个趋势值得注意
  3. 我理解的javascript事件循环(一)
  4. protobuf java基础
  5. 清北·NOIP2017济南考前冲刺班 DAY1 morning
  6. linux-buff/cache过大导致内存不足-程序异常
  7. 11月16日云栖精选夜读:阿里云 oss JavaScript客户端签名文件上传 vue2.0
  8. fd在python什么意思_python用法总结
  9. alphazawgyiversion3 安装口令_车窗晴雨挡到底有着什么用?为什么那么多人安装?酷斯特玩车...
  10. C++新特性探究(十八):智能指针
  11. poj1161Post Office【经典dp】
  12. 45个非常有用的Oracle查询语句(转自开源中国社区)
  13. android view绘制速度,关于android ui的优化 view 的绘制速度
  14. 4.3配置自定义情况的Bean实例
  15. 服务器 16路直连 英特尔,Intel 10nm服务器怪咖:八通道+16条内存
  16. BZOJ1934[SHOI2007] Vote 善意的投票
  17. ssm-学子商城-项目第十一天
  18. 快递行业面单打印解决方案-快宝云打印
  19. Matlab存tif格式图片
  20. shp2sdo的下载及使用说明

热门文章

  1. 软工网络15团队作业4——Alpha阶段敏捷冲刺之Scrum 冲刺博客(Day1)
  2. python documents in chinese_python xlwt 设置 格式
  3. 总结使用Unity 3D优化游戏运行性能的经验
  4. 【IT之路】Docker系列-CentOS 7 64位镜像下载
  5. three.js学习笔记(十四)——Shaders着色器
  6. KVM虚拟机扩容硬盘
  7. 在eclipse部署OpenBravo项目
  8. 三国志战略版:魏国新黑科技,程昱春华满宠
  9. 免费在线客服 livezilla 中文版,安装+使用教程
  10. python counter转换为列表_Python标准库---collections.counter