zoukankan      html  css  js  c++  java
  • 边播放歌曲边滚动歌词

    一、LyricView

    package com.ct.lrc;
    
    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.R.integer;
    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.util.Log;
    import android.view.MotionEvent;
    import android.view.View;
    
    public class LyricView extends View{
        private Context context;
        private float offsetY;      //歌词在Y轴上的偏移量,此值会根据歌词的滚动变小 
        private static TreeMap<Integer, LyricObject> lrc_map;
         private static boolean blLrc=false;
         private float mX;       //屏幕X轴的中点,此值固定,保持歌词在X中间显示  
        Paint paint = new Paint();
        Paint paintHL = new Paint();
        private  int SIZEWORD=0;//显示歌词文字的大小值  
        private  int INTERVAL=45;//歌词每行的间隔  
        private int lrcIndex=0; //保存歌词TreeMap的下标  
        private float touchY;   //当触摸歌词View时,保存为当前触点的Y轴坐标  
        private float touchX;  
        private boolean blScrollView=false;  
    
        public LyricView(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
            init();
            // TODO Auto-generated constructor stub
        }
    
        public LyricView(Context context, AttributeSet attrs) {
            super(context, attrs);
            init();
            // TODO Auto-generated constructor stub
        }
    
        public LyricView(Context context) {
            super(context);
            init();
            // TODO Auto-generated constructor stub
        }
        
        private void init(){
            lrc_map = new TreeMap<Integer, LyricObject>();
            offsetY = 320;
            
            paint = new Paint();
            paint.setAlpha(180);
            paint.setColor(Color.GREEN);
            paint.setTextAlign(Align.CENTER);
            paint.setDither(true);
            paint.setAntiAlias(true);
            
            paintHL = new Paint();
            paintHL.setAlpha(255);
            paintHL.setColor(Color.RED);
            paintHL.setTextAlign(Align.CENTER);
            paintHL.setDither(true);
            paintHL.setAntiAlias(true);
            
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            // TODO Auto-generated method stub
            if(blLrc){//歌词存在的情况
                paint.setTextSize(SIZEWORD);
                paintHL.setTextSize(SIZEWORD);
                LyricObject temp = lrc_map.get(lrcIndex);
                canvas.drawText(temp.getLrc(),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.getLrc(), 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.getLrc(),mX, offsetY+(SIZEWORD+INTERVAL)*i, paint);
                } 
            }else {
                paint.setTextSize(25);
                canvas.drawText("找不到歌词", mX, 310, paint);
            }
            super.onDraw(canvas);
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            // TODO Auto-generated method stub
            float tt = event.getY();
            switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                touchX = event.getX();
                break;
            case MotionEvent.ACTION_MOVE:
                touchY = tt - touchY;
                offsetY = touchY + offsetY;
                break;
            case MotionEvent.ACTION_UP:
                blScrollView = false;
                break;
    
            default:
                break;
            }
            
            touchY = tt;
            
            return true;
        }
        
        /** 
         * 根据歌词里面最长的那句来确定歌词字体的大小 
         *
         */
        public void SetTextSize(){
            if(!blLrc){
                return;
            }
            int max = lrc_map.get(0).getLrc().length();
            for(int i=1;i<lrc_map.size();i++){
                LyricObject temp = lrc_map.get(i);
                int length = temp.getLrc().length();
                if(max < length){
                    max = length;
                }
            }
            
            SIZEWORD=320/max; 
        }
    
        @Override
        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
            // TODO Auto-generated method stub
            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;  
            }  
            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.getBegintime() < 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.setBegintime(currTime);  
                                item1.setLrc("");  
                                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.setBegintime(currTime);  
                                item1.setLrc(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;  
                    int delta = val.getBegintime() - oldval.getBegintime();
                    
                    item1.setTimeline(delta);
                    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 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.ct.lrc.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>

    三、mainactivty

    package com.ct.lrc;
    
    import java.io.IOException;
    
    import android.app.Activity;
    import android.media.MediaPlayer;
    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 miniStart;  
         private SeekBar seekBar;  
         private String mp3Path;  
         private int INTERVAL=45;//歌词每行的间隔  
        
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.main);
            
            mp3Path = Environment.getExternalStorageDirectory().getAbsolutePath() + "/lyq.mp3";
            System.out.println("ct test------------->mp3Path  "+mp3Path);
            
            lyricView = (LyricView) findViewById(R.id.mylrc);  
            mediaPlayer = new MediaPlayer();  
            
            ResetMusic(mp3Path);
            SerchLrc(); 
            lyricView.SetTextSize(); 
            miniStart = (Button) findViewById(R.id.button);  
            miniStart.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));  
      
                    }  
                }  
            });  
            
            miniStart.setOnClickListener(new OnClickListener() {  
                  
                @Override  
                public void onClick(View v) {  
                    // TODO Auto-generated method stub  
                    if (mediaPlayer.isPlaying()) {  
                        miniStart.setText("播放");  
                        mediaPlayer.pause();  
                    } else {  
                        miniStart.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(); 
            
            
        }
        
        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(); // 更新视图  
            }  
        };  
        
        private 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();  
            }  
        }
        
        public void SerchLrc() {  
            String lrc = mp3Path;  
            lrc = lrc.substring(0, lrc.length() - 4).trim() + ".lrc".trim();  
            LyricView.read(lrc);  
            lyricView.SetTextSize();  
            lyricView.setOffsetY(350);  
        }  
    }

    四、LyricObject

    package com.ct.lrc;
    
    public class LyricObject {
             public int begintime; // 开始时间  
            public int endtime; // 结束时间  
            public int timeline; // 单句歌词用时  
            public String lrc; // 单句歌词  
    }
  • 相关阅读:
    MySQL的事务和视图
    MySQL中的常用函数
    高级查询(一)
    街道管理系统
    深入.NET平台和C#编程的错题
    appium python下的API方法
    Charles 使用教程
    appium,iOS下,ios_predicate等定位方式
    Linux -常用命令
    python-列表以及相关函数认识
  • 原文地址:https://www.cnblogs.com/ct732003684/p/2862977.html
Copyright © 2011-2022 走看看