失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > android开发歌词滑动效果_Android 歌词同步滚动效果

android开发歌词滑动效果_Android 歌词同步滚动效果

时间:2020-07-20 02:03:09

相关推荐

android开发歌词滑动效果_Android 歌词同步滚动效果

歌词是播放器类App必不可少的组件,而一般的歌词组件都需要做到歌词的显示与播放进度同步。我们知道,歌词是如下所示的文件:

lrc

[ti:原来爱情这么伤]

[ar:梁咏琪]

[al:给自己的情歌]

[00:00.55]梁咏琪 - 原来爱情这么伤

[00:05.43]作词:彭学斌

[00:06.68]作曲:彭学斌

[00:09.63]

[00:22.27]我睁开眼睛 却感觉不到天亮

[00:29.74]东西吃一半 莫名其妙哭一场

[00:37.06]我忍住不想 时间变得更漫长

[00:44.09]也与你有关 否则又开始胡思乱想

[00:53.81]我日月无光 忙得不知所以然

[00:59.96]找朋友交谈 其实全帮不上忙

[01:07.49]以为会习惯 有你在才是习惯

[01:14.62]你曾住在我心上 现在空了一个地方

[01:21.89]原来爱情这么伤 比想象中还难

[01:29.90]泪水总是不听话 幸福躲起来不声不响

[01:37.43]太多道理太牵强 道理全是一样

[01:44.34]说的时候很简单 爱上后却正巧打乱

[02:00.00]我日月无光 忙得不知所以然

[02:07.41]找朋友交谈 其实全帮不上忙

[02:15.07]以为会习惯 有你在才是习惯

[02:21.88]你曾住在我心上 现在空了一个地方

[02:29.38]原来爱情这么伤 比想象中还难

[02:36.60]泪水总是不听话 幸福躲起来不声不响

[02:44.22]太多道理太牵强 道理全是一样

[02:50.78]说的时候很简单 爱上后却正巧打乱

[03:00.32]只想变的坚强 强到能够去忘

[03:07.29]无所谓悲伤 只要学会抵抗

[03:14.19]原来爱情这么伤

[03:20.78]原来爱情是这样 这样峰回路转

[03:28.12]泪水明明流不干 瞎了眼还要再爱一趟

[03:35.83]有一天终于打完 思念的一场战

[03:43.45]回过头再看一看 原来爱情那么伤

[03:54.76]下次还会不会这样

[88:88.88]

我们需要读取以上歌词文件的每一行转换成成一个个歌词实体:

Java代码

packagecom.music.lyricsync;

publicclassLyricObject{

publicintbegintime;//开始时间

publicintendtime;//结束时间

publicinttimeline;//单句歌词用时

publicStringlrc;//单句歌词

}

可根据当前播放器的播放进度与每句歌词的开始时间,得到当前屏幕中央高亮显示的那句歌词。在UI线程中另起线程,通过回调函数 onDraw() 每隔100ms重新绘制屏幕,实现歌词平滑滚动的动画效果。MainActivity代码如下:

Java代码

packagecom.music.lyricsync;

importjava.io.IOException;

importandroid.app.Activity;

importandroid.media.MediaPlayer;

.Uri;

importandroid.os.Bundle;

importandroid.os.Environment;

importandroid.os.Handler;

importandroid.view.View;

importandroid.view.View.OnClickListener;

importandroid.widget.Button;

importandroid.widget.SeekBar;

importandroid.widget.SeekBar.OnSeekBarChangeListener;

