一个卡拉OK效果的自定义歌词控件
上图就是效果。
背景和字体颜色可以自己调,都定义成了常量并有说明,直接更改即可。
可以自适应不同分别率。
可以自己将其改成根据屏幕高度动态决定显示几行。只需要调整ondraw就好了。
package com.godyde.musicplayer;/** * 显示歌词的自定义控件。 * 核心思想就是根据Mediaplayer.getCurrentPosition()返回的当前进度 * 从歌词中查找当前进度对应的行mCount。以及当前行进行的百分比mPercent * 然后根据mCount 来决定打印哪些行[mCount-6,mCount+6 * 每行歌词打印的基线会随着mPercent的增加而上移(mPercent/100)*mLineHeight. * 第一行的透明度会随着mPercent的增加而增加形成淡出效果。 * 第十三行的透明度会随着mPercent的增加而减少形成淡入效果。 * 当前进度对应的行为第7行。第7行的效果来自于渐变效果。只是将渐变设置成了类似突变的效果。 * 突变的位置会随着mPercent的增加而右移。形成卡拉OK效果。 * <p/> * 几个未完成的事情。 * 2.当歌词单行过长时未分行。会导致部分歌词行显示不完全。(TODO 根据字符串长度和控件宽度来分行显示) * 3.未实现从文件加载。直接加载的项目内资源。(TODO 使用Uri来加载歌词) * 4.是否考虑使用surfaceview * * V0.2 :已解决1,2, * @author 易登 * @version V0.2 06/12/2016 */ import android.content.Context; import android.graphics.Canvas; import android.graphics.LinearGradient; import android.graphics.Paint; import android.graphics.Shader; import android.media.MediaPlayer; import android.util.AttributeSet; import android.view.View;import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream;public class LrcTextView extends View {//歌词显示的基本颜色 static final int BASECOLOR = 0xFFEEEEEE;//歌词显示的卡拉OK效果颜色 static final int COVERCOLOR = 0xFF39DF7C;static final int BACKGROUNDCOLOR=0xFF201633;//字体大小 static final float MAXTEXTSIZE = 48.0f;static final float MINTEXTSIZE = 8f;//歌词刷新频率 每秒25帧 static final long REFRESHRATE = 25;//歌词字体大小 private float mTextSize;//每行歌词占用的高度 private int mLineHeight = 100;//歌词最多的一行。所包含的字符数。 private int mMaxLrcLine;//画笔 private Paint mPaint;//歌词字符串数组。从lrc文件提取 private String mLrc[];//显示歌词的时间索引。从lrc文件提取 private int mLrcTime[];//播放歌曲的播放器(由start函数传入) private MediaPlayer mMediaPlayer;//当前播放进度对应的歌词行 private int mCount;//当前播放进度对应的歌词当前行的百分比 0-100 private float mPercent;public LrcTextView(Context context) {super(context);initPaint();}public LrcTextView(Context context, AttributeSet attrs) {super(context, attrs);initPaint();}public LrcTextView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);initPaint();}@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {//偷懒的做法。控件总是从宽度上铺满父控件。高度取800和父控件高度中的较小值。 setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), Math.min(widthMeasureSpec, MeasureSpec.getSize(heightMeasureSpec)));}@Override protected void onDraw(Canvas canvas) {if (mMediaPlayer != null) {//获取当前的进度 getCountAndPrecent();//循环打印七行歌词 setTextSize(canvas);for (int i = 0; i < 13; i++) {//计算需要打印的歌词所在行 int line = mCount - 6 + i;//这个判断是在歌曲开始和结束时,防止越界。小于0 和大于等于mLrc.length无需打印 if (line >= 0 && line < mLrc.length) {//最上面一行歌词淡出效果 if (i == 0) {//根据mPercent设置透明度 mPaint.setAlpha((int) ((1 - mPercent / 100) * 255));//打印歌词 canvas.drawText(mLrc[line], mTextSize, (i + 1 - mPercent / 100) * mLineHeight, mPaint);//恢复成不透明。 mPaint.setAlpha(255);} else if (i == 6) { //当前正在播放的歌词 实现卡拉OK效果 //获取当前行歌词的显示宽度 float mTextWidth = mPaint.measureText(mLrc[mCount]);//创建一个类似突变的渐变效果 。变化节点根据 mPercent设置。 LinearGradient shader = new LinearGradient(0, 0,mTextSize*2+mTextWidth * mPercent / 100, 0,new int[]{COVERCOLOR, BASECOLOR}, new float[]{0.99f, 0.01f},Shader.TileMode.CLAMP);//将突变效果设置给画笔。 mPaint.setShader(shader);//使用该画笔画出当前歌词。 canvas.drawText(mLrc[line], mTextSize, (i + 1 - mPercent / 100) * mLineHeight, mPaint);//恢复画笔属性。 //mPaint.setColor(BASECOLOR); mPaint.setShader(null);} else if (i == 12) {//最下面一行歌词实现淡入效果 //根据mPercent设置透明度 mPaint.setAlpha((int) ((mPercent / 100) * 255));//打印歌词 canvas.drawText(mLrc[line],mTextSize, (i + 1 - mPercent / 100) * mLineHeight, mPaint);//恢复画笔效果 mPaint.setAlpha(255);} else {//打印其他行歌词。 canvas.drawText(mLrc[line], mTextSize, (i + 1 - mPercent / 100) * mLineHeight, mPaint);}}}//根据刷新频率设置下一次刷新时间 postInvalidateDelayed(1000 / REFRESHRATE);} else super.onDraw(canvas);}/** * 初始化画笔属性 */ private void initPaint() {mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);mPaint.setColor(BASECOLOR);//mPaint.setTextSize(mTextSize); mPaint.setStyle(Paint.Style.FILL_AND_STROKE);mPaint.setShader(null);mPaint.setTextAlign(Paint.Align.LEFT);setBackgroundColor(BACKGROUNDCOLOR);}/** * 计算当前的歌词显示参数 * 当前行,以及当前行进行的百分比 */ private void getCountAndPrecent() {if (mMediaPlayer != null) {int mPosition = mMediaPlayer.getCurrentPosition();//计算当前对应的歌词行。 for (int i = mLrc.length - 1; i >= 0; i--) {if (mPosition > mLrcTime[i]) {mCount = i;break;}}//计算当前行进行的百分比 if (mCount == mLrc.length) {//如果已经到了最后一行,表示播放完毕。 mPercent = 100;} else {mPercent = ((float) (mPosition - mLrcTime[mCount]) * 100) / (mLrcTime[mCount + 1] - mLrcTime[mCount]);}}}/** * 根据最长一行歌词的字符数来确认歌词的字体大小。 * @param canvas 显示歌词的画布 */ private void setTextSize(Canvas canvas) {if (mTextSize == 0) {float textWidth;mTextSize = MAXTEXTSIZE;//逐步缩小字体让最长一行的歌词也能显示完整。 do {mPaint.setTextSize(mTextSize);textWidth = mPaint.measureText(mLrc[mMaxLrcLine]);if (textWidth > canvas.getWidth()-mTextSize && mTextSize > MINTEXTSIZE)mTextSize -= 0.5f;else break;} while (true);System.out.println("字体大小:" + mTextSize);//调整行高。 mLineHeight = canvas.getHeight()/13;System.out.println("行高:" + mLineHeight);}}/** * 开始显示歌词 * @param lrcSource 歌词文件 * @param mediaPlayer 播放器 */ public void start(int lrcSource, MediaPlayer mediaPlayer) {this.mMediaPlayer = mediaPlayer;//加载和分解歌词 InputStream inputStream = getResources().openRawResource(lrcSource);BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);StringBuilder builder = new StringBuilder("");byte buffer[] = new byte[1024];try {int count;while ((count = bufferedInputStream.read(buffer)) != -1) {builder.append(new String(buffer, 0, count));}mLrc = builder.toString().split("\n");//歌词时间索引数组比歌词数组长度要大1.用来标识最后一行歌词时间(这里默认5秒) mLrcTime = new int[mLrc.length + 1];//去掉第一行初始的一个字符 百度歌词下载下来,第一行的第一个字节未特殊字符。 while (mLrc[0].charAt(0) != '[')mLrc[0] = mLrc[0].substring(1);int mMaxLrcLineChar = 0;mMaxLrcLine = 0;for (int i = 0; i < mLrc.length; i++) {String item[] = mLrc[i].split("]");mLrcTime[i] = 0;//本行的开始时间 mLrcTime[i] += Integer.parseInt(item[0].substring(1, 3)) * 60 * 1000;mLrcTime[i] += Integer.parseInt(item[0].substring(4, 6)) * 1000;mLrcTime[i] += Integer.parseInt(item[0].substring(7, 9)) * 10;//本行显示的歌词 if (item.length == 2)mLrc[i] = item[1];else mLrc[i] = "";if (mMaxLrcLineChar < mLrc[i].length()) {mMaxLrcLineChar = mLrc[i].length();mMaxLrcLine = i;}}//最后一行歌词默认耗时5秒 mLrcTime[mLrc.length] = mLrcTime[mLrc.length - 1] + 5000;} catch (IOException e) {e.printStackTrace();} finally {try {bufferedInputStream.close();inputStream.close();} catch (IOException e) {e.printStackTrace();}mTextSize = 0;//置0让onDraw重新计算字体大小和行高。 invalidate();//触发onDraw 开始刷新歌词 }}/** * 停止显示歌词 */ public void stop() {mMediaPlayer = null;invalidate();} }
一个卡拉OK效果的自定义歌词控件相关推荐
- ASP.NET中实现复用代码自定义用户控件UserControl的使用
场景 ASP.NET中新建Web网站并部署到IIS上(详细图文教程): https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/107199 ...
- android歌词效果,自定义View:Android歌词控件
TicktockMusic 音乐播放器项目相关文章汇总: 简介 之前做 TicktockMusic 音乐播放器,一个必要的需求肯定是歌词,在 github 上找了几个,发现或多或少都有点不满足需求,所 ...
- 自定义ScrollView控件 -- 拉申时跟随缩放效果
需求就是让第一张的图片在拉申时跟随缩放效果. 一个可以滑动的自定义ScrollView控件,放大第一个子控件.还能监听它滑动时位置 不废话直接上效果图 这个中间放大的效果图: 直接上代码了: 自定义的 ...
- Android自定义View-简约风歌词控件
前言 最近重构了之前的音乐播放器,添加了许多功能,比如歌词,下载功能等.这篇文章就让我们聊聊歌词控件的实现(歌词控件也已经开源,地址也在文章底部),先上效果图,如果感觉海星,就继续瞧下去! 看到这里, ...
- java自定义日历控件_【无私分享】修订版干货!!!一个炫酷的自定义日历控件,摆脱日历时间选择烦恼,纯福利~...
可能不少的小伙伴都有看楼主昨天发的自定义日历控件,虽然实现功能不多,但也还算将就吧. 但是看了的小伙伴就很心急了,说楼主上传到gitHub的东西有问题,楼主下载来看了看,基本都没问题吧,没弄好的小伙伴 ...
- 【无私分享】修订版干货!!!一个炫酷的自定义日历控件,摆脱日历时间选择烦恼,纯福利~...
可能不少的小伙伴都有看楼主昨天发的自定义日历控件,虽然实现功能不多,但也还算将就吧. 没有看的小伙伴如果有兴趣的话可以去看看:http://www.cnblogs.com/liushilin/p/57 ...
- C# WPF 歌词控件(支持逐字定位描色效果)
C# WPF 歌词控件(支持逐字定位描色效果) 原文:C# WPF 歌词控件(支持逐字定位描色效果) 之前做了一个模仿网易云歌词的控件,实现了加载网易云歌词并能随音乐播放进度定位歌词.今天呢将在这个控 ...
- Android自定义View实战:简约风歌词控件
作者:jsyjst 前言 最近重构了之前的音乐播放器,添加了许多功能,比如歌词,下载功能等.这篇文章就让我们聊聊歌词控件的实现,先上效果图,如果感觉海星,就继续瞧下去! 看到这里,估计你对这个控件还有 ...
- Android自定义View实战:简约风歌词控件,Android开发者值得深入思考的几个问题
57[02:41.62]从不知 她的痛苦 58[02:52.02] 59[02:54.11]喜欢你 那双眼动人 60[03:00.13]笑声更迷人 61[03:02.38] 62[03:03.14]愿 ...
最新文章
- CodeFirst 的编程方式
- Visual Studio 2017常用快捷键(小白入)
- 关于 Workbench中 pk,nn,uq,bin,un,zf,ai 的解释
- Android常用开源库之Universal-image-loader
- php 随机几率,php实现根据概率配置随机抽奖
- 转:有了这些网站,英文论文再也不难写了(15个英文论文写作辅助网站介绍和使用技巧)
- Flex Builder 破解和注册方法
- python 设置输入法为英文_python 怎么转化输入法
- 更新WP Mango应用磁贴内容的3种方法
- 定语从句中的关系代词
- 皮卡丘为什么不进化_皮卡丘为什么不进化 真是原因让人感动哭
- SPSS大学生毕业生就业问题分析
- 区块链项目ICO注意事项
- 电脑计算机进去用户文件不见了,为什么文件夹夹会不见
- 树莓派摄像头安装及配置
- 基于矢量成果从影像提取中深度学习样本库
- 游戏乱码解决软件 NTLEA
- spring注解配置
- 还在为多环境下项目管理的问题困扰吗,用上 direnv 后工作轻松乐无忧!
- java编译成功,但运行失败,即错误: 找不到或无法加载主类 原因: java.lang.ClassNotFoundException:
热门文章
- 山洪灾害监测预警系统经验分享
- 前端最常见的移动App分类介绍及优缺点
- windows server 2012中配置扩展文件服务器之实现,Wins 2012中实现扩展文件服务器的7个步骤...
- 嵌入式软件硬件比例_嵌入式做硬件方向好还是软件方向好?
- 【精】LintCode领扣算法问题答案:1084. “马”在棋盘上的概率
- uiautomator2详细使用方法
- 联想微型计算机c5606,中小企业入门级服务器首选联想万全R510
- 触手可及的AI:2020十大人工智能APP揭榜时刻
- 试用期满转正工作总结
- 第161天学习打卡(谷粒商城 3 安装powerDesigner软件 下载nodejs)