Android 自定义歌词滚动
生活随笔
收集整理的這篇文章主要介紹了
Android 自定义歌词滚动
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
本篇博客主要是通過存放本地的一篇.txt歌詞,和這個(gè).txt文件對(duì)應(yīng)的一首歌,然后點(diǎn)擊播放后,根據(jù)lrcview分割線來判斷歌詞滑動(dòng)到那個(gè)線的時(shí)候播放,支持手勢(shì)滑動(dòng),歌詞暫停,如果有資源的話,做一個(gè)音樂播放器應(yīng)該也不是問題,現(xiàn)在車載系統(tǒng)很需要這些控件,學(xué)會(huì)的話,可以從事以下其他技能。
1丶在配置清單當(dāng)中加入權(quán)限:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
2丶在Assets里面加入歌和歌詞:
? ? ?( ?看自己喜歡什么歌 ,在這里需要下載一首歌,和一首歌的歌詞)
在我的上一篇文章自定義雙指放大,有詳細(xì)的自定義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;@Overrideprotected 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);//根據(jù)播放的進(jìn)度,時(shí)時(shí)跟新歌詞}};};Thread thread=new Thread(new Runnable() {@Overridepublic void run() { // TODO Auto-generated method stubwhile (timeFlag) {try {Thread.sleep(1000);} catch (Exception e) {}handler.sendEmptyMessage(0);}}});private void initview() { // TODO Auto-generated method stubList<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}}//歌曲播放時(shí),根據(jù)拖動(dòng)跨越的行數(shù)里面的時(shí)間快進(jìn)或快退帶時(shí)間對(duì)應(yīng)的播放進(jìn)度@Overridepublic void call(long time) {if (mediaPlayer.isPlaying()) {mediaPlayer.seekTo((int) time);}}@Overrideprotected void onDestroy() { // TODO Auto-generated method stubsuper.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;//歌詞數(shù)據(jù)源private int nowColor=Color.YELLOW;//正在播放的歌詞顏色private int normalColor=Color.WHITE;//其他歌詞的顏色private int lineColor=Color.CYAN;//分割線及時(shí)間顯示的顏色private float textSize=20f;//歌詞字體的大小private float timeSize=15f;//時(shí)間顯示的大小private int lineHeight=2;//分割線的高度private int marginHeight=10;//歌詞與歌詞之間的間隔private int height;//自定義視圖的高度private int width;//自定義視圖的寬;private int index=0;//正在播放的歌詞的行數(shù)private String tipstr="暫無歌詞";//默認(rèn)情況下的歌詞private boolean TouchFlag=false;//手指按下的標(biāo)志:當(dāng)手指滑動(dòng)的時(shí)候,界面不進(jìn)行刷新//回調(diào)接口private MedCallBack medCallBack;private float lasty=0;//最后手指按下的坐標(biāo)不public LrcView(Context context) {super(context); // TODO Auto-generated constructor stub}public LrcView(Context context, AttributeSet attrs) {super(context, attrs); //實(shí)例化畫筆, 抗鋸齒paint=new Paint(Paint.ANTI_ALIAS_FLAG);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // TODO Auto-generated method stubsuper.onMeasure(widthMeasureSpec, heightMeasureSpec); //getheight:獲得界面內(nèi)的高度 //getgetMeasuredHeight:獲得視圖實(shí)際測(cè)量的高度height=getMeasuredHeight();width=getMeasuredWidth();}@Overrideprotected void onDraw(Canvas canvas) { // TODO Auto-generated method stubsuper.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);}}@Overridepublic boolean onTouchEvent(MotionEvent event) {if (list==null) {return true;}if (list.size()==0) {return true;} //手指按下if (event.getAction()==MotionEvent.ACTION_DOWN) {TouchFlag=true;//顯示時(shí)間和分割線lasty=event.getY();//獲取你手按下的Y軸坐標(biāo)//手指滑動(dòng) }else if (event.getAction()==MotionEvent.ACTION_MOVE) {float nowY=event.getY();//當(dāng)前手指滑動(dòng)到的Y值float disY=nowY-lasty;//計(jì)算手指滑動(dòng)的Y軸的距離//判斷滑動(dòng)的距離跨越幾行歌詞if (Math.abs(disY)>marginHeight) {int num=(int)(Math.abs(disY)/(marginHeight+textSize));if (num>=1) {if (disY<0) { //快進(jìn)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;//調(diào)用接口if (medCallBack!=null) {medCallBack.call(list.get(index).time);}}invalidate();//刷新布局return true;}//查找歌詞的方法,根據(jù)播放的進(jìn)度跟新歌詞 ,根據(jù)傳入的參數(shù)進(jìn)行當(dāng)前歌曲進(jìn)度的跳播public void LrcToPlayer(long time){if (list==null) {return;}if (list.size()==0) {return;}if (TouchFlag) {return;}//遍歷整個(gè)歌詞集合,尋找time的插入?yún)^(qū)間for (int i = 0; i < list.size(); i++) {LrcRow lrcRow=list.get(i);//當(dāng)前的歌詞對(duì)象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{//接口回調(diào)吧時(shí)間回調(diào)到主啊抽屜activity中去更新歌曲播放進(jìn)度public void call(long time);}//設(shè)置歌詞的方法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"); //將字節(jié)輸入流轉(zhuǎn)化為字符流BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(inputStream));String line=null;while ((line=bufferedReader.readLine())!=null) {LrcRow lrcRow=new LrcRow();//創(chuàng)建每行封裝歌詞的對(duì)象 //獲取新的解析封裝好的歌詞 添加到集合中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 {//每行歌詞的封裝類:每行歌詞的javaBeanpublic String row;//歌詞public String str_timer;//字符串格式的時(shí)間public long time;//每行歌詞的毫秒時(shí)間//獲取每行歌詞的 及歌詞解析的方法 public LrcRow getRow(String str){if (str==null) {return null;}if (str.equals("")) {return null;} //!=9,將 歌詞中不是歌詞的那部分給過濾掉,因?yàn)楦柙~的時(shí)間字符串格式都是第九個(gè)位置為]if (str.indexOf("]")!=9) {//index of 返回指定字符串在str中第一次出現(xiàn)的索引return null;} //獲取每行的主題歌詞row=str.substring(str.indexOf("]")+1); //獲取字符串格式的歌詞時(shí)間 截取的時(shí)候包含開頭,不包含結(jié)尾str_timer=str.substring(1,str.indexOf("]")); //字符替換:. 替換成:---》給字符串分割的時(shí)候提供分割標(biāo)志String newTime=str_timer.replace(".", ":"); //字符串的分割:自動(dòng)分割成一個(gè)string類型的數(shù)組String[]arr=newTime.split(":"); //將字符串格式的時(shí)間轉(zhuǎn)換為毫秒格式的時(shí)間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.LrcViewandroid:id="@+id/mylrcview"android:layout_height="match_parent"android:layout_width="match_parent"/> </RelativeLayout>?
總結(jié)
以上是生活随笔為你收集整理的Android 自定义歌词滚动的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: WORKNC基础到进阶视频教程
- 下一篇: Android压缩图片到100K以下并保