android里教你如何制作一个音乐同步显示歌词的应用_场所设备_音频应用论坛 - Powered by AUIOAPP

音频应用论坛

 找回密码
 快速注册

QQ登录

只需一步,快速开始

搜索
热搜: 音频应用

[资讯] android里教你如何制作一个音乐同步显示歌词的应用

[复制链接]
羊二羊 发表于 2008-5-14 03:29:13

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有帐号?快速注册

x
android里教你如何制作一个音乐同步显示歌词的应用
+ R: z) T: W) K& R% I, U% `, b" g
/ O! d" @& N' D- W" G: x+ c最近在做一款android手机上的英语听力播放器,学习到了很多东西,像是Fragment,ActionBar的使用等等,这里就先介绍一下歌词同步的实现问题。
$ @6 }& b8 x3 P6 l+ |
# M8 u# T- I) T/ |歌词同步的实现思路很简单:获取歌词文件LRC中的时间和歌词内容,然后在指定的时间内播放相应的内容。获取不难,难就在于如何在手机屏幕上实现歌词的滚动。
9 J& w% j' n* ], {9 X
9 }- {2 @; G& ]  ~& z# Q* @先上效果图:
! o* K7 ?/ F7 r4 z5 j; ^  U4 t/ t
  T' u- ?" d( X% E9 b/ w3 D( u- g: n1 r( ?2 }( }( U: P0 j

. N) b' v, F+ x8 A7 r/ f0 m8 u$ h8 j* _# N2 ]4 w/ g4 V2 z
我先准备一个歌词文件,先大概看一看里面的内容:" v$ a* a) j. g. k& d: g
; u6 K. }" ~4 s) W
[ti:兄弟干杯] 1 ]; J5 z* A( F0 x
[ar:秦博]
' b7 J3 i! A/ h; _$ a[t_time:(03:20)]
4 i% q  f  _) \  q+ s[00:00.00] 兄弟干杯
+ |) }& U6 o3 c[00:02.50] 作词:秦博! J7 O9 N, {+ s, R* k$ R- e
[00:05.00] 作曲:周宏涛
9 p5 K  V) n! h# S  I9 ?# ?! E[00:07.50] 演唱:秦博. Q7 \4 i8 a8 M3 p
[00:10.00] 编曲:龙奔: `4 Z' X$ e  s- O; [+ d$ {
0 n$ f8 S2 V7 i
2 f, ?: Q1 j) q+ d' F
[00:25.18] 男人在社会 酒一定要学会
$ C7 _5 o0 L) v! G1 K& X[00:29.25] 烦了累了失恋了 只要你来陪& L* o2 p0 X- J- I' h+ e  M# ~
[00:33.46] 酒这东西咱别说那贱与贵
3 C. p. a0 L* ?: l  M[00:37.62] 酸甜苦辣倒进杯 忘了是与非& N) }( R1 s5 p* ~6 p" C; C
[00:41.79] 现在的社会 美女是一大堆3 U; ~) }2 k( o4 ?1 g
[00:45.87] 不富不帅不理咱 不跟咱约会
0 r1 G3 L, x. X  P* W) P/ o1 m[00:50.14] 人要活着就别让那心太累
; \4 Y) ~8 N7 r  @+ O[00:54.26] 金钱名利淡如水 不如啊醉一回
) A, f8 L* O$ \! z) R$ C[00:58.52] 来来来 兄弟 干了这一杯
2 A9 f; X3 a, A3 G[01:02.62] 谁大谁小无所谓 有酒今朝醉1 N( Z6 j: \, d7 r8 c, ?
[01:06.80] 来来来 兄弟 干了这一杯
! h2 m2 J/ r9 W: |" A% U/ @1 c[01:10.94] 出门在外不容易 辛酸讲给谁
" \/ ^6 f! U/ w  z6 Y[01:15.14] 来来来 兄弟 干了这一杯# n+ O2 b' X- ?- s& r
[01:19.29] 别管天南和地北 此时最陶醉* i2 I+ Z2 a4 Y! \8 r# h
[01:23.50] 来来来 兄弟 干了这一杯/ i% d* O8 l7 ]+ |: t
[01:27.65] 酒逢知己千杯少 人生醉几回: D7 I% }. [" }& P4 V
[01:31.95] RAP:深夜街头来买醉 究竟为了谁
6 F; z! Q8 E& [  Y2 f3 w[01:36.02] 看破红尘的卑微 口是与心非
! A5 \# @( t& R' b3 k: I% k[01:40.14] 种种挫折无所谓 勇敢来面对
8 H: V, I1 s: V  ?$ [1 F' H! H[01:44.30] 不枉人间走一回 男儿不流泪
9 _% C! |/ E6 j5 n8 q  j[01:48.60] 现在的社会 美女是一大堆9 r) c. p) ~, U3 ?+ }# v/ J8 ]4 n
[01:52.75] 不富不帅不理咱 不跟咱约会
- A! k) u" l( B1 i' N[01:56.88] 人要活着就别让那心太累. E/ V( n0 ~2 c; P
[02:01.03] 金钱名利淡如水 不如啊醉一回
5 v9 C4 ^+ G' O/ K* ][02:05.30] 来来来 兄弟 干了这一杯# v9 e" e+ e4 L
[02:09.39] 谁大谁小无所谓 有酒今朝醉4 m% {6 y+ d! H" Z# B
[02:13.58] 来来来 兄弟 干了这一杯
3 J. |8 G; J6 E9 X[02:17.77] 出门在外不容易 辛酸讲给谁+ B$ s1 |$ \* y, X; y) Z
[02:21.94] 来来来 兄弟 干了这一杯& d; Q- J4 {  ]8 J7 Q  _. ?# n
[02:26.11] 别管天南和地北 此时最陶醉* i4 [( a( ?" ~4 ?, I- A
[02:30.28] 来来来 兄弟 干了这一杯
; x# W, F0 u5 l3 q[02:34.42] 酒逢知己千杯少 人生醉几回
% s: C, u. x( }, ~' f" v[02:38.65] 来来来 兄弟 干了这一杯: t" I# S7 N6 |9 o9 X! C2 i
[02:42.79] 谁大谁小无所谓 有酒今朝醉) Y. o+ i- j* w' ^7 K
[02:46.96] 来来来 兄弟 干了这一杯
0 e( L- H+ K* v( X7 L[02:51.16] 出门在外不容易 辛酸讲给谁: v8 U: N$ ~1 o9 N) U
[02:55.34] 来来来 兄弟 干了这一杯" I% J# e: `" x7 j1 i2 n% M
[02:59.46] 别管天南和地北 此时最陶醉  s1 g! Z- o8 V/ `
[03:03.63] 来来来 兄弟 干了这一杯
, Z# k5 a' ~- w  ~& v# B[03:07.83] 酒逢知己千杯少 人生醉几回, P5 \- F' |. V1 |( L3 u: Y4 V. i
看到了,每段歌词前面都有一个开始时间,那一段的时间就是这一段的结束时间,那原理明白了,就开始准备代码。
 楼主| 羊二羊 发表于 2008-5-15 19:14:32
先从最基本的读取歌词文件开始:
" j  e! I$ }; ^$ L' l5 K7 _3 x# W8 |7 j/ X
Public class LrcHandle {
* x! T* e  k0 O6 I    private ListmWords = new ArrayList();
& g" f3 ^/ c6 F0 H9 [    private ListmTimeList = new ArrayList();, F4 N. k3 N' e8 q

- _8 S1 Y9 l# X$ u' A    //处理歌词文件0 o3 v$ R9 s; n: V5 {8 I  L6 U1 i
    public void readLRC(String path) {
4 F) j8 e5 \4 X5 l& k7 C( ^        File file = new File(path);" }& `& M" X* R7 ]1 d& N* r

1 j; Y' {9 F# r( ~& w0 Q. k        try {
  D& b) i0 o5 b" n9 Z% j& @6 @            FileInputStream fileInputStream = new FileInputStream(file);. X) q/ F/ P8 r# K* N* |+ b- S3 a
            InputStreamReader inputStreamReader = new InputStreamReader(% _2 O8 e  T! u7 p( P- H
                    fileInputStream, "utf-8");
  Z; c" c( D; q! m" @8 j& z            BufferedReader bufferedReader = new BufferedReader(
  B: B! V' K0 z% ^+ Q                    inputStreamReader);/ ~& _9 \7 ~% N" T* k4 l
            String s = "";' [) g, w1 C9 m4 J$ r
            while ((s = bufferedReader.readLine()) != null) {+ v1 `- D  h+ a) g, S9 W
                addTimeToList(s);
% c5 l' Q/ j/ V9 w                if ((s.indexOf("[ar:") != -1) || (s.indexOf("[ti:") != -1)/ N7 M( _5 e" K. |( s9 k
                        || (s.indexOf("[by:") != -1)) {
1 a& m+ l- n6 s# v                    s = s.substring(s.indexOf(":") + 1, s.indexOf("]"));( c8 D5 f& f  L+ }2 W
                } else {
  t) Q/ `; F! j8 q: L+ Y                    String ss = s.substring(s.indexOf("["), s.indexOf("]") + 1);- ~; S2 D! v0 o& _, ~
                    s = s.replace(ss, "");: v# ?2 h# [$ C* S. g6 b9 _
                }
( {( Q+ k/ I& @1 E                mWords.add(s);
, z2 x! ?* D8 `- M7 f            }
6 i: e, K, ?$ d8 r
3 ?# Z$ D* k2 T, _) {            bufferedReader.close();! ]9 F; T# F  h6 D' n4 a
            inputStreamReader.close();
* |- L! d: T0 v" b" `            fileInputStream.close();% \0 |  D$ H% K* c! p1 k: w7 p, I
        } catch (FileNotFoundException e) {' ]2 S4 ^& h& R: m7 s5 t8 |0 U
            e.printStackTrace();
- a- z- ?. |1 H5 i$ o& l            mWords.add("没有歌词文件,赶紧去下载");
4 Q0 T- h9 s+ v" K3 x/ K        } catch (IOException e) {
! o& n; n  W8 h' H            e.printStackTrace();3 b$ P, O4 P$ h
            mWords.add("没有读取到歌词");
5 P% }$ Q( |+ W/ ~  L$ \$ }5 ]5 `# h        }9 o! `  f7 ^( [% [  n; y/ j& x
    }" e! n5 g" \! ?- ]- s! d- n
   public ListgetWords() {
% W- @2 s! D1 G/ }; X( f/ G        return mWords;- E6 ?; G' i9 u, o: Y& V  _- t& L
   }  A0 X, \" D2 B* I. w& D- K

( V, g2 H' b1 b: k: r: n# O8 h) D6 d    public ListgetTime() {
# c: i* `! E4 _% x1 d: L& w        return mTimeList;
% Z6 G+ l, a1 m    }; A4 e1 ^" D9 ~* [! m/ X0 S" Y
$ @3 U& g' V* ^! \( \( q
    // 分离出时间! h/ K, g3 S5 }2 w0 H
    private int timeHandler(String string) {
$ c1 I; F  ^! h2 b: S       string = string.replace(".", ":");. o! O$ A, ~2 J# S8 J% F7 y9 L5 o
       String timeData[] = string.split(":");1 j# ?6 h- f% n. F
// 分离出分、秒并转换为整型% ]0 b8 H7 j0 Y( p% \0 P4 Z
        int minute = Integer.parseInt(timeData[0]);
" v% Y+ L6 F# W5 X        int second = Integer.parseInt(timeData[1]);
& z+ ]8 D  m# k6 o% J6 ?* S        int millisecond = Integer.parseInt(timeData[2]);% X# b& ?" {5 P" {' T) ~3 [: n
! }7 i2 S6 V. O* ]/ j& M
        // 计算上一行与下一行的时间转换为毫秒数1 a3 E; n( i: T% y$ n! f7 }
        int currentTime = (minute * 60 + second) * 1000 + millisecond * 10;' I5 V: I4 O4 v+ }, x! p

, \5 L+ ^# j( G' t) J2 D( K+ {        return currentTime;
0 X( u" R3 ~  E1 @) J    }
* ~" K4 a8 o+ ^
3 [8 v0 V9 T/ v, q& P9 E  _* V   private void addTimeToList(String string) {3 Z& B/ b% x3 w& s! d
        Matcher matcher = Pattern.compile(# m1 n6 C. g, n- A
                "\\[\\d{1,2}:\\d{1,2}([\\.:]\\d{1,2})?\\]").matcher(string);; `0 x' _- E2 A9 V
        if (matcher.find()) {
/ E! v1 X: }( v+ r1 r8 v% @            String str = matcher.group();
- X3 C7 J% K% L0 g8 Q            mTimeList.add(new LrcHandle().timeHandler(str.substring(1,
$ K- y# i6 r4 e( ]0 ~% G                    str.length() - 1)));! l  m5 p, `" h+ ], S+ u2 p# i
        }
. q( d/ L" Z# c& A- R4 A' [. I7 U4 ]! \4 j, V
    }
/ Y" H9 A. U9 F" X. ]}
. ^: x/ K7 g  K; G: _
 楼主| 羊二羊 发表于 2008-5-15 19:15:20
一般歌词文件的格式大概如下:
+ e3 c* a4 j. I/ O: q* _2 s0 @2 O4 \* T0 N: Q$ |$ Z" ~
[ar:艺人名]" Z' e( i' q# X+ |  Y7 W
8 x: {  D6 K# e+ O
[ti:曲名]
0 w$ `/ C  E) b: ^5 U4 x9 y6 O6 ^% w+ F6 s
[al:专辑名]
5 A! c  h5 B% E! Q
6 e4 c* \1 W  u+ {[by:编者(指编辑LRC歌词的人)]
! r8 h5 z- K$ D& d& j$ q
# t0 o. D4 ^5 e: F  v2 i! ?% m[offset:时间补偿值] 其单位是毫秒,正值表示整体提前,负值相反。这是用于总体调整显示快慢的。
7 I) {! L+ x) j9 `' a$ x+ }0 I' A7 l* M+ C
但也不一定,有时候并没有前面那些ar:等标识符,所以我们这里也提供了另一种解析方式。% o6 r- `0 F  o' _  ^$ ^' U+ N

. i/ ?9 u. q+ c1 v4 m歌词文件中的时间格式则比较统一:[00:00.50]等等,00:表示分钟,00.表示秒数,.50表示毫秒数,当然,我们最后是要将它们转化为毫秒数处理才比较方便。
6 i# h+ p( @8 t1 U1 U0 j3 V0 D  M) _+ b$ h/ F% P
处理完歌词文件并得到我们想要的数据后,我们就要考虑如何在手机上滚动显示我们的歌词并且与我们得到的时间同步了。5 g1 a  F. `3 w: x9 m0 o2 r

. K* b" A( T7 X0 S) o先是布局文件:
1 {$ O( T7 b3 s  Q6 w! w8 T9 W, c$ d' f- }, _" |0 Y) H
/ G- y" k! D8 F3 M5 L# O
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
2 l2 Z  T: G- U: k1 Exmlns:tools="http://schemas.android.com/tools": |) G5 ]+ w2 ]
android:layout_width="match_parent"( o$ s" D4 m2 n3 M- Z3 l( I6 z$ k4 v' B. Z
android:layout_height="match_parent"  s3 V) h+ u& P4 R, H) V  w
tools:context=".MainActivity" >
/ E: o+ e/ F, t5 x/ i% ~<Button  S" s9 j1 f) Q9 ~
android:id="@+id/button"
( T; X' e( t- J+ e, zandroid:layout_width="60dip"
8 M1 ^  O1 I5 }4 |& O4 }android:layout_height="60dip"6 W* H1 E2 s3 D0 c
android:text="@string/停止" />( x( W( L8 c4 h; L
<com.example.slidechange.WordView2 V0 Q& |1 M' `! ^2 k1 R  o) K9 J
android:id="@+id/text"! N4 E* p6 d" W1 x
android:layout_width="match_parent"
& T1 a9 e; T; g, q* l2 nandroid:layout_height="match_parent"* g4 q5 J- l( \. L
android:layout_below="@id/button" />. Y1 h! F3 R' f  l! t. n  w
/ B5 m& ~# u3 E9 r9 |! a
WordView是自定义的TextView,它继承自TextView:
" K* j- B& D$ h$ c0 p0 u0 Y. a# ~4 t7 ?: O" L: E7 p
public class WordView extends TextView {# {6 v+ H8 {; h+ n5 f7 n) J% l
private List mWordsList = new ArrayList();
7 I5 Q. K- @  Pprivate Paint mLoseFocusPaint;
- N! z4 K$ A& Qprivate Paint mOnFocusePaint;  j  f4 |  |" i
private float mX = 0;6 W( u% s* d, s, z
private float mMiddleY = 0;9 k$ I9 s+ l2 \' }9 S1 C
private float mY = 0;: B0 _8 V/ R; e1 {
private static final int DY = 50;
  [' w; @$ j9 D% }. q; C& }. oprivate int mIndex = 0;! W2 h: a# V6 b5 G" [
public WordView(Context context) throws IOException {
; p9 f6 Z0 k- y. Gsuper(context);4 Y% O5 }/ B0 f) J6 S+ E6 f! J! _
init();
( A$ O  l3 b. z" z}( U1 U2 U5 q0 D. ]' M+ \; i
public WordView(Context context, AttributeSet attrs) throws IOException {
( @' e. g- X5 g) c' c/ psuper(context, attrs);
" }. ^6 Z0 E% |4 Finit();
/ [; \$ ^5 O: b0 Q1 Z}
9 T, I2 H3 I; X0 p2 ^7 k& Mpublic WordView(Context context, AttributeSet attrs, int defStyle)8 O' D' R. E# g6 j7 z) ^" W1 J. V
throws IOException {; L+ @: o5 q. ^9 {3 Q) L1 E
super(context, attrs, defStyle);
8 j, s6 r7 s( L  ~* ginit();
% ^. k( r& B  n+ l}
( ~! n- Q5 P$ ?# |) t. o+ T@Override
% o4 L8 w0 F7 i) Yprotected void onDraw(Canvas canvas) {% Q( g5 C) N1 i* E
super.onDraw(canvas);" J9 b' o$ V6 O, s6 i
canvas.drawColor(Color.BLACK);' c9 h1 A4 P+ d, [4 w7 O
Paint p = mLoseFocusPaint;
) L& d! T. q% h0 h& }" Zp.setTextAlign(Paint.Align.CENTER);
3 M8 P/ ]) T4 A- X8 lPaint p2 = mOnFocusePaint;
8 `- W* E$ ~8 Y& c$ t! o% jp2.setTextAlign(Paint.Align.CENTER);
4 ]) G' ?7 ]0 g$ P/ tcanvas.drawText(mWordsList.get(mIndex), mX, mMiddleY, p2);( I" d# b3 M9 }' ~
int alphaValue = 25;
2 {9 [+ P2 F4 {& [float tempY = mMiddleY;
2 ?1 g  ]3 R2 O0 \% V, Y+ Dfor (int i = mIndex - 1; i >= 0; i--) {
6 m7 a  D5 o+ {! Y" `+ EtempY -= DY;; f" S+ C- X+ O% d: g. T1 M* Z8 Q
if (tempY < 0) {
. M  _4 k6 ?2 V% Gbreak;
$ j2 D0 F* K0 Q; U; u7 f  f2 [4 p% e}
* v+ Z' t% ^) U/ w' h4 E' xp.setColor(Color.argb(255 - alphaValue, 245, 245, 245));' f1 t9 K2 H3 K6 y7 v: D$ r7 \5 P
canvas.drawText(mWordsList.get(i), mX, tempY, p);
2 q$ L( F9 ~- h7 UalphaValue += 25;
0 M0 Z, g# [& p0 U2 o9 E: W}
, [0 R4 ?+ q2 aalphaValue = 25;
6 R8 s1 t& ?: p, k+ NtempY = mMiddleY;
! j. Y% g' W9 P- i% Q5 Q- afor (int i = mIndex + 1, len = mWordsList.size(); i < len; i++) {
* i4 e7 G0 ]% ?* R) etempY += DY;
' `4 Y* r7 v! H2 T/ m$ xif (tempY > mY) {
9 |- T! U' v1 Xbreak;! U- I8 K. O; G
}
6 v& E! K" w4 a. g% v7 J- sp.setColor(Color.argb(255 - alphaValue, 245, 245, 245));
2 J  q2 c$ _6 x, ^9 Icanvas.drawText(mWordsList.get(i), mX, tempY, p);6 R8 Z- M9 i: d0 n+ M, r
alphaValue += 25;( G, D; G" @% N" U/ ]
}  k* a4 J$ }; j. {0 @* @
mIndex++;
$ ]; R/ v2 N6 U' w8 h$ Q% l}
( I, w4 U0 n1 T; [% Y6 X9 f* N@Override
7 H$ D# s  x3 ~, Hprotected void onSizeChanged(int w, int h, int ow, int oh) {
. @- u7 n: L2 s% Fsuper.onSizeChanged(w, h, ow, oh);
7 n6 W" a1 `0 r; Z' \0 D1 y2 m) gmX = w * 0.5f;
' ~1 v3 }1 o. f8 a8 J& B! TmY = h;
9 `  z! V4 b" f% a' hmMiddleY = h * 0.3f;
; m" K8 T! m: P# K0 X}
9 b4 L& X6 }# P3 m& n8 {' y@SuppressLint("SdCardPath")/ t. u8 [  F/ {/ ^: H1 [+ N0 e
private void init() throws IOException {5 |8 s: `" M% G
setFocusable(true);
* t1 Q1 T, M! z: b1 z# gLrcHandle lrcHandler = new LrcHandle();
" m5 z4 X# B9 |9 V6 [+ s- GlrcHandler.readLRC("/sdcard/陪我去流浪.lrc");% r! u" A0 \) `/ s
mWordsList = lrcHandler.getWords();
: N9 `7 ]1 B! P* n3 M& rmLoseFocusPaint = new Paint();4 Z. v5 l( ~' P5 _
mLoseFocusPaint.setAntiAlias(true);
7 w  [9 O# b& |0 F" GmLoseFocusPaint.setTextSize(22);
0 F2 p, p+ E4 b$ Q1 emLoseFocusPaint.setColor(Color.WHITE);
# ~, g7 y2 U- g; w* q' i( u9 p+ UmLoseFocusPaint.setTypeface(Typeface.SERIF);
5 `- B1 e) y& x6 a$ g3 rmOnFocusePaint = new Paint();
; O, p; g+ I3 bmOnFocusePaint.setAntiAlias(true);: @1 B- `7 g  G5 N  z5 W
mOnFocusePaint.setColor(Color.YELLOW);
7 Q0 y" W) i" m/ TmOnFocusePaint.setTextSize(30);4 ]3 `5 F6 l3 C/ B
mOnFocusePaint.setTypeface(Typeface.SANS_SERIF);
& [7 L0 p2 w0 a1 T8 g! l- {}
- W, i0 M, f$ q) G' `: Y}" ~3 O# u; d6 M! X# C3 c- y$ |& {
* |; {" s0 @2 @
最主要的是覆盖TextView的onDraw()和onSizeChanged()。1 ^2 |) B+ b! u: T" j' \
 楼主| 羊二羊 发表于 2008-5-22 17:15:46
/ I, A9 t9 O+ }3 w
在onDraw()中我们重新绘制TextView,这就是实现歌词滚动实现的关键。歌词滚动的实现思路并不复杂:将上一句歌词向上移动,当前歌词字体变大,颜色变黄突出显示。9 S7 M+ P' B! A" G' ^
3 X! Q% M9 _& c
我们需要设置位移量DY = 50。颜色和字体大小我们可以通过设置Paint来实现。
* Z4 w6 |1 f( z8 a; ~5 F
  H3 u* u0 A) t* S3 b我们注意到,在我实现的效果中,距离当前歌词越远的歌词,就会变透明,这个可以通过p.setColor(Color.argb(255 - alphaValue, 245, 245, 245))来实现。$ [8 B+ p# A9 m( [+ I4 }! K
: N6 b" v& _2 Y; t4 X) g+ }
接着就是主代码:" d: v" T4 h, y1 H$ X

" |. f5 @, R, I4 dpublic class MainActivity extends Activity {
3 u: p1 A* f0 u! i+ f: \private WordView mWordView;
# I8 g& f# Z* {1 z. A; }private List mTimeList;% r0 q& N% @8 ?& J5 u* Z
private MediaPlayer mPlayer;  M- Y5 @( }* B8 k+ ?$ B8 t
@SuppressLint("SdCardPath")
- i: n. \# z: B8 z: k, Y2 E@Override
% k1 @0 n" M" A2 u) v5 yprotected void onCreate(Bundle savedInstanceState) {# L9 g) ^& k; K# U. N6 U$ s/ y
super.onCreate(savedInstanceState);
% C% w% @! _$ \2 @0 j+ T$ ]$ FsetContentView(R.layout.activity_main);
& C9 g; f. ?$ e' j( L! F) u5 mButton button = (Button) findViewById(R.id.button);0 G6 P) f2 _2 \0 p9 i; d0 C
button.setOnClickListener(new OnClickListener() {
7 l! l. @. X. b! r3 M: o@Override
6 t3 ]9 o- Q$ N: q, q& Vpublic void onClick(View v) {
/ e- I; I% P1 K) E9 c$ n; z  SmPlayer.stop();
! Y! b2 h2 t2 {& ?8 nfinish();
8 i- }) t6 z1 m2 A2 o6 s. i/ h: o+ u}
; R% u6 g$ i* _" {1 {. X});
, [: A* t9 E4 y" D, AmWordView = (WordView) findViewById(R.id.text);
3 U. a( R! G; X6 Q* ~. }1 nmPlayer = new MediaPlayer();
8 O' c; [2 a3 @# Z5 }mPlayer.reset();
1 }1 k1 a3 E7 oLrcHandle lrcHandler = new LrcHandle();
# i. a+ [+ e3 x- `try {
8 B6 W& ]. J5 tlrcHandler.readLRC("/sdcard/陪我去流浪.lrc");
9 T' t! P2 t7 a$ a" m3 E# ^mTimeList = lrcHandler.getTime();
8 y7 @! E& B0 U: y) G# }mPlayer.setDataSource("/sdcard/陪我去流浪.mp3");; z0 v. t( ^/ h: A# I
mPlayer.prepare();
! _) ~( }: @, R' k- Y! x9 Q} catch (IOException e) {
% X: X0 @/ i4 [0 Ue.printStackTrace();
8 N3 b0 _7 t' `2 u' b, u} catch (IllegalArgumentException e) {$ J6 m2 `- K+ K8 I0 O4 E/ V4 E
e.printStackTrace();) f+ p2 t6 f$ W: y
} catch (SecurityException e) {7 e% ?: G% _- J$ i! H# m( }
e.printStackTrace();' }4 T# g2 K5 S3 W1 ?
} catch (IllegalStateException e) {
! K; g# F' D# J+ n2 te.printStackTrace();/ c: F* \) C0 j
}6 I/ [# V- j- q) N3 ?
final Handler handler = new Handler();
& B) \: ]3 z1 c! E( O# O/ F* ~mPlayer.start();, l. Z8 {4 R  L" T' }
new Thread(new Runnable() {
* X. H8 u$ g4 v' y, hint i = 0;8 ?* I9 y6 N0 E  ^; p0 d
@Override& H! d1 K1 D6 ~* i$ x( i
public void run() {' ~4 k, _% K* Q- \" K6 d0 V3 w
while (mPlayer.isPlaying()) {
9 G6 a% K& z" @6 Dhandler.post(new Runnable() {+ D7 L9 @& H0 y5 ]. r- y
@Override
; F6 D8 {- N$ ?4 \8 Z  vpublic void run() {3 e' j# v+ {# p5 f9 ]( ?; f
mWordView.invalidate();  C* [$ ^" a0 Z+ e
}
& w7 M0 d6 E+ [5 t/ K7 i& t. N, \1 x1 t});( ^, h5 L+ Z# n4 }' {& h
try {
, h' @% R6 Q  [; d% FThread.sleep(mTimeList.get(i + 1) - mTimeList.get(i));
9 y; M0 u: U6 ~4 ~  r} catch (InterruptedException e) {
, W, C8 K' e- x1 [}
$ z( p; I5 ]" Ei++;5 O/ N2 V+ _$ f# I1 P% |7 J+ b
if (i == mTimeList.size() - 1) {, B/ y4 E" B% k# v8 R" g
mPlayer.stop();
/ [3 _' H; x( _* @( F- J7 i; L  hbreak;
% ]" A( X' A7 ?: u- K% J: r6 o6 O}, J% T- Z5 b! v6 ?4 `! ?
}5 e  M9 t. I  S1 k4 j) A
}
, T' p# O  M, E$ A  d* G6 ]; A; X}).start();
+ I- u" K& `2 s% x}. }% {7 G8 v/ c! n# J) C
}  h! W8 @* ]# ~! q% u  ?5 K6 V

