app 一般都会有倒计时功能,无论是引导页面,还是短信验证码填写。本文便讲解下简单的实现。
效果图
分析
这个小小的倒计时功能也有一些技巧,特别是对于初学者来说可能就是坑,比如你在ui线程进行倒计时10秒。不好意思 app crash,主线程不能进行耗时操作,这时我们又想到了Handler,Timer一些类。
实现1
自己封装 < . . >
既然能写这个界面,那么相信你一定是入门界别的了,不说读过handler 源码,肯定会使用Handler,自己看看网上思路也可以尝试封装下一个自己的计时器倒计时。当时我也是傻傻的自己封装了一个哈,还是对api掌握的不够了解,原来google工程师为我们提供的就有。
实现2 CountDownTimer
系统源码实现(可以忽略不看,参考下面的具体使用即可):
/** 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;/*** 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 downprivate Handler mHandler = new Handler() {
@Overridepublic void handleMessage(Message msg) {
synchronized (CountDownTimer.this) {
if (mCancelled) {
return;}final long millisLeft = mStopTimeInFuture - SystemClock.elapsedRealtime();if (millisLeft <= 0) {
onFinish();} else {
long lastTickStart = SystemClock.elapsedRealtime();onTick(millisLeft);// take into account user's onTick taking time to executelong lastTickDuration = SystemClock.elapsedRealtime() - lastTickStart;long delay;if (millisLeft < mCountdownInterval) {
// just delay until donedelay = millisLeft - lastTickDuration;// special case: user's onTick took more than interval to// complete, trigger onFinish without delayif (delay < 0) delay = 0;} else {
delay = mCountdownInterval - lastTickDuration;// special case: user's onTick took more than interval to// complete, skip to next intervalwhile (delay < 0) delay += mCountdownInterval;}sendMessageDelayed(obtainMessage(MSG), delay);}}}};
}
可以看出 源码内部使用Handler 已经帮我们实现好了,我们只需要使用几个方法即可。
使用解释
1 、首先CountDownTimer是一个抽象类我们可以像使用点击事件那样直接以匿名内部类方式使用。或者我们写个类继承此抽象类也行。
2 、 构造
public CountDownTimer(long millisInFuture, long countDownInterval) {
mMillisInFuture = millisInFuture;mCountdownInterval = countDownInterval;}
参数1 倒计时总时间
参数2 倒计时时间间隔
3、 抽象方法
onTick 在这处理倒计时期间相关逻辑
onFinish 在这处理 倒计时结束工作
4 、不要忘记的重要一点 通过start()方法开启任务。
5、代码规范
在 acyivity fragment dialog相关中使用一定要留意空指针或者内存泄漏避免(参看代码实现)。具体引起原因参考handler引起内存泄漏的原因。
6、简单使用参考
package com.example.administrator.modelmall.ui.activities;import android.annotation.SuppressLint;
import android.content.Intent;
import android.os.CountDownTimer;
import android.support.v7.widget.AppCompatTextView;
import android.view.View;import com.example.administrator.modelmall.R;import butterknife.BindView;/*** Create by SunnyDay on 2019/03/15*/
public class CountDownActivity extends BaseActivity {
@BindView(R.id.tv_count_down)AppCompatTextView countDownText;private CountDownTimer timer;@Overridepublic Object offerLayout() {
return R.layout.activity_countdown;}@Overridepublic void onBindView() {
setNoTitleBarAndFullScreen();initCountDown();}/*** 倒计时逻辑处理*/private void initCountDown() {
// 避免内存泄漏if (!isFinishing()) {
timer = new CountDownTimer(1000 * 10, 1000) {
@SuppressLint("SetTextI18n")@Overridepublic void onTick(long millisUntilFinished) {
countDownText.setEnabled(true);countDownText.setText((int) millisUntilFinished / 1000 + " 跳过");countDownText.setOnClickListener(new View.OnClickListener() {
@Overridepublic void onClick(View v) {
startActivity(new Intent(CountDownActivity.this, GuideActivity.class));finish();}});}@Overridepublic void onFinish() {
startActivity(new Intent(CountDownActivity.this, GuideActivity.class));finish();}}.start();}}@Overridepublic void destory() {
// 避免内存泄漏if (timer != null) {
timer.cancel();timer = null;}}
}
8、参考
-
项目实战参考
-
参考文章:https://blog.csdn.net/zp0119/article/details/78731488
-
handler避免警告参考
小结
简单的分享下使用吧 溜了。。。