Android 實現歌曲播放時歌詞同步顯示

我們需要讀取以上歌詞文件的每一行轉換成成一個個歌詞實體:

[java]  public class LyricObject {   
    public int begintime; // 開始時間    
    public int endtime; // 結束時間    
    public int timeline; // 單句歌詞用時    
    public String lrc; // 單句歌詞    
}   

public class LyricObject { 
    public int begintime; // 開始時間 
    public int endtime; // 結束時間 
    public int timeline; // 單句歌詞用時 
    public String lrc; // 單句歌詞 

可根據當前播放器的播放進度與每句歌詞的開始時間,得到當前屏幕中央高亮顯示的那句歌詞。在UI線程中另起線程,通過回調函數 onDraw() 每隔100ms重新繪制屏幕,實現歌詞平滑滾動的動畫效果。MainActivity代碼如下:

 

[java]   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(); // 更新視圖    
        }   
    };   
}   

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的代碼如下:

 

[java]  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;   
    }   
}   

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佈局文件如下:

 

[html]  <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> 

<?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> 

 

You May Also Like