歌词滚动"/>
歌词滚动
1.以前的滚动只是安行来刷新,现在不是按行来滚动了,其实就是在一定时间内整体往上移动,比如说在1S内刷新10次,由于认得肉眼看起来像是滚动。关键代码如下:
view plaincopy to clipboardprint?
float plus = currentDunringTime == 0 ? 30
: 30
+ (((float) currentTime - (float) sentenctTime) / (float) currentDunringTime)
* (float) 30;
// 向上滚动 这个是根据歌词的时间长短来滚动,整体上移
canvas.translate(0, -plus);
plus就是每次移动的大小,它就是根据歌词所持续的时间来计算的,时间越长,plus的值就越小。具体的看代码了。
下面就是获取歌词的信息:
view plaincopy to clipboardprint?
public void updateIndex(long time) {
this.currentTime = time;
// 歌词序号
index = mLyric.getNowSentenceIndex(time);
if (index != -1) {
Sentence sen = Sentencelist.get(index);
sentenctTime = sen.getFromTime();
currentDunringTime = sen.getDuring();
}
以下就是跟新线程:
view plaincopy to clipboardprint?
class UIUpdateThread implements Runnable {
long time = 100; // 滚动速度
public void run() {
while (mp.isPlaying()) {
lyricView.updateIndex(mp.getCurrentPosition());
mHandler.post(mUpdateResults);
try {
Thread.sleep(time);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
网上androi播放器虽然挺多,感觉提供的歌词显示功能比较死板,要么搜索给的条件死死的,要么放置sdcard内部的歌词格式需要统一,应该提供类似文件夹浏览的功能。^_^,不过在这之前先搞定歌词的现实界面:
转载请注明
播放器的歌词界面实现以下几个功能
- 根据歌曲的播放进度自下而上滚动;
- 提供上下拖动调整歌曲进度的功能;
- 突出显示当前进度的歌词段,并保证该歌词段处于布局中心
不多说了直接贴代码,首先开启一个线程每隔一段时间往view中送入一串字符
Java代码- import android.os.Bundle;
- import android.os.Handler;
- import android.view.View;
- import android.view.View.OnClickListener;
- import android.widget.Button;
- public class TextAlign extends GraphicsActivity implements OnClickListener {
- private SampleView mView;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- // Animation in = AnimationUtils.loadAnimation(this, R.anim.push_up_in);
- // mView.setAnimation(in);
- setContentView(R.layout.main);
- mView =(SampleView) findViewById(R.id.text01);
- Button bt = (Button) findViewById(R.id.Button01);
- bt.setOnClickListener(this);
- new Thread(new UIUpdateThread()).start();
- }
- class UIUpdateThread implements Runnable {
- long time = 40000;
- long sleeptime = 100;
- public void run() {
- try {
- while (time < 200000) {
- Thread.sleep(sleeptime);
- mView.updateIndex(time);//.index = mLyric.getNowSentenceIndex(time);
- // Log.v("UIThread", mView.index + ":" + time);
- time += sleeptime;
- mHandler.post(mUpdateResults);
- }
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- Handler mHandler = new Handler();
- Runnable mUpdateResults = new Runnable() {
- public void run() {
- mView.invalidate(); // 更新视图
- }
- };
- @Override
- public void onClick(View v) {
- mView.mTouchHistoryY -=30;
- mHandler.post(mUpdateResults);
- }
- }
这里将时间送到SampleView中,该类对此时间进行加工得到一系列list(该list是动态生成的),从而根据时间的推移递增的得到一系列的字串。这个过程模拟了歌词的显示过程
接下来的SampleView继承了TextView并重载了onDraw方法.注意,这里只给了个sample,里面歌词怎么生成的见YOYOPlayer。这里就不给代码了
Java代码- import java.io.File;
- import java.util.List;
- import android.content.Context;
- import android.graphics.Canvas;
- import android.graphics.Color;
- import android.graphics.Paint;
- import android.graphics.Path;
- import android.graphics.Typeface;
- import android.util.AttributeSet;
- import android.util.Log;
- import android.view.MotionEvent;
- import android.widget.TextView;
- import android.widget.Toast;
- import com.android.lyric.Lyric;
- import com.android.lyric.PlayListItem;
- import com.android.lyric.Sentence;
- public class SampleView extends TextView {
- private Paint mPaint;
- private float mX;
- private static Lyric mLyric;
- private Path mPath;
- private Paint mPathPaint;
- public String test = "test";
- public int index = 0;
- private List<Sentence> list;
- private float mTouchStartY;
- private float mTouchCurrY;
- public float mTouchHistoryY;
- private int mY;
- private long currentTime;
- private long currentDunringTime;
- private long sentenctTime;
- private float middleY;
- private String middleContent="Empty";
- private static final int DY = 30;
- private static void makePath(Path p) {
- p.moveTo(10, 0);
- p.cubicTo(100, -50, 200, 50, 300, 0);
- }
- public SampleView(Context context) {
- super(context);
- init();
- }
- public SampleView(Context context,AttributeSet attr) {
- super(context,attr);
- init();
- }
- public SampleView(Context context,AttributeSet attr,int i) {
- super(context,attr,i);
- init();
- }
- private void init() {
- setFocusable(true);
- PlayListItem pli = new PlayListItem("", "", 1000L, true);
- mLyric = new Lyric(new File("/sdcard/M0005044007.lrc"), pli);
- list = mLyric.list;
- mPaint = new Paint();
- mPaint.setAntiAlias(true);
- mPaint.setTextSize(20);
- mPaint.setTypeface(Typeface.SERIF);
- mPath = new Path();
- makePath(mPath);
- mPathPaint = new Paint();
- mPathPaint.setAntiAlias(true);
- mPathPaint.setColor(0x800000FF);
- mPathPaint.setStyle(Paint.Style.STROKE);
- }
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
- canvas.drawColor(Color.WHITE);
- Paint p = mPaint;
- float x = mX;
- float plus =currentDunringTime==0?index*30: index*30 +(((float)currentTime - (float)sentenctTime)/(float)currentDunringTime)*(float)30;
- float y = mY- plus+mTouchCurrY - mTouchStartY+mTouchHistoryY;
- canvas.translate(0,y);
- for (int i = 0; i < index; i++) {
- String text = list.get(i).getContent();
- if((y+i*30)<=middleY&&(y+i*30+30)>=middleY)
- middleContent = text;
- p.setTextAlign(Paint.Align.CENTER);
- canvas.drawText(text, x, 0, p);
- // mY- mY/lines*(index - i)
- canvas.translate(0, DY);
- }
- }
- @Override
- protected void onSizeChanged(int w, int h, int ow, int oh) {
- super.onSizeChanged(w, h, ow, oh);
- mX = w * 0.5f; // remember the center of the screen
- mY = h;
- middleY = h*0.5f;
- }
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- float y = event.getY();
- switch (event.getAction()) {
- case MotionEvent.ACTION_DOWN:
- mTouchHistoryY += mTouchCurrY - mTouchStartY;
- mTouchStartY =mTouchCurrY= y;
- invalidate();
- break;
- case MotionEvent.ACTION_MOVE:
- mTouchCurrY = y;
- invalidate();
- break;
- case MotionEvent.ACTION_UP:
- Log.v("Lyric content", middleContent.length()+"");
- CharSequence chars = new CharSequence(){
- @Override
- public char charAt(int index) {
- // TODO Auto-generated method stub
- return middleContent.charAt(index);
- }
- @Override
- public int length() {
- // TODO Auto-generated method stub
- return middleContent.length();
- }
- @Override
- public CharSequence subSequence(int start, int end) {
- // TODO Auto-generated method stub
- return middleContent.subSequence(start, end);
- }
- @Override
- public String toString(){
- return middleContent;
- }
- };
- Toast toast = Toast.makeText(SampleView.this.getContext(),chars, 1000);
- toast.show();
- invalidate();
- break;
- }
- return true;
- }
- public void updateIndex(long time) {
- this.currentTime = time;
- index = mLyric.getNowSentenceIndex(time);
- Sentence sen = list.get(index);
- currentDunringTime = sen.getDuring();
- sentenctTime = sen.getFromTime();
- }
- }
Android 实现歌词同步
歌词同步的原理其实很简单:
歌词显示是一个线程, 音乐播放时一个线程,通过播放时间将歌词显示进度与播放进度同步起来。
网络标准的歌词格式是LRC. 我们看下一个LRC文档,其格式就一目了然了.
[ti:爱]
[ar:小虎队]
[al:华纳国语情浓13首]
[by:爱上你了音乐网]
[02:08.00][00:38.00]把你的心、我的心串一串
[02:11.00][00:41.00]串一株幸运草、串一?同心圆
[02:16.00][00:46.00]让所有期待未?的呼唤
[02:19.00][00:49.00]趁青春做?伴
[03:16.00][02:24.00][00:53.00]?让年轻越长大越孤单
[03:19.00][02:27.00][00:56.00]把我的幸运草种在你的梦田
[03:23.00][02:31.00][01:01.00]让地球随我?的同心圆
….
LRC 格式为 [歌词显示起始时间][歌词显示结束时间]歌词内容。
了解歌词同步原理,我们可以想到要做如下工作:
1. LRC 解析
2. LRC 歌词显示
3. 歌词与播放音乐同步
4. 歌词的获取
一 LRC解析
这边我推荐YOYOPlayer 音乐播放开源项目. 源代码已经非常好的支持了LRC解析. 我拿过来就用了.
过程大概如此: 把LRC文件读到内存里面,用 sentence数据结构存放. Sentence里面有 Fromtime, Totime, content三个成员变量。显示的时候需要这些数据。
二. LRC歌词显示
歌词的绘制通过重写 OnDraw方法.
绘制的代码贴出来:
long t = tempTime;
int index = getNowSentenceIndex(t);
if (index == -1) {
return;
}
Sentence now = list.get(index);
float f = (t - now.getFromTime()) * 1.0f
/ (now.getToTime() - now.getFromTime());
if (f > 0.98f) {
f = 0.98f;
}
Shader shader = new LinearGradient(0, 0,
now.getContentWidth(mTxtPaint), 0, new int[] { Color.RED,
Color.BLUE }, new float[] { f, f + 0.01f },
TileMode.CLAMP);
mTxtPaint.setShader(shader);
canvas.drawText(now.getContent(), 0, 20, mTxtPaint);
上面的代码很简单,关键一个函数是getNowSentenceIndex(t). 通过播放时间来获得歌词的索引. 我们看下getNowSentenceIndex()如何实现.
/**
* 得到当前正在播放的那一句的下标 不可能找不到,因为最开头要加一句 自己的句子 ,所以加了以后就不可能找不到了
*
* @return 下标
*/
private int getNowSentenceIndex(long t) {
for (int i = 0; i < list.size(); i++) {
if (list.get(i).isInTime(t)) {
return i;
}
}
// throw new RuntimeException("竟然出现了找不到的情况!");
return -1;
}
还有一个歌词渐变的效果,其关键代码在与对画笔的设置,如下.
Shader shader = new LinearGradient(0, 0,
now.getContentWidth(mTxtPaint), 0, new int[] { Color.RED,
Color.BLUE }, new float[] { f, f + 0.01f },
TileMode.CLAMP);
mTxtPaint.setShader(shader);
三. 歌词与播放音乐同步
音乐播放的时候,同时启动歌词显示线程. 将音乐播放的时间设置到歌词View的成员变量中, 以达到同步的目的。
private class MyHandler extends Handler {
@Override
public void handleMessage(Message msg) {
// Log.v("#################hahah", "" + mp.getCurrentPosition());
// 重画
lrcView.mLyric.setTime(mp.getCurrentPosition());
lrcView.invalidate();
}
}
四. 歌词的获取
歌词的获取是通过百度搜索出来的. 具体代码如下,很好看懂,关键代码如下:
GetMethod get = new GetMethod("=" + URLEncoder.encode("filetype:lrc " + key, "GBK"));
get.addRequestHeader("Host", "www.baidu.com");
get.addRequestHeader("User-Agent", "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.8.1.11) Gecko/20071127 Firefox/2.0.0.11");
get.addRequestHeader("Accept", "text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5");
get.addRequestHeader("Accept-Language", "zh-cn,zh;q=0.5");
get.addRequestHeader("Keep-Alive", "300");
get.addRequestHeader("Referer", "/");
get.addRequestHeader("Connection", "keep-alive");
int i = http.executeMethod(get);
更多推荐
歌词滚动
发布评论