publicclassMainActivityextendsActivity{

/**Calledwhentheactivityisfirstcreated.*/

privateLyricViewlyricView;

privateMediaPlayermediaPlayer;

privateButtonbutton;

privateSeekBarseekBar;

privateStringmp3Path;

privateintINTERVAL=45;//歌词每行的间隔

@Override

publicvoidonCreate(BundlesavedInstanceState){

super.onCreate(savedInstanceState);

//this.requestWindowFeature(Window.FEATURE_NO_TITLE);

//getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);

setContentView(R.layout.main);

mp3Path=Environment.getExternalStorageDirectory().getAbsolutePath()+"/LyricSync/1.mp3";

lyricView=(LyricView)findViewById(R.id.mylrc);

mediaPlayer=newMediaPlayer();

//this.requestWindowFeature(Window.FEATURE_NO_TITLE);

ResetMusic(mp3Path);

SerchLrc();

lyricView.SetTextSize();

button=(Button)findViewById(R.id.button);

button.setText("播放");

seekBar=(SeekBar)findViewById(R.id.seekbarmusic);

seekBar.setOnSeekBarChangeListener(newOnSeekBarChangeListener(){

@Override

publicvoidonStopTrackingTouch(SeekBarseekBar){

//TODOAuto-generatedmethodstub

}

@Override

publicvoidonStartTrackingTouch(SeekBarseekBar){

//TODOAuto-generatedmethodstub

}

@Override

publicvoidonProgressChanged(SeekBarseekBar,intprogress,

booleanfromUser){

//TODOAuto-generatedmethodstub

if(fromUser){

mediaPlayer.seekTo(progress);

lyricView.setOffsetY(220-lyricView.SelectIndex(progress)

*(lyricView.getSIZEWORD()+INTERVAL-1));

}

}

});

button.setOnClickListener(newOnClickListener(){

@Override

publicvoidonClick(Viewv){

//TODOAuto-generatedmethodstub

if(mediaPlayer.isPlaying()){

button.setText("播放");

mediaPlayer.pause();

}else{

button.setText("暂停");

mediaPlayer.start();

lyricView.setOffsetY(220-lyricView.SelectIndex(mediaPlayer.getCurrentPosition())

*(lyricView.getSIZEWORD()+INTERVAL-1));

}

}

});

mediaPlayer.setOnCompletionListener(newMediaPlayer.OnCompletionListener(){

@Override

publicvoidonCompletion(MediaPlayermp){

ResetMusic(mp3Path);

lyricView.SetTextSize();

lyricView.setOffsetY(200);

mediaPlayer.start();

}

});

seekBar.setMax(mediaPlayer.getDuration());

newThread(newrunable()).start();

}

publicvoidSerchLrc(){

Stringlrc=mp3Path;

lrc=lrc.substring(0,lrc.length()-4).trim()+".lrc".trim();

LyricView.read(lrc);

lyricView.SetTextSize();

lyricView.setOffsetY(350);

}

publicvoidResetMusic(Stringpath){

mediaPlayer.reset();

try{

mediaPlayer.setDataSource(mp3Path);

mediaPlayer.prepare();

}catch(IllegalArgumentExceptione){

//TODOAuto-generatedcatchblock

e.printStackTrace();

}catch(IllegalStateExceptione){

//TODOAuto-generatedcatchblock

e.printStackTrace();

}catch(IOExceptione){

//TODOAuto-generatedcatchblock

e.printStackTrace();

}

}

classrunableimplementsRunnable{

@Override

publicvoidrun(){

//TODOAuto-generatedmethodstub

while(true){

try{

Thread.sleep(100);

if(mediaPlayer.isPlaying()){

lyricView.setOffsetY(lyricView.getOffsetY()-lyricView.SpeedLrc());

lyricView.SelectIndex(mediaPlayer.getCurrentPosition());

seekBar.setProgress(mediaPlayer.getCurrentPosition());

mHandler.post(mUpdateResults);

}

}catch(InterruptedExceptione){

//TODOAuto-generatedcatchblock

e.printStackTrace();

}

}

}

}

HandlermHandler=newHandler();

RunnablemUpdateResults=newRunnable(){

publicvoidrun(){

lyricView.invalidate();//更新视图

}

};

}

歌词View的代码如下:

Java代码

packagecom.music.lyricsync;

importjava.io.BufferedReader;

importjava.io.File;

importjava.io.FileInputStream;

importjava.io.FileNotFoundException;

importjava.io.IOException;

importjava.io.InputStreamReader;

importjava.util.Iterator;

importjava.util.TreeMap;

importjava.util.regex.Matcher;

importjava.util.regex.Pattern;

importandroid.content.Context;

importandroid.graphics.Canvas;

importandroid.graphics.Color;

importandroid.graphics.Paint;

importandroid.util.AttributeSet;

importandroid.util.Log;

importandroid.view.MotionEvent;

importandroid.view.View;

