Android 自定义歌词滚动

本篇博客主要是通过存放本地的一篇.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>

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值