8 M/ B- m3 j8 |$ f+ N8 Q歌词的显示需要重新开启一个线程,因为主线程是播放歌曲的。
6 i1 c0 q; \( D0 \8 k1 e/ q4 I8 U. O' e2 V; M
代码很简单,功能也很简单,最主要的是多多尝试,多多修改,就能明白代码的原理了。
fashaozg 发表于 2008-5-23 01:39:59
:victory: :victory: :time:
米问天 发表于 2008-7-8 17:45:34
支持,好文章,点赞
* j9 c0 s& S% h* N7 i) U. {5 m  X% {& v! K+ ~" ?' U7 R# J$ E' S- l4 V& }
您需要登录后才可以回帖 登录 | 快速注册

本版积分规则

软硬产品代理咨询服务:声卡:雅马哈UR22C、罗兰声卡、福克斯特、艾肯、阿波罗 、M-audio 、普瑞声纳 、福克斯特、 达思冠系列 音箱:吸顶NS-IW560C、吸顶NS-IW660 、NS-AW350、低音NS-SW050、低音NS-SW100;JBL 吸顶8124、CSS-8006BM;香蕉猴 gibbon系列;普瑞声纳E5XT、E4.5、 E3.5BT 有线话筒:舒尔PGA27、PGA48、MV51、mv88、mv88+、SM27 ;森海E945 、MK4、E835S;舒伯乐top248s;罗德NT1-A、VIDEOMIC、VIDEOMIC GO、VideoMicro、VideoMic NTG 无线领夹麦克风:罗德 wireless go II 一拖一、一拖二;猛犸lark150 耳机:森海HD300 pro、美奇 CR-Buds 、索尼7506、爱科技K240S、K240 MKII、K271 MKII、K52、K72、K92、先锋、飞利浦 1:飞利浦会务通/会议摄像头/全向麦克风/执法仪/录音笔 2:洋铭便携式移动演播室 / 切换台 / 摄控一体摄像机 / 虚拟演播室 / 微金课教室 / 色键器 3:逻兰音视频切换台 / 声卡 / 电子鼓 /电钢琴 /耳机 4:Blackmagic专业摄影机 /调色台 / 切换台/ 广播级转换器 / 监视器 / 采集卡 5:索尼专业摄像机/佳能专业摄像机/松下专业摄像机/ insta360专业摄像机 6:话筒:铁三角/ 舒尔/ 森海塞尔 / AKG / RODE/ BBS 7:音响:YAMAHA/ 声艺 / 皇冠 /JBL / 真力/咪宝/BOSE /美奇 8:声卡:RME/羚羊/IXI /艾肯/PreSonus普瑞声纳/Focusrite福克斯特/YAMAHA/雅马哈/ickb 9:耳机:铁三角/beyerdynamic拜亚动力/AKG爱科技/索尼/RunningMan/美技 10:思锐三脚架 /防潮箱 /米泊三脚架/意美捷三脚架/曼富图三脚架 11:XSW系列,300.500代理商,EWD数字系列代理,6000.9000定制产品,还包销了全国三个型号:XSW1-825,EW100 G4-945,EWD- kk 205

小黑屋|手机版|Archiver|音频应用 (鄂ICP备13005321号-1)

Powered by Audio app

快速回复 返回顶部 返回列表