publicclassLyricViewextendsView{

privatestaticTreeMaplrc_map;

privatefloatmX;//屏幕X轴的中点,此值固定,保持歌词在X中间显示

privatefloatoffsetY;//歌词在Y轴上的偏移量,此值会根据歌词的滚动变小

privatestaticbooleanblLrc=false;

privatefloattouchY;//当触摸歌词View时,保存为当前触点的Y轴坐标

privatefloattouchX;

privatebooleanblScrollView=false;

privateintlrcIndex=0;//保存歌词TreeMap的下标

privateintSIZEWORD=0;//显示歌词文字的大小值

privateintINTERVAL=45;//歌词每行的间隔

Paintpaint=newPaint();//画笔,用于画不是高亮的歌词

PaintpaintHL=newPaint();//画笔,用于画高亮的歌词,即当前唱到这句歌词

publicLyricView(Contextcontext){

super(context);

init();

}

publicLyricView(Contextcontext,AttributeSetattrs){

super(context,attrs);

init();

}

/*(non-Javadoc)

*@seeandroid.view.View#onDraw(android.graphics.Canvas)

*/

@Override

protectedvoidonDraw(Canvascanvas){

if(blLrc){

paintHL.setTextSize(SIZEWORD);

paint.setTextSize(SIZEWORD);

LyricObjecttemp=lrc_map.get(lrcIndex);

canvas.drawText(temp.lrc,mX,offsetY+(SIZEWORD+INTERVAL)*lrcIndex,paintHL);

//画当前歌词之前的歌词

for(inti=lrcIndex-1;i>=0;i--){

temp=lrc_map.get(i);

if(offsetY+(SIZEWORD+INTERVAL)*i<0){

break;

}

canvas.drawText(temp.lrc,mX,offsetY+(SIZEWORD+INTERVAL)*i,paint);

}

//画当前歌词之后的歌词

for(inti=lrcIndex+1;i

temp=lrc_map.get(i);

if(offsetY+(SIZEWORD+INTERVAL)*i>600){

break;

}

canvas.drawText(temp.lrc,mX,offsetY+(SIZEWORD+INTERVAL)*i,paint);

}

}

else{

paint.setTextSize(25);

canvas.drawText("找不到歌词",mX,310,paint);

}

super.onDraw(canvas);

}

/*(non-Javadoc)

*@seeandroid.view.View#onTouchEvent(android.view.MotionEvent)

*/

@Override

publicbooleanonTouchEvent(MotionEventevent){

//TODOAuto-generatedmethodstub

System.out.println("bllll==="+blScrollView);

floattt=event.getY();

if(!blLrc){

//returnsuper.onTouchEvent(event);

returnsuper.onTouchEvent(event);

}

switch(event.getAction()){

caseMotionEvent.ACTION_DOWN:

touchX=event.getX();

break;

caseMotionEvent.ACTION_MOVE:

touchY=tt-touchY;

offsetY=offsetY+touchY;

break;

caseMotionEvent.ACTION_UP:

blScrollView=false;

break;

}

touchY=tt;

returntrue;

}

publicvoidinit(){

lrc_map=newTreeMap();

offsetY=320;

paint=newPaint();

paint.setTextAlign(Paint.Align.CENTER);

paint.setColor(Color.GREEN);

paint.setAntiAlias(true);

paint.setDither(true);

paint.setAlpha(180);

paintHL=newPaint();

paintHL.setTextAlign(Paint.Align.CENTER);

paintHL.setColor(Color.RED);

paintHL.setAntiAlias(true);

paintHL.setAlpha(255);

}

/**

*根据歌词里面最长的那句来确定歌词字体的大小

*/

publicvoidSetTextSize(){

if(!blLrc){

return;

}

intmax=lrc_map.get(0).lrc.length();

for(inti=1;i

LyricObjectlrcStrLength=lrc_map.get(i);

if(max

max=lrcStrLength.lrc.length();

}

}

SIZEWORD=320/max;

}

protectedvoidonSizeChanged(intw,inth,intoldw,intoldh){

mX=w*0.5f;

super.onSizeChanged(w,h,oldw,oldh);

}

/**

*歌词滚动的速度

*

*@return返回歌词滚动的速度

*/

publicFloatSpeedLrc(){

floatspeed=0;

if(offsetY+(SIZEWORD+INTERVAL)*lrcIndex>220){

speed=((offsetY+(SIZEWORD+INTERVAL)*lrcIndex-220)/20);

}elseif(offsetY+(SIZEWORD+INTERVAL)*lrcIndex

Log.i("speed","speedistoofast!!!");

speed=0;

}

//if(speed<0.2){

//speed=0.2f;

//}

returnspeed;

}

/**

*按当前的歌曲的播放时间,从歌词里面获得那一句

*@paramtime当前歌曲的播放时间

*@return返回当前歌词的索引值

*/

publicintSelectIndex(inttime){

if(!blLrc){

return0;

}

intindex=0;

for(inti=0;i

LyricObjecttemp=lrc_map.get(i);

if(temp.begintime

++index;

}

}

lrcIndex=index-1;

if(lrcIndex<0){

lrcIndex=0;

}

returnlrcIndex;

}

/**

*读取歌词文件

*@paramfile歌词的路径

*

*/

publicstaticvoidread(Stringfile){

TreeMaplrc_read=newTreeMap();

Stringdata="";

try{

FilesaveFile=newFile(file);

//System.out.println("是否有歌词文件"+saveFile.isFile());

if(!saveFile.isFile()){

blLrc=false;

return;

}

blLrc=true;

//System.out.println("bllrc==="+blLrc);

FileInputStreamstream=newFileInputStream(saveFile);//context.openFileInput(file);

BufferedReaderbr=newBufferedReader(newInputStreamReader(stream,"GB2312"));

inti=0;

Patternpattern=pile("\\d{2}");

while((data=br.readLine())!=null){

//System.out.println("++++++++++++>>"+data);

data=data.replace("[","");//将前面的替换成后面的

data=data.replace("]","@");

Stringsplitdata[]=data.split("@");//分隔

if(data.endsWith("@")){

for(intk=0;k

Stringstr=splitdata[k];

str=str.replace(":",".");

str=str.replace(".","@");

Stringtimedata[]=str.split("@");

Matchermatcher=pattern.matcher(timedata[0]);

if(timedata.length==3&&matcher.matches()){

intm=Integer.parseInt(timedata[0]);//分

ints=Integer.parseInt(timedata[1]);//秒

intms=Integer.parseInt(timedata[2]);//毫秒

intcurrTime=(m*60+s)*1000+ms*10;

LyricObjectitem1=newLyricObject();

item1.begintime=currTime;

item1.lrc="";

lrc_read.put(currTime,item1);

}

}

}

else{

StringlrcContenet=splitdata[splitdata.length-1];

for(intj=0;j

{

Stringtmpstr=splitdata[j];

tmpstr=tmpstr.replace(":",".");

tmpstr=tmpstr.replace(".","@");

Stringtimedata[]=tmpstr.split("@");

Matchermatcher=pattern.matcher(timedata[0]);

if(timedata.length==3&&matcher.matches()){

intm=Integer.parseInt(timedata[0]);//分

ints=Integer.parseInt(timedata[1]);//秒

intms=Integer.parseInt(timedata[2]);//毫秒

intcurrTime=(m*60+s)*1000+ms*10;

LyricObjectitem1=newLyricObject();

item1.begintime=currTime;

item1.lrc=lrcContenet;

lrc_read.put(currTime,item1);//将currTime当标签item1当数据插入TreeMap里

i++;

}

}

}

}

stream.close();

}

catch(FileNotFoundExceptione){

}

catch(IOExceptione){

}

/*

*遍历hashmap计算每句歌词所需要的时间

*/

lrc_map.clear();

data="";

Iteratoriterator=lrc_read.keySet().iterator();

LyricObjectoldval=null;

inti=0;

while(iterator.hasNext()){

Objectob=iterator.next();

LyricObjectval=(LyricObject)lrc_read.get(ob);

if(oldval==null)

oldval=val;

else

{

LyricObjectitem1=newLyricObject();

item1=oldval;

item1.timeline=val.begintime-oldval.begintime;

lrc_map.put(newInteger(i),item1);

i++;

oldval=val;

}

if(!iterator.hasNext()){

lrc_map.put(newInteger(i),val);

}

}

}

/**

*@returntheblLrc

*/

publicstaticbooleanisBlLrc(){

returnblLrc;

}

/**

*@returntheoffsetY

*/

publicfloatgetOffsetY(){

returnoffsetY;

}

/**

*@paramoffsetYtheoffsetYtoset

*/

publicvoidsetOffsetY(floatoffsetY){

this.offsetY=offsetY;

}

/**

*@return返回歌词文字的大小

*/

publicintgetSIZEWORD(){

returnSIZEWORD;

}

/**

*设置歌词文字的大小

*@paramsIZEWORDthesIZEWORDtoset

*/

publicvoidsetSIZEWORD(intsIZEWORD){

SIZEWORD=sIZEWORD;

}

}

xml布局文件如下:

Xml代码

android:layout_width="fill_parent"

android:layout_height="fill_parent"

android:background="#FFFFFF">

android:id="@+id/mylrc"

android:layout_width="fill_parent"

android:layout_height="fill_parent"

android:layout_marginBottom="50dip"

android:layout_marginTop="50dip"/>

xmlns:android="/apk/res/android"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_alignParentBottom="true"

android:orientation="horizontal">

android:id="@+id/button"

android:layout_width="wrap_content"

android:layout_height="wrap_content"/>

android:id="@+id/seekbarmusic"

android:layout_width="205px"

android:layout_height="wrap_content"

android:layout_gravity="center_vertical"

android:layout_marginBottom="5px"

android:progress="0"/>

如果觉得《android开发歌词滑动效果_Android 歌词同步滚动效果》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。