本篇博客主要是通过存放本地的一篇.txt歌词,和这个.txt文件对应的一首歌,然后点击播放后,根据lrcview分割线来判断歌词滑动到那个线的时候播放,支持手势滑动,歌词暂停,如果有资源的话,做一个音乐播放器应该也不是问题,现在车载系统很需要这些控件,学会的话,可以从事以下其他技能。

1丶在配置清单当中加入权限:

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

2丶在Assets里面加入歌和歌词:

(  看自己喜欢什么歌 ,在这里需要下载一首歌,和一首歌的歌词)

在我的上一篇文章自定义双指放大,有详细的自定义view介绍,如果想深入了解,可以去看看。。。

MainActivity

import java.util.List;
import com.example.lyricdemo.LrcView.MedCallBack;
import android.R.plurals;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.app.Activity;
import android.content.res.AssetFileDescriptor;
import android.view.Menu;public class MainActivity extends Activity implements MedCallBack{private LrcRows lrcRows=new LrcRows();private MediaPlayer mediaPlayer=new MediaPlayer();private boolean timeFlag=true;private LrcView lrcView;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);initview();}Handler handler=new Handler(){public void handleMessage(android.os.Message msg) {if (msg.what==0) {int time=mediaPlayer.getCurrentPosition();lrcView.LrcToPlayer(time);//根据播放的进度,时时跟新歌词}};};Thread thread=new Thread(new Runnable() {@Overridepublic void run() {
// TODO Auto-generated method stubwhile (timeFlag) {try {Thread.sleep(1000);} catch (Exception e) {}handler.sendEmptyMessage(0);}}});private void initview() {
// TODO Auto-generated method stubList<LrcRow>list=lrcRows.BuildList(this);lrcView = (LrcView)findViewById(R.id.mylrcview);lrcView.setLrc(list);lrcView.setCall(this);try {
//从assets打开AssetFileDescriptor fileDescriptor=getAssets().openFd("farawayfromhome.mp3");mediaPlayer.setDataSource(fileDescriptor.getFileDescriptor(),fileDescriptor.getStartOffset(),fileDescriptor.getLength());mediaPlayer.prepare();mediaPlayer.start();thread.start();} catch (Exception e) {
// TODO: handle exception}}//歌曲播放时,根据拖动跨越的行数里面的时间快进或快退带时间对应的播放进度@Overridepublic void call(long time) {if (mediaPlayer.isPlaying()) {mediaPlayer.seekTo((int) time);}}@Overrideprotected void onDestroy() {
// TODO Auto-generated method stubsuper.onDestroy();timeFlag=false;mediaPlayer.stop();}
}

LrcView

import java.util.List;
import android.R.bool;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Align;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;public class LrcView extends View{private Paint paint;//画笔private List<LrcRow>list;//歌词数据源private int nowColor=Color.YELLOW;//正在播放的歌词颜色private int normalColor=Color.WHITE;//其他歌词的颜色private int lineColor=Color.CYAN;//分割线及时间显示的颜色private float textSize=20f;//歌词字体的大小private float timeSize=15f;//时间显示的大小private int lineHeight=2;//分割线的高度private int marginHeight=10;//歌词与歌词之间的间隔private int height;//自定义视图的高度private int width;//自定义视图的宽;private int index=0;//正在播放的歌词的行数private String tipstr="暂无歌词";//默认情况下的歌词private boolean TouchFlag=false;//手指按下的标志:当手指滑动的时候,界面不进行刷新//回调接口private MedCallBack medCallBack;private float lasty=0;//最后手指按下的坐标不public LrcView(Context context) {super(context);
// TODO Auto-generated constructor stub}public LrcView(Context context, AttributeSet attrs) {super(context, attrs);
//实例化画笔,  抗锯齿paint=new Paint(Paint.ANTI_ALIAS_FLAG);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// TODO Auto-generated method stubsuper.onMeasure(widthMeasureSpec, heightMeasureSpec);
//getheight:获得界面内的高度
//getgetMeasuredHeight:获得视图实际测量的高度height=getMeasuredHeight();width=getMeasuredWidth();}@Overrideprotected void onDraw(Canvas canvas) {
// TODO Auto-generated method stubsuper.onDraw(canvas);paint.reset();//重置画笔paint.setColor(nowColor);paint.setTextSize(textSize);paint.setTextAlign(Align.CENTER);if (list==null) {canvas.drawText(tipstr, width/2, height/2-textSize,paint );return;}if (list.size()==0) {canvas.drawText(tipstr, width/2,height/2-textSize, paint);return;}
//绘制中间正在播放的歌词canvas.drawText(list.get(index).row, width/2, height/2-textSize, paint);
//绘制中间的分割线paint.reset();paint.setColor(lineColor);if (TouchFlag) {canvas.drawLine(0, height/2-textSize, width, height/2-textSize+lineHeight, paint);paint.setTextSize(timeSize);paint.setTextAlign(Align.LEFT);canvas.drawText(list.get(index).str_timer,0,height/2,paint);}
//绘制普通的歌词paint.reset();paint.setColor(normalColor);paint.setTextSize(textSize);paint.setTextAlign(Align.CENTER);
//绘制正在播放歌词上面的歌词int normalIndex=0;int rowY=0;//每行歌词的Y值normalIndex=index-1;rowY=(int)(height/2-textSize*2-marginHeight);while (normalIndex>=0&&rowY>-textSize) {canvas.drawText(list.get(normalIndex).row,width/2, rowY, paint);normalIndex--;rowY=(int) (rowY-textSize-marginHeight);}
//2.绘制播放歌词下面的歌词normalIndex=index+1;rowY=(int) (height/2+marginHeight);while(normalIndex<list.size()&&rowY<(height+textSize)){canvas.drawText(list.get(normalIndex).row, width/2, rowY, paint);normalIndex++;rowY=(int) (rowY+marginHeight+textSize);}}@Overridepublic boolean onTouchEvent(MotionEvent event) {if (list==null) {return true;}if (list.size()==0) {return true;}
//手指按下if (event.getAction()==MotionEvent.ACTION_DOWN) {TouchFlag=true;//显示时间和分割线lasty=event.getY();//获取你手按下的Y轴坐标//手指滑动  }else if (event.getAction()==MotionEvent.ACTION_MOVE) {float nowY=event.getY();//当前手指滑动到的Y值float disY=nowY-lasty;//计算手指滑动的Y轴的距离//判断滑动的距离跨越几行歌词if (Math.abs(disY)>marginHeight) {int num=(int)(Math.abs(disY)/(marginHeight+textSize));if (num>=1) {if (disY<0) {
//快进index+=num;index=Math.min(list.size()-1, index);}else if (disY>0) {
//快退index-=num;index=Math.max(0,index);}}}lasty=nowY;
//手指抬起}else if (event.getAction()==MotionEvent.ACTION_UP) {TouchFlag=false;//调用接口if (medCallBack!=null) {medCallBack.call(list.get(index).time);}}invalidate();//刷新布局return true;}//查找歌词的方法,根据播放的进度跟新歌词 ,根据传入的参数进行当前歌曲进度的跳播public void LrcToPlayer(long time){if (list==null) {return;}if (list.size()==0) {return;}if (TouchFlag) {return;}//遍历整个歌词集合,寻找time的插入区间for (int i = 0; i < list.size(); i++) {LrcRow lrcRow=list.get(i);//当前的歌词对象LrcRow lrcRow2=(i+1)>=list.size()?null:list.get(i+1);if (time>lrcRow.time&&lrcRow2!=null&&time<lrcRow2.time) {index=i;break;}if (lrcRow2==null) {index=list.size()-1;}}invalidate();}public interface MedCallBack{//接口回调吧时间回调到主啊抽屉activity中去更新歌曲播放进度public void call(long time);}//设置歌词的方法public void setLrc(List<LrcRow>list){this.list=list;}public void setCall(MedCallBack medCallBack){this.medCallBack=medCallBack;}
}

LrcRows

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import android.content.Context;
import android.content.res.AssetManager;public class LrcRows {private List<LrcRow>list=new ArrayList<LrcRow>();//存放每行歌词的集合
//获取list集合的方法,将每行的歌词添加到list集合中public List<LrcRow>BuildList(Context context){//获取assets的管理器AssetManager assetManager=context.getAssets();
//打开assets下的指定文件,获取输入流try {InputStream inputStream=assetManager.open("farawayfromhome.lrc");
//将字节输入流转化为字符流BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(inputStream));String line=null;while ((line=bufferedReader.readLine())!=null) {LrcRow lrcRow=new LrcRow();//创建每行封装歌词的对象
//获取新的解析封装好的歌词 添加到集合中LrcRow lrcRow2=lrcRow.getRow(line);if (lrcRow2!=null) {list.add(lrcRow2);}}bufferedReader.close();} catch (Exception e) {
// TODO: handle exception}return list;}}

LrcRow

public class LrcRow {//每行歌词的封装类:每行歌词的javaBeanpublic String row;//歌词public String str_timer;//字符串格式的时间public long time;//每行歌词的毫秒时间//获取每行歌词的 及歌词解析的方法 public LrcRow getRow(String str){if (str==null) {return null;}if (str.equals("")) {return null;}
//!=9,将 歌词中不是歌词的那部分给过滤掉,因为歌词的时间字符串格式都是第九个位置为]if (str.indexOf("]")!=9) {//index of 返回指定字符串在str中第一次出现的索引return null;}
//获取每行的主题歌词row=str.substring(str.indexOf("]")+1);
//获取字符串格式的歌词时间    截取的时候包含开头,不包含结尾str_timer=str.substring(1,str.indexOf("]"));
//字符替换:. 替换成:---》给字符串分割的时候提供分割标志String newTime=str_timer.replace(".", ":");
//字符串的分割:自动分割成一个string类型的数组String[]arr=newTime.split(":");
//将字符串格式的时间转换为毫秒格式的时间time=Integer.valueOf(arr[0])*60*1000+Integer.valueOf(arr[1])*1000+Integer.valueOf(arr[2]);return this;}}

//以下是xml布局:

activity_main

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:background="#008AD4"tools:context=".MainActivity" >
<com.example.lyricdemo.LrcViewandroid:id="@+id/mylrcview"android:layout_height="match_parent"android:layout_width="match_parent"/>
</RelativeLayout>

Android 自定义歌词滚动相关推荐

  1. [Android]自定义垂直滚动的广告区

    [Android]自定义垂直滚动的广告区View @Author GQ 2016年07月13日 在github上找到一个在项目中常用的自定义控件,封装的不错,垂直滚动带渐入渐出动画. github项目 ...

  2. android 自定义含有滚动选择器的对话框

    最近在写一个项目,需要用到滚动选择器,本人的想法是弹出一个对话框,中间包含滚动选择器,其中滚动选择器的源码参考的这篇文章http://blog.csdn.net/zhongkejingwang/art ...

  3. Android自定义ListView示例,以创建不可滚动的ListView

    In this tutorial we'll override the ListView class to suit it according to our requirements in the a ...

  4. 我的Android进阶之旅------Android自定义View来实现解析lrc歌词并同步滚动、上下拖动、缩放歌词的功能...

    前言 一LRC歌词文件简介 1什么是LRC歌词文件 2LRC歌词文件的格式 LRC歌词文件的标签类型 1标识标签 2时间标签 二解析LRC歌词 1读取出歌词文件 2解析得到的歌词内容 1表示每行歌词内 ...

  5. Android自定义View来实现解析lrc歌词同步滚动、上下拖动、缩放歌词等功能

    http://blog.csdn.net/ouyang_peng/article/details/50813419 前言 一LRC歌词文件简介 1什么是LRC歌词文件 2LRC歌词文件的格式 LRC歌 ...

  6. Android自定义View来实现解析lrc歌词并同步滚动、上下拖动、缩放歌词的功能

    原文地址https://blog.csdn.net/qq446282412/article/details/50813419 前言 一LRC歌词文件简介 1什么是LRC歌词文件 2LRC歌词文件的格式 ...

  7. 我的Android进阶之旅------gt;Android自定义View来实现解析lrc歌词并同步滚动、上下拖动、缩放歌词的功能...

    前言 一LRC歌词文件简介 1什么是LRC歌词文件 2LRC歌词文件的格式 LRC歌词文件的标签类型 1标识标签 2时间标签 二解析LRC歌词 1读取出歌词文件 2解析得到的歌词内容 1表示每行歌词内 ...

  8. Android自定义View来实现解析lrc歌词并同步滚动、上下拖动、缩放歌词的功能[转]

    前言 一LRC歌词文件简介 1什么是LRC歌词文件 2LRC歌词文件的格式 LRC歌词文件的标签类型 1标识标签 2时间标签 二解析LRC歌词 1读取出歌词文件 2解析得到的歌词内容 1表示每行歌词内 ...

  9. Android自定义View,高仿QQ音乐歌词滚动控件!

    最近在以QQ音乐为样板做一个手机音乐播放器,源码下篇博文放出.今天我想聊的是这个QQ音乐播放器中歌词显示控件的问题,和小伙伴们一起来探讨怎么实现这个歌词滚动的效果.OK,废话不多说,先来看看效果图: ...

最新文章

  1. TASKCTL敏捷调度理念的诠释
  2. [BUUCTF-pwn]——[HarekazeCTF2019]baby_rop
  3. java小票_Java编程打印购物小票实现代码
  4. HDOJ水题集合1:最小生成树(Kruskal)
  5. 解决AngularJS在IE下取数据总是缓存的问题
  6. Error mounting /dev/sdc1 at /media/XXXX: Command-line `mount -t “ntfs“ -o
  7. SQL基础知识总结(SQL必知必会)
  8. 乐谱五线谱排版软件种类与介绍
  9. axure 8.1 授权码分享
  10. python分词和生成词云图
  11. 推荐五款你从未见过的嵌入式电子电路仿真APP
  12. springboot整合支付宝支付
  13. excel 将日期转换为8位数字
  14. 测试用例Passed和Failed有效性问题
  15. Unity实现遮挡人物的障碍物体设为透明,并在不遮挡时恢复的个人解决方法
  16. Win10如何删除输入法(删除默认输入法)
  17. 笛卡尔坐标为什么叫Cartesian coordinate而不是Descartes coordinate?
  18. 文字图片白底黑字_白底黑字简短文字图片 白底黑字纯简短文字
  19. 深大uooc学术道德与学术规范教育第四章
  20. 22.实战:Kaggle房价预测

热门文章

  1. 在计算机睡眠时主机关闭,电脑为什么睡眠后就关机怎么办
  2. Win10+VS2019编译Jpeg源码时缺少win32.mak文件的内容
  3. selenium自动化图片不加载设置
  4. python函数快查快用
  5. 计算机毕业设计Java新生报到管理(源码+系统+mysql数据库+lw文档)
  6. 史上最强HashMap面试教程
  7. 【高自旋和低自旋配合物】
  8. 易语言 vb c 那个写的程序运行快,VB好还是易语言
  9. 【NOIP2015模拟10.28B组】终章-剑之魂
  10. python的基本统计和分组分析和分布分析和交叉分析和结构分析