前言
LZ-Says:今天收到轉(zhuǎn)正通知了,內(nèi)心也就12s開心柔滔,隨之而來的就是不平不淡溢陪。16年10月18號入職,感覺一切都好像眨眼之間睛廊,過得好快形真,各種忙
答應(yīng)了自己,要好好努力超全。答應(yīng)了家人咆霜,要好好奮斗。答應(yīng)了心嘶朱,要好好堅持蛾坯。有什么理由不去努力
開始正題
好吧,今天一起回顧下疏遏,關(guān)于Android中實現(xiàn)倒計時功能吧~
今天為大家介紹倆種方式脉课,都可以實現(xiàn)倒計時功能。這倆種方式分別是:
- Handler+Thread
- CountDownTimer
想必大家對于第一種實現(xiàn)方式肯定不會陌生了财异,簡直So easy那再次回顧下第一種寫法
1.通過使用Handler+Thread實現(xiàn)倒計時
首先編寫布局文件
布局文件很簡單倘零,就是一個TextView,默認(rèn)顯示Handler獲取驗證碼戳寸,點擊TextView呈驶,進(jìn)行倒計時操作,完成后恢復(fù)默認(rèn)顯示疫鹊。
<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" >
<TextView
android:id="@+id/tv_show_h"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="193dp"
android:text="Handler獲取驗證碼" />
</RelativeLayout>
放大招袖瞻,編寫Activity,實現(xiàn)效果~
public class MainActivity extends Activity {
private TextView tvShowH;
private Handler handler = new Handler() {
public void handleMessage(Message msg) {
tvShowH.setText(msg.what - 1 + "s");
if (msg.what == 0) {
// 倒計時結(jié)束讓按鈕可用
tvShowH.setEnabled(true);
tvShowH.setText("Handler獲取驗證碼");
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tvShowH = (TextView) findViewById(R.id.tv_show_h);
tvShowH.setOnClickListener(listenerH);
}
private OnClickListener listenerH = new View.OnClickListener() {
@Override
public void onClick(View v) {
tvShowH.setEnabled(false);
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 10; i >= 0; i--) {
handler.sendEmptyMessage(i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
};
}
來張效果圖瞅瞅唄~
2.通過CountDownTimer實現(xiàn)倒計時
CountDownTimer簡介
CountDownTimer是Android內(nèi)部封裝好的一個關(guān)于實現(xiàn)倒計時功能的類拆吆。所在包:package android.os;其內(nèi)部實現(xiàn)也是通過咱第一種實現(xiàn)方式聋迎,沒啥好說的,看看人家官方簡介吧
官方使用方式
new CountdownTimer(30000, 1000) {
public void onTick(long millisUntilFinished) {
mTextField.setText("seconds remaining: " + millisUntilFinished / 1000);
}
public void onFinish() {
mTextField.setText("done!");
}
}.start();
The calls to onTick(long) are synchronized to this object so that one call to onTick(long) won't ever occur before the previous callback is complete. This is only relevant when the implementation of onTick(long) takes an amount of time to execute that is significant compared to the countdown interval.
從上面官方提供例子可以看出锈拨,如果想要使用CountDownTimer去實現(xiàn)倒計時砌庄,需要如下幾個步驟:
- 實例化CountDownTimer對象;
- 提供計時時間毫秒以及時間間隔毫秒;
- 重寫onTick()和onFinish()方法娄昆;
那么這倆個方法分別都是什么作用呢佩微?
3.1 onTick(long millisUntilFinished)
參數(shù)millisUntilFinished是倒計時的剩余時間。在倒計時結(jié)束后會調(diào)用onFinish萌焰。
3.2 onFinish()
倒計時結(jié)束后需要執(zhí)行的操作可以寫在這里哺眯。- start()開始倒計時~
開始Coding
首先在原有界面新增一個TextView,操作流程都一樣扒俯。
新增后layout
<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" >
<TextView
android:id="@+id/tv_show_c"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/tv_show_h"
android:layout_centerHorizontal="true"
android:layout_marginTop="25dp"
android:text="CountDownTimer獲取驗證碼" />
<TextView
android:id="@+id/tv_show_h"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="178dp"
android:text="Handler獲取驗證碼" />
</RelativeLayout>
新增后Activity
package com.example.hlqcountdowntimer;
import android.app.Activity;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.TextView;
public class MainActivity extends Activity {
private TextView tvShowH, tvShowC;
private Handler handler = new Handler() {
public void handleMessage(Message msg) {
tvShowH.setText(msg.what - 1 + "s");
if (msg.what == 0) {
// 倒計時結(jié)束讓按鈕可用
tvShowH.setEnabled(true);
tvShowH.setText("Handler獲取驗證碼");
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tvShowH = (TextView) findViewById(R.id.tv_show_h);
tvShowH.setOnClickListener(listenerH);
tvShowC = (TextView) findViewById(R.id.tv_show_c);
tvShowC.setOnClickListener(listenerC);
}
private OnClickListener listenerH = new View.OnClickListener() {
@Override
public void onClick(View v) {
tvShowH.setEnabled(false);
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 10; i >= 0; i--) {
handler.sendEmptyMessage(i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
};
private OnClickListener listenerC = new View.OnClickListener() {
@Override
public void onClick(View v) {
tvShowC.setEnabled(false);
timer.start();
}
};
private CountDownTimer timer = new CountDownTimer(10000, 1000) {
@Override
public void onTick(long millisUntilFinished) {
long time = millisUntilFinished / 1000;
if (time == 0) {
tvShowC.setText(time + "秒后可重發(fā)");
onFinish();
}
tvShowC.setText(time + "秒后可重發(fā)");
}
@Override
public void onFinish() {
tvShowC.setEnabled(true);
tvShowC.setText("CountDownTimer獲取驗證碼");
}
};
}
來個效果圖~
不知道大家有沒有發(fā)現(xiàn)一個小問題奶卓,怎么到1秒時,會出現(xiàn)短暫延遲撼玄?而且使用CountDownTimer可以顯示0秒么夺姑?
關(guān)于以上問題,讓我們一起去看看人家是什么寫的掌猛,從他們寫的代碼中看看能不能發(fā)現(xiàn)相關(guān)蛛絲馬跡關(guān)于可以顯示0秒么這個問題盏浙,個人覺得,那必須啊就看怎么改他了~
深入了解CountDownTimer
讓我們一起進(jìn)入它內(nèi)部去瞅瞅~
//首先就是相關(guān)的介紹荔茬,LZ英文很LOW废膘,就不多說了~
/*
* 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 android.os;
//下面就是為大家簡單介紹如何使用CountDownTimer去實現(xiàn)倒計時效果
/**
* 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;
/**
* boolean representing if the timer was cancelled
*/
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.
*/
public synchronized final void cancel() {
mCancelled = true;
mHandler.removeMessages(MSG);
}
/**
* Start the countdown.
*/
public synchronized final CountDownTimer start() {
mCancelled = false;
if (mMillisInFuture <= 0) {
onFinish();
return this;
}
mStopTimeInFuture = SystemClock.elapsedRealtime() + mMillisInFuture;
mHandler.sendMessage(mHandler.obtainMessage(MSG));
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) {
if (mCancelled) {
return;
}
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;
sendMessageDelayed(obtainMessage(MSG), delay);
}
}
}
};
}
CountDownTimer分析
首先從上面可以看出,一上來他就給我們進(jìn)行一些簡單的介紹慕蔚,之后就是提供使用方法丐黄,接下來就是重點,讓我們瞅瞅他們是什么寫的~
- 方法使用synchronized修飾孔飒,保證一次操作只能有一個進(jìn)行訪問灌闺;
- 在文章開頭,我簡單說過他內(nèi)部同樣是通過Handler去實現(xiàn)倒計時效果十偶,但是我們發(fā)現(xiàn)他使用了一個SystemClock.elapsedRealtime()菩鲜,那么這個東西又是什么呢园细?經(jīng)過百度后得知惦积,他的作用就是返回系統(tǒng)啟動到現(xiàn)在的毫秒數(shù),包含休眠時間猛频。不難理解狮崩,其實個人覺得和我們第一種寫法差不多。
- 那么問題他為什么會出現(xiàn)短暫卡頓呢鹿寻?其實大家在仔細(xì)查閱后會發(fā)現(xiàn)睦柴,當(dāng)它等于1時,接下來再走不就是0了么毡熏,小于等于0的時候同樣也會走一次坦敌,但是這次卻不會更新UI,所以造成一種假象,就會讓我們覺得界面出現(xiàn)了稍微卡頓狱窘。那么說到這杜顺,大家也就知道了怎么使用CountDownTimer去顯示0.下面請看修改后的CountDownTimer~
改造后的CountDownTimer
基于以上分析,我們明白蘸炸,只需要當(dāng)計時毫秒數(shù)小于等于0的時候躬络,他不會進(jìn)行更新UI操作,那么我們只需要讓它在小于等于0的時候搭儒,進(jìn)行更新UI操作即可穷当。
修改CountDownTimer
這部分很簡單,創(chuàng)建一個類淹禾,將CountDownTimer中復(fù)制到我們新的類中馁菜,小小修改下即可實現(xiàn)我們的效果~
package com.example.hlqcountdowntimer.weight;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.widget.TextView;
public abstract class CountDownTimer {
private final TextView test;
/**
* 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;
/**
* boolean representing if the timer was cancelled
*/
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(TextView test, long millisInFuture, long countDownInterval) {
this.test = test;
mMillisInFuture = millisInFuture;
mCountdownInterval = countDownInterval;
}
/**
* Cancel the countdown.
*/
public synchronized final void cancel() {
mCancelled = true;
mHandler.removeMessages(MSG);
}
/**
* Start the countdown.
*/
public synchronized final CountDownTimer start() {
mCancelled = false;
if (mMillisInFuture <= 0) {
onFinish();
return this;
}
mStopTimeInFuture = SystemClock.elapsedRealtime() + mMillisInFuture;
mHandler.sendMessage(mHandler.obtainMessage(MSG));
return this;
}
/**
* Callback fired on regular interval.
*
* @param millisUntilFinished
* The amount of time until finished.
*/
public void onTick(long millisUntilFinished) {
long time = millisUntilFinished / 1000;
test.setText(time + "秒后可重發(fā)");
};
/**
* Callback fired when the time is up.
*/
public void onFinish() {
test.setEnabled(true);
test.setText("完犢子");
};
private static final int MSG = 1;
// handles counting down
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
synchronized (CountDownTimer.this) {
if (mCancelled) {
return;
}
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;
sendMessageDelayed(obtainMessage(MSG), delay);
}
}
}
};
}
調(diào)用的時候需要傳遞當(dāng)前TextView,計時毫秒數(shù)以及調(diào)用間隔毫秒數(shù)即可铃岔,如下:
new com.example.hlqcountdowntimer.weight.CountDownTimer(test, 10000, 1000) {
}.start();
來個圖瞅瞅~
源碼奉上~
下載地址:http://download.csdn.net/detail/u012400885/9752225
感謝大家觀看~