# 1.背景
? ? 還是音樂播放界面,實現倒計時和進度條功能,基本實現過程: 當打開MusicActivity 的時候,MusicService會發送廣播給MusicActivity ,后開始當前播放的時間進度,從而實現倒計時和進度條;
? ? 這里說明下 進度條是 從小到大 ,倒計時是 從大到小 ;
? ? 效果展示 :
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
# 2.倒計時實現
? ? ? 實現通過CountDownTimer實現,提供了start()和cancel() 兩個方法,可以開始倒計時和取消倒計時,但是,(Android5.0以下)不可以停止,這是非常不給力的;
### ? ? ?(1)解決方法1
? ? ? ? ? ? ? 在使用的時候,每次更新,將CountDownTimer 對象先調用cancel()方法,后進行銷毀(賦值為null),重新創建和初始化時間,并start();
### ? ? ?(2)解決辦法2
? ? ? ? ? ? ? 在網上查閱資料后,有人提供了android5.0的CountDownTimmer 源碼,使用這個可以cancel(); 但是我沒有成功;
? ? ? ? 源碼分享:
~~~
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.labelnet.ui;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
/**
* Schedule a countdown until a time in the future, with regular notifications
* on intervals along the way.
*
* Example of showing a 30 second countdown in a text field:
*
* <pre class="prettyprint">
* new CountdownTimer(30000, 1000) {
*
* public void onTick(long millisUntilFinished) {
* mTextField.setText("seconds remaining: " + millisUntilFinished / 1000);
* }
*
* public void onFinish() {
* mTextField.setText("done!");
* }
* }.start();
* </pre>
*
* The calls to {@link #onTick(long)} are synchronized to this object so that
* one call to {@link #onTick(long)} won't ever occur before the previous
* callback is complete. This is only relevant when the implementation of
* {@link #onTick(long)} takes an amount of time to execute that is significant
* compared to the countdown interval.
*/
public abstract class CountDownTimer {
/**
* Millis since epoch when alarm should stop.
*/
private final long mMillisInFuture;
/**
* The interval in millis that the user receives callbacks
*/
private final long mCountdownInterval;
private long mStopTimeInFuture;
private boolean mCancelled = false;
/**
* @param millisInFuture
* The number of millis in the future from the call to
* {@link #start()} until the countdown is done and
* {@link #onFinish()} is called.
* @param countDownInterval
* The interval along the way to receive {@link #onTick(long)}
* callbacks.
*/
public CountDownTimer(long millisInFuture, long countDownInterval) {
mMillisInFuture = millisInFuture;
mCountdownInterval = countDownInterval;
}
/**
* Cancel the countdown.
*
* Do not call it from inside CountDownTimer threads
*/
public final void cancel() {
mHandler.removeMessages(MSG);
mCancelled = true;
}
/**
* Start the countdown.
*/
public synchronized final CountDownTimer start() {
if (mMillisInFuture <= 0) {
onFinish();
return this;
}
mStopTimeInFuture = SystemClock.elapsedRealtime() + mMillisInFuture;
mHandler.sendMessage(mHandler.obtainMessage(MSG));
mCancelled = false;
return this;
}
/**
* Callback fired on regular interval.
*
* @param millisUntilFinished
* The amount of time until finished.
*/
public abstract void onTick(long millisUntilFinished);
/**
* Callback fired when the time is up.
*/
public abstract void onFinish();
private static final int MSG = 1;
// handles counting down
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
synchronized (CountDownTimer.this) {
final long millisLeft = mStopTimeInFuture
- SystemClock.elapsedRealtime();
if (millisLeft <= 0) {
onFinish();
} else if (millisLeft < mCountdownInterval) {
// no tick, just delay until done
sendMessageDelayed(obtainMessage(MSG), millisLeft);
} else {
long lastTickStart = SystemClock.elapsedRealtime();
onTick(millisLeft);
// take into account user's onTick taking time to execute
long delay = lastTickStart + mCountdownInterval
- SystemClock.elapsedRealtime();
// special case: user's onTick took more than interval to
// complete, skip to next interval
while (delay < 0)
delay += mCountdownInterval;
if (!mCancelled) {
sendMessageDelayed(obtainMessage(MSG), delay);
}
}
}
}
};
}
~~~
### ? ? ? (3)倒計時實現
? ? ? ? ? ? ? ?作用:
? ? ? ? ? ? ? ? ? ? ? ? 1)倒計時時間顯示
? ? ? ? ? ? ? ? ? ? ? ? 2)進度條實時更新顯示
? ? ? ? ? ? ? ? ? ? ? ? 3)歌詞進度顯示
? ? ? ? ? ? ? ? ? ? ? ? 4)播放結束 :進度條設置為0,時間設置為總時長,回調LrcView播放結束,進行歌詞初始化,顯示第一行。
~~~
/**
* 倒計時
*/
private class CountDownTime extends CountDownTimer {
private double second = 0;
public CountDownTime(long millisInFuture, long countDownInterval) {
super(millisInFuture, countDownInterval);
}
@Override
public void onTick(long millisUntilFinished) {
//倒計時顯示操作
second = millisUntilFinished / 1000;
tv_time_sheng.setText(TimeUtil.getMinuteBySecond((int) second));
// 進度條實現更新操作
second = (allSecond - second) / allSecond * 100;
//
progressbar_music.setProgress((int) second);
// 歌詞更新操作
second = allSecond * 1000 - millisUntilFinished;
// Log.d("MaskMusic", "geci : "+(long)second);
lrc.updateTime((long) second);
// lrcplaytoend.playToPause((long)
// (allSecond*1000-millisUntilFinished));
}
@Override
public void onFinish() {
// showToast("MusicActivity 播放完畢");
lrc.destroyDrawingCache();
// 播放完畢顯示歌詞
// showLrc();
// 播放完畢需要進行 ,初始化界面 1.進度條初始值,2.歌詞回歸到第一行 3.時間恢復到總時間
// 播放中 ,暫停恢復 : 1.進度條進度保持 2.歌詞保持位置 3.時間保持(可以從MusicService獲取)
progressbar_music.setProgress(0);
tv_time_sheng.setText(TimeUtil.getMinuteBySecond((int) allSecond));
allSecond = 0;
lrcplaytoend.playToEnd();
}
}
~~~
### ? ? ? ?(4)一個單獨的方法來初始化倒計時
~~~
private void CountDown(int allTime) {
countDown = new CountDownTime(allTime, COUNT_DOWN_INTERVAL);
}
~~~
### ? ? ? (5)初始化倒計時所需要的判斷
? ? ? ? ? ? ? ? ?410001不需要看,里面的countDown是CountDownTimmer的對象,清楚該對象,重新重建倒計時,這是這里面使用的;
~~~
if (code == 41001) {
// 初始化 時間
if (countDown != null) {
countDown.cancel();
countDown = null;
}
CountDown(mm.getSeconds() * 1000);
} else {
// 銷毀上一個對象
if (countDown != null) {
countDown.cancel();
countDown = null;
}
// 倒計時同步
currentTime = intent.getIntExtra(
MUSIC_SERVICE_TO_ACTIVITY_NOWTIME, 0);
CountDown(currentTime);
}
~~~
### ? ? ? ? ?(6)將秒轉化為分
~~~
/**
* 1.秒轉分
*/
public static String getMinuteBySecond(int seconds) {
StringBuffer buffer = new StringBuffer();
int second = seconds % 60;
int minute = seconds / 60;
if (minute <= 9) {
buffer.append("0" + minute + ":");
} else {
buffer.append(minute + ":");
}
if (second <= 9) {
buffer.append("0" + second);
} else {
buffer.append(second);
}
return buffer.toString();
}
~~~
# 3.進度條實現
### ? ?(1)布局實現
? ? ? ? ? ? ?直接使用的是系統的ProgressBar , 不是很漂亮但很是可以使用的;
~~~
<ProgressBar
android:id="@+id/progressbar_music"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/tv_time_sheng"
android:layout_centerInParent="true"
android:max="100"
android:progress="50" />
~~~
### ? ? (2)控制實現
? ? ? ? ? ? ?思路是倒計時在執行的時候,總時間減去當前的時間,后將時間換位百分制來使用,實時更新,如倒計時實現;
# 4.總結
? ?倒計時和進度條的實現是相對簡單的,主要是倒計時的實現,它決定了歌詞顯示,進度條顯示,倒計時顯示,三個主要的模塊;在使用過程中遇到的問題就是上面的無法cancel() 。當然倒計時也可以自己去封裝一個類使用,這里就不實現了。
- 前言
- Android實戰 - 音心音樂播放器 (開啟篇)
- Android實戰 - 音心音樂播發器 (主界面實現)
- Android實戰 - 音心播放器 (Music Service 實現)
- Android實戰 - 音心播放器 (通知實現音樂的播放/暫停/下一曲控制)
- Android實戰 - 音心播發器 (MusicService ,Notification, MainActivity 總結)
- Android實戰 - 音心播放器 (MusicActivity-音樂播放頁面界面實現)
- Android實戰 - 音心播放器 (MusciActivity-專輯圖片獲得,基本控制實現)
- Android實戰 - 音心播放器(MusicActivity - 歌詞實現)
- Android實戰 - 音心播放器 (MusicActivity - 倒計時 ,進度條實現)
- Android實戰 - 音心播放器 (MusicActivity ,MusicNotification,MusicService總結)
- Android實戰 - 音心播放器 (MusicListActivity - 分類信息界面實現)
- Android實戰 - 音心播放器 (MusicListActivity - 音樂播放和MainActivity的一個問題)
- Android實戰 - 音心播放器 (啟動頁與社交分享(ShareSDK))
- Android實戰 - 音心播放器 (優化Service退出,按兩下退出應用實現)
- Android實戰 - 音心播放器 (項目總結,應用打包發布)