广

android开发

  • IOS开发
  • android开发
  • PHP编程
  • JavaScript
  • ASP.NET
  • ASP编程
  • JSP编程
  • Java编程
  • 易语言
  • Ruby编程
  • Perl编程
  • AJAX
  • 正则表达式
  • C语言
  • 编程开发

    Android实现歌曲播放时歌词同步显示具体思路

    2018-04-09 07:42:26 次阅读 稿源:互联网
    广告
    我们需要读取以上歌词文件的每一行转换成成一个个歌词实体:
    代码如下:

    public class LyricObject {
    public int begintime; // 开始时间
    public int endtime; // 结束时间
    public int timeline; // 单句歌词用时
    public String lrc; // 单句歌词
    }

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

    import java.io.IOException;
    import android.app.Activity;
    import android.media.MediaPlayer;
    import android.net.Uri;
    import android.os.Bundle;
    import android.os.Environment;
    import android.os.Handler;
    import android.view.View;
    import android.view.View.OnClickListener;
    import android.widget.Button;
    import android.widget.SeekBar;
    import android.widget.SeekBar.OnSeekBarChangeListener;
    public class MainActivity extends Activity {
    /** Called when the activity is first created. */
    private LyricView lyricView;
    private MediaPlayer mediaPlayer;
    private Button button;
    private SeekBar seekBar;
    private String mp3Path;
    private int INTERVAL=45;//歌词每行的间隔
    @Override
    public void onCreate(Bundle savedInstanceState) {
    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 = new MediaPlayer();
    // 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(new OnSeekBarChangeListener() {
    @Override
    public void onStopTrackingTouch(SeekBar seekBar) {
    // TODO Auto-generated method stub
    }
    @Override
    public void onStartTrackingTouch(SeekBar seekBar) {
    // TODO Auto-generated method stub
    }
    @Override
    public void onProgressChanged(SeekBar seekBar, int progress,
    boolean fromUser) {
    // TODO Auto-generated method stub
    if (fromUser) {
    mediaPlayer.seekTo(progress);
    lyricView.setOffsetY(220 - lyricView.SelectIndex(progress)
    * (lyricView.getSIZEWORD() + INTERVAL-1));
    }
    }
    });
    button.setOnClickListener(new OnClickListener() {
    @Override
    public void onClick(View v) {
    // TODO Auto-generated method stub
    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(new MediaPlayer.OnCompletionListener() {
    @Override
    public void onCompletion(MediaPlayer mp) {
    ResetMusic(mp3Path);
    lyricView.SetTextSize();
    lyricView.setOffsetY(200);
    mediaPlayer.start();
    }
    });
    seekBar.setMax(mediaPlayer.getDuration());
    new Thread(new runable()).start();
    }
    public void SerchLrc() {
    String lrc = mp3Path;
    lrc = lrc.substring(0, lrc.length() - 4).trim() + ".lrc".trim();
    LyricView.read(lrc);
    lyricView.SetTextSize();
    lyricView.setOffsetY(350);
    }
    public void ResetMusic(String path) {
    mediaPlayer.reset();
    try {
    mediaPlayer.setDataSource(mp3Path);
    mediaPlayer.prepare();
    } catch (IllegalArgumentException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    } catch (IllegalStateException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    } catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    }
    }
    class runable implements Runnable {
    @Override
    public void run() {
    // TODO Auto-generated method stub
    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 (InterruptedException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    }
    }
    }
    }
    Handler mHandler = new Handler();
    Runnable mUpdateResults = new Runnable() {
    public void run() {
    lyricView.invalidate(); // 更新视图
    }
    };
    }

    歌词View的代码如下:
    代码如下:

    import java.io.BufferedReader;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.util.Iterator;
    import java.util.TreeMap;
    import java.util.regex.Matcher;
    import java.util.regex.Pattern;
    import android.content.Context;
    import android.graphics.Canvas;
    import android.graphics.Color;
    import android.graphics.Paint;
    import android.util.AttributeSet;
    import android.util.Log;
    import android.view.MotionEvent;
    import android.view.View;
    public class LyricView extends View{
    private static TreeMap<Integer, LyricObject> lrc_map;
    private float mX; //屏幕X轴的中点,此值固定,保持歌词在X中间显示
    private float offsetY; //歌词在Y轴上的偏移量,此值会根据歌词的滚动变小
    private static boolean blLrc=false;
    private float touchY; //当触摸歌词View时,保存为当前触点的Y轴坐标
    private float touchX;
    private boolean blScrollView=false;
    private int lrcIndex=0; //保存歌词TreeMap的下标
    private int SIZEWORD=0;//显示歌词文字的大小值
    private int INTERVAL=45;//歌词每行的间隔
    Paint paint=new Paint();//画笔,用于画不是高亮的歌词
    Paint paintHL=new Paint(); //画笔,用于画高亮的歌词,即当前唱到这句歌词
    public LyricView(Context context){
    super(context);
    init();
    }
    public LyricView(Context context, AttributeSet attrs) {
    super(context, attrs);
    init();
    }
    /* (non-Javadoc)
    * @see android.view.View#onDraw(android.graphics.Canvas)
    */
    @Override
    protected void onDraw(Canvas canvas) {
    if(blLrc){
    paintHL.setTextSize(SIZEWORD);
    paint.setTextSize(SIZEWORD);
    LyricObject temp=lrc_map.get(lrcIndex);
    canvas.drawText(temp.lrc, mX, offsetY+(SIZEWORD+INTERVAL)*lrcIndex, paintHL);
    // 画当前歌词之前的歌词
    for(int i=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(int i=lrcIndex+1;i<lrc_map.size();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)
    * @see android.view.View#onTouchEvent(android.view.MotionEvent)
    */
    @Override
    public boolean onTouchEvent(MotionEvent event) {
    // TODO Auto-generated method stub
    System.out.println("bllll==="+blScrollView);
    float tt=event.getY();
    if(!blLrc){
    //return super.onTouchEvent(event);
    return super.onTouchEvent(event);
    }
    switch(event.getAction()){
    case MotionEvent.ACTION_DOWN:
    touchX=event.getX();
    break;
    case MotionEvent.ACTION_MOVE:
    touchY=tt-touchY;
    offsetY=offsetY+touchY;
    break;
    case MotionEvent.ACTION_UP:
    blScrollView=false;
    break;
    }
    touchY=tt;
    return true;
    }
    public void init(){
    lrc_map = new TreeMap<Integer, LyricObject>();
    offsetY=320;
    paint=new Paint();
    paint.setTextAlign(Paint.Align.CENTER);
    paint.setColor(Color.GREEN);
    paint.setAntiAlias(true);
    paint.setDither(true);
    paint.setAlpha(180);
    paintHL=new Paint();
    paintHL.setTextAlign(Paint.Align.CENTER);
    paintHL.setColor(Color.RED);
    paintHL.setAntiAlias(true);
    paintHL.setAlpha(255);
    }
    /**
    * 根据歌词里面最长的那句来确定歌词字体的大小
    */
    public void SetTextSize(){
    if(!blLrc){
    return;
    }
    int max=lrc_map.get(0).lrc.length();
    for(int i=1;i<lrc_map.size();i++){
    LyricObject lrcStrLength=lrc_map.get(i);
    if(max<lrcStrLength.lrc.length()){
    max=lrcStrLength.lrc.length();
    }
    }
    SIZEWORD=320/max;
    }
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    mX = w * 0.5f;
    super.onSizeChanged(w, h, oldw, oldh);
    }
    /**
    * 歌词滚动的速度
    *
    * @return 返回歌词滚动的速度
    */
    public Float SpeedLrc(){
    float speed=0;
    if(offsetY+(SIZEWORD+INTERVAL)*lrcIndex>220){
    speed=((offsetY+(SIZEWORD+INTERVAL)*lrcIndex-220)/20);
    } else if(offsetY+(SIZEWORD+INTERVAL)*lrcIndex < 120){
    Log.i("speed", "speed is too fast!!!");
    speed = 0;
    }
    // if(speed<0.2){
    // speed=0.2f;
    // }
    return speed;
    }
    /**
    * 按当前的歌曲的播放时间,从歌词里面获得那一句
    * @param time 当前歌曲的播放时间
    * @return 返回当前歌词的索引值
    */
    public int SelectIndex(int time){
    if(!blLrc){
    return 0;
    }
    int index=0;
    for(int i=0;i<lrc_map.size();i++){
    LyricObject temp=lrc_map.get(i);
    if(temp.begintime<time){
    ++index;
    }
    }
    lrcIndex=index-1;
    if(lrcIndex<0){
    lrcIndex=0;
    }
    return lrcIndex;
    }
    /**
    * 读取歌词文件
    * @param file 歌词的路径
    *
    */
    public static void read(String file) {
    TreeMap<Integer, LyricObject> lrc_read =new TreeMap<Integer, LyricObject>();
    String data = "";
    try {
    File saveFile=new File(file);
    // System.out.println("是否有歌词文件"+saveFile.isFile());
    if(!saveFile.isFile()){
    blLrc=false;
    return;
    }
    blLrc=true;
    //System.out.println("bllrc==="+blLrc);
    FileInputStream stream = new FileInputStream(saveFile);// context.openFileInput(file);
    BufferedReader br = new BufferedReader(new InputStreamReader(stream,"GB2312"));
    int i = 0;
    Pattern pattern = Pattern.compile("//d{2}");
    while ((data = br.readLine()) != null) {
    // System.out.println("++++++++++++>>"+data);
    data = data.replace("[","");//将前面的替换成后面的
    data = data.replace("]","@");
    String splitdata[] =data.split("@");//分隔
    if(data.endsWith("@")){
    for(int k=0;k<splitdata.length;k++){
    String str=splitdata[k];
    str = str.replace(":",".");
    str = str.replace(".","@");
    String timedata[] =str.split("@");
    Matcher matcher = pattern.matcher(timedata[0]);
    if(timedata.length==3 && matcher.matches()){
    int m = Integer.parseInt(timedata[0]); //分
    int s = Integer.parseInt(timedata[1]); //秒
    int ms = Integer.parseInt(timedata[2]); //毫秒
    int currTime = (m*60+s)*1000+ms*10;
    LyricObject item1= new LyricObject();
    item1.begintime = currTime;
    item1.lrc = "";
    lrc_read.put(currTime,item1);
    }
    }
    }
    else{
    String lrcContenet = splitdata[splitdata.length-1];
    for (int j=0;j<splitdata.length-1;j++)
    {
    String tmpstr = splitdata[j];
    tmpstr = tmpstr.replace(":",".");
    tmpstr = tmpstr.replace(".","@");
    String timedata[] =tmpstr.split("@");
    Matcher matcher = pattern.matcher(timedata[0]);
    if(timedata.length==3 && matcher.matches()){
    int m = Integer.parseInt(timedata[0]); //分
    int s = Integer.parseInt(timedata[1]); //秒
    int ms = Integer.parseInt(timedata[2]); //毫秒
    int currTime = (m*60+s)*1000+ms*10;
    LyricObject item1= new LyricObject();
    item1.begintime = currTime;
    item1.lrc = lrcContenet;
    lrc_read.put(currTime,item1);// 将currTime当标签 item1当数据 插入TreeMap里
    i++;
    }
    }
    }
    }
    stream.close();
    }
    catch (FileNotFoundException e) {
    }
    catch (IOException e) {
    }
    /*
    * 遍历hashmap 计算每句歌词所需要的时间
    */
    lrc_map.clear();
    data ="";
    Iterator<Integer> iterator = lrc_read.keySet().iterator();
    LyricObject oldval = null;
    int i =0;
    while(iterator.hasNext()) {
    Object ob =iterator.next();
    LyricObject val = (LyricObject)lrc_read.get(ob);
    if (oldval==null)
    oldval = val;
    else
    {
    LyricObject item1= new LyricObject();
    item1 = oldval;
    item1.timeline = val.begintime-oldval.begintime;
    lrc_map.put(new Integer(i), item1);
    i++;
    oldval = val;
    }
    if (!iterator.hasNext()) {
    lrc_map.put(new Integer(i), val);
    }
    }
    }
    /**
    * @return the blLrc
    */
    public static boolean isBlLrc() {
    return blLrc;
    }
    /**
    * @return the offsetY
    */
    public float getOffsetY() {
    return offsetY;
    }
    /**
    * @param offsetY the offsetY to set
    */
    public void setOffsetY(float offsetY) {
    this.offsetY = offsetY;
    }
    /**
    * @return 返回歌词文字的大小
    */
    public int getSIZEWORD() {
    return SIZEWORD;
    }
    /**
    * 设置歌词文字的大小
    * @param sIZEWORD the sIZEWORD to set
    */
    public void setSIZEWORD(int sIZEWORD) {
    SIZEWORD = sIZEWORD;
    }
    }

    xml布局文件如下:
    代码如下:

    <SPAN style="FONT-SIZE: 18px"><STRONG><?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="#FFFFFF" >
    <com.music.lyricsync.LyricView
    android:id="@+id/mylrc"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:layout_marginBottom="50dip"
    android:layout_marginTop="50dip" />
    <LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentBottom="true"
    android:orientation="horizontal" >
    <Button
    android:id="@+id/button"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />
    <SeekBar
    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" />
    </LinearLayout>
    </RelativeLayout> </STRONG></SPAN>

    一起学吧部分文章转载自互联网,供读者交流和学习,若有涉及作者版权等问题请及时与我们联系,以便更正、删除或按规定办理。感谢所有提供资讯的网站,欢迎各类媒体与一起学吧进行文章共享合作。

    广告
    广告
    广告