前言
LZ-Says:今天收到转正通知了,内心也就12s开心,随之而来的就是不平不淡。16年10月18号入职,感觉一切都好像眨眼之间,过得好快,各种忙
答应了自己,要好好努力。答应了家人,要好好奋斗。答应了心,要好好坚持。有什么理由不去努力
开始正题
好吧,今天一起回顾下,关于Android中实现倒计时功能吧~
今天为大家介绍俩种方式,都可以实现倒计时功能。这俩种方式分别是:
- Handler+Thread
- CountDownTimer
想必大家对于第一种实现方式肯定不会陌生了,简直So easy那再次回顾下第一种写法
1.通过使用Handler+Thread实现倒计时
首先编写布局文件
布局文件很简单,就是一个TextView,默认显示Handler获取验证码,点击TextView,进行倒计时操作,完成后恢复默认显示。
<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,实现效果~
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) {
// 倒计时结束让按钮可用
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实现倒计时
CountDownTimer简介
CountDownTimer是Android内部封装好的一个关于实现倒计时功能的类。所在包:package android.os;其内部实现也是通过咱第一种实现方式,没啥好说的,看看人家官方简介吧
官方使用方式
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去实现倒计时,需要如下几个步骤:
- 实例化CountDownTimer对象;
- 提供计时时间毫秒以及时间间隔毫秒;
- 重写onTick()和onFinish()方法;
那么这俩个方法分别都是什么作用呢?
3.1 onTick(long millisUntilFinished)
参数millisUntilFinished是倒计时的剩余时间。在倒计时结束后会调用onFinish。
3.2 onFinish()
倒计时结束后需要执行的操作可以写在这里。- 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) {
// 倒计时结束让按钮可用
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 + "秒后可重发");
onFinish();
}
tvShowC.setText(time + "秒后可重发");
}
@Override
public void onFinish() {
tvShowC.setEnabled(true);
tvShowC.setText("CountDownTimer获取验证码");
}
};
}
来个效果图~
这里写图片描述不知道大家有没有发现一个小问题,怎么到1秒时,会出现短暂延迟?而且使用CountDownTimer可以显示0秒么?
关于以上问题,让我们一起去看看人家是什么写的,从他们写的代码中看看能不能发现相关蛛丝马迹关于可以显示0秒么这个问题,个人觉得,那必须啊就看怎么改他了~
深入了解CountDownTimer
让我们一起进入它内部去瞅瞅~
//首先就是相关的介绍,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去实现倒计时效果
/**
* 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分析
首先从上面可以看出,一上来他就给我们进行一些简单的介绍,之后就是提供使用方法,接下来就是重点,让我们瞅瞅他们是什么写的~
- 方法使用synchronized修饰,保证一次操作只能有一个进行访问;
- 在文章开头,我简单说过他内部同样是通过Handler去实现倒计时效果,但是我们发现他使用了一个SystemClock.elapsedRealtime(),那么这个东西又是什么呢?经过百度后得知,他的作用就是返回系统启动到现在的毫秒数,包含休眠时间。不难理解,其实个人觉得和我们第一种写法差不多。
- 那么问题他为什么会出现短暂卡顿呢?其实大家在仔细查阅后会发现,当它等于1时,接下来再走不就是0了么,小于等于0的时候同样也会走一次,但是这次却不会更新UI,所以造成一种假象,就会让我们觉得界面出现了稍微卡顿。那么说到这,大家也就知道了怎么使用CountDownTimer去显示0.下面请看修改后的CountDownTimer~
改造后的CountDownTimer
基于以上分析,我们明白,只需要当计时毫秒数小于等于0的时候,他不会进行更新UI操作,那么我们只需要让它在小于等于0的时候,进行更新UI操作即可。
修改CountDownTimer
这部分很简单,创建一个类,将CountDownTimer中复制到我们新的类中,小小修改下即可实现我们的效果~
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 + "秒后可重发");
};
/**
* 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);
}
}
}
};
}
调用的时候需要传递当前TextView,计时毫秒数以及调用间隔毫秒数即可,如下:
new com.example.hlqcountdowntimer.weight.CountDownTimer(test, 10000, 1000) {
}.start();
来个图瞅瞅~
这里写图片描述源码奉上~
下载地址:http://download.csdn.net/detail/u012400885/9752225
感谢大家观看~
网友评论