本篇博客主要是通过存放本地的一篇.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;
@Override
protected 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() {
@Override
public void run() {
// TODO Auto-generated method stub
while (timeFlag) {
try {
Thread.sleep(1000);
} catch (Exception e) {
}
handler.sendEmptyMessage(0);
}
}
});
private void initview() {
// TODO Auto-generated method stub
List<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
}
}
//歌曲播放时,根据拖动跨越的行数里面的时间快进或快退带时间对应的播放进度
@Override
public void call(long time) {
if (mediaPlayer.isPlaying()) {
mediaPlayer.seekTo((int) time);
}
}
@Override
protected void onDestroy() {
// TODO Auto-generated method stub
super.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);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// TODO Auto-generated method stub
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//getheight:获得界面内的高度
//getgetMeasuredHeight:获得视图实际测量的高度
height=getMeasuredHeight();
width=getMeasuredWidth();
}
@Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
super.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);
}
}
@Override
public 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 {
//每行歌词的封装类:每行歌词的javaBean
public 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.LrcView
android:id="@+id/mylrcview"
android:layout_height="match_parent"
android:layout_width="match_parent"
/>
</RelativeLayout>