Android之实现倒计时

今天为大家介绍俩种方式,都可以实现倒计时功能。这俩种方式分别是:

  1. Handler+Thread

  2. 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去实现倒计时,需要如下几个步骤:

  1. 实例化CountDownTimer对象;

  2. 提供计时时间毫秒以及时间间隔毫秒;

  3. 重写onTick()和onFinish()方法;

      那么这俩个方法分别都是什么作用呢?
      3.1 onTick(long millisUntilFinished)
            参数millisUntilFinished是倒计时的剩余时间。在倒计时结束后会调用onFinish。
       3.2 onFinish()
             倒计时结束后需要执行的操作可以写在这里。
  4. 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获取验证码");
        }
    };

}
CountDownTimer分析

首先从上面可以看出,一上来他就给我们进行一些简单的介绍,之后就是提供使用方法,接下来就是重点,让我们瞅瞅他们是什么写的~

  1. 方法使用synchronized修饰,保证一次操作只能有一个进行访问;

  2. 在文章开头,我简单说过他内部同样是通过Handler去实现倒计时效果,但是我们发现他使用了一个SystemClock.elapsedRealtime(),那么这个东西又是什么呢?经过百度后得知,他的作用就是返回系统启动到现在的毫秒数,包含休眠时间。不难理解,其实个人觉得和我们第一种写法差不多。

  3. 那么问题他为什么会出现短暂卡顿呢?其实大家在仔细查阅后会发现,当它等于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);
                }
            }
        }
    };
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值