上图就是效果。
背景和字体颜色可以自己调,都定义成了常量并有说明,直接更改即可。

可以自适应不同分别率。

可以自己将其改成根据屏幕高度动态决定显示几行。只需要调整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效果的自定义歌词控件相关推荐

  1. ASP.NET中实现复用代码自定义用户控件UserControl的使用

    场景 ASP.NET中新建Web网站并部署到IIS上(详细图文教程): https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/107199 ...

  2. android歌词效果,自定义View:Android歌词控件

    TicktockMusic 音乐播放器项目相关文章汇总: 简介 之前做 TicktockMusic 音乐播放器,一个必要的需求肯定是歌词,在 github 上找了几个,发现或多或少都有点不满足需求,所 ...

  3. 自定义ScrollView控件 -- 拉申时跟随缩放效果

    需求就是让第一张的图片在拉申时跟随缩放效果. 一个可以滑动的自定义ScrollView控件,放大第一个子控件.还能监听它滑动时位置 不废话直接上效果图 这个中间放大的效果图: 直接上代码了: 自定义的 ...

  4. Android自定义View-简约风歌词控件

    前言 最近重构了之前的音乐播放器,添加了许多功能,比如歌词,下载功能等.这篇文章就让我们聊聊歌词控件的实现(歌词控件也已经开源,地址也在文章底部),先上效果图,如果感觉海星,就继续瞧下去! 看到这里, ...

  5. java自定义日历控件_【无私分享】修订版干货!!!一个炫酷的自定义日历控件,摆脱日历时间选择烦恼,纯福利~...

    可能不少的小伙伴都有看楼主昨天发的自定义日历控件,虽然实现功能不多,但也还算将就吧. 但是看了的小伙伴就很心急了,说楼主上传到gitHub的东西有问题,楼主下载来看了看,基本都没问题吧,没弄好的小伙伴 ...

  6. 【无私分享】修订版干货!!!一个炫酷的自定义日历控件,摆脱日历时间选择烦恼,纯福利~...

    可能不少的小伙伴都有看楼主昨天发的自定义日历控件,虽然实现功能不多,但也还算将就吧. 没有看的小伙伴如果有兴趣的话可以去看看:http://www.cnblogs.com/liushilin/p/57 ...

  7. C# WPF 歌词控件(支持逐字定位描色效果)

    C# WPF 歌词控件(支持逐字定位描色效果) 原文:C# WPF 歌词控件(支持逐字定位描色效果) 之前做了一个模仿网易云歌词的控件,实现了加载网易云歌词并能随音乐播放进度定位歌词.今天呢将在这个控 ...

  8. Android自定义View实战:简约风歌词控件

    作者:jsyjst 前言 最近重构了之前的音乐播放器,添加了许多功能,比如歌词,下载功能等.这篇文章就让我们聊聊歌词控件的实现,先上效果图,如果感觉海星,就继续瞧下去! 看到这里,估计你对这个控件还有 ...

  9. 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]愿 ...

最新文章

  1. CodeFirst 的编程方式
  2. Visual Studio 2017常用快捷键(小白入)
  3. 关于 Workbench中 pk,nn,uq,bin,un,zf,ai 的解释
  4. Android常用开源库之Universal-image-loader
  5. php 随机几率,php实现根据概率配置随机抽奖
  6. 转:有了这些网站,英文论文再也不难写了(15个英文论文写作辅助网站介绍和使用技巧)
  7. Flex Builder 破解和注册方法
  8. python 设置输入法为英文_python 怎么转化输入法
  9. 更新WP Mango应用磁贴内容的3种方法
  10. 定语从句中的关系代词
  11. 皮卡丘为什么不进化_皮卡丘为什么不进化 真是原因让人感动哭
  12. SPSS大学生毕业生就业问题分析
  13. 区块链项目ICO注意事项
  14. 电脑计算机进去用户文件不见了,为什么文件夹夹会不见
  15. 树莓派摄像头安装及配置
  16. 基于矢量成果从影像提取中深度学习样本库
  17. 游戏乱码解决软件 NTLEA
  18. spring注解配置
  19. 还在为多环境下项目管理的问题困扰吗,用上 direnv 后工作轻松乐无忧!
  20. java编译成功,但运行失败,即错误: 找不到或无法加载主类 原因: java.lang.ClassNotFoundException:

热门文章

  1. 山洪灾害监测预警系统经验分享
  2. 前端最常见的移动App分类介绍及优缺点
  3. windows server 2012中配置扩展文件服务器之实现,Wins 2012中实现扩展文件服务器的7个步骤...
  4. 嵌入式软件硬件比例_嵌入式做硬件方向好还是软件方向好?
  5. 【精】LintCode领扣算法问题答案:1084. “马”在棋盘上的概率
  6. uiautomator2详细使用方法
  7. 联想微型计算机c5606,中小企业入门级服务器首选联想万全R510
  8. 触手可及的AI:2020十大人工智能APP揭榜时刻
  9. 试用期满转正工作总结
  10. 第161天学习打卡(谷粒商城 3 安装powerDesigner软件 下载nodejs)