美文网首页自定义控件Android资料汇总
Android倒计时控件的封装(发送验证码控件)

Android倒计时控件的封装(发送验证码控件)

作者: aositeluoke | 来源:发表于2017-08-03 13:05 被阅读0次

    验证码效果图(点击发送验证码后,开始倒计时,退出该界面,再进来,还在倒计时)

    效果.gif

    在开发过程中,几乎每一款应用都会有发送验证码的功能,因此,心中产生了封装的念头,一次封装到处可用,这也是程序员偷懒的一种方式。
    Android开发实现倒计时主要有以下几种方式
    1、使用Handlert通过延时发送信息实现倒计时功能
    2、使用CountDownTimer(源码中使用的也是Handler延时实现)
    3、使用Timer
    在这里,我参考了一下CountDownTimer源码,在此基础上进行修改,加入一些自定义属性,例如倒计时的前缀、后缀、Handler标识,这些属性在布局文件中都可以设置。

    1、自定义控件

    1.1自定义属性(复制到自己的项目中attrs文件下)

    <declare-styleable name="ValidateCodeView" tools:ignore="ResourceName">
           <!--总秒数-->
           <attr name="mTotalSeconds" format="integer" />
           <!--前缀文本-->
           <attr name="mPreStr" format="string" />
           <!--后缀文本-->
           <attr name="mSuffixStr" format="string" />
           <!--handler发送信息表标识码(解决多个界面共用验证码问题)-->
           <attr name="mHandlerSendCode" format="integer" />
    </declare-styleable>
    

    1.2自定义控件(复制到自己的项目下)
    ValidateCodeView.java

    /**
     * 类描述:发送验证码控件
     * 作者:xues
     * 时间:2017年08月01日
     */
    public class ValidateCodeView extends TextView {
    
        private static final String TAG = "ValidateCodeView";
    
        /**
         * 退出界面后,在onDestroy方法中保存当前剩余时间
         */
        private static final HashMap<Integer, Long> mStopTimes = new HashMap<>();
        /**
         * 前缀文本
         */
        private String mPreStr;
        /**
         * 后缀文本
         */
        private String mSuffixStr;
        /**
         * 原始显示字符串
         */
        private String mSrcStr;
    
        /**
         * Millis since epoch when alarm should stop.
         * 总时间
         */
        private long mMillisInFuture = 3000;
    
        /**
         * 间隔时间毫秒 固定一秒
         * The interval in millis that the user receives callbacks
         */
        private final long mCountdownInterval = 1000;
    
        /**
         * 最终的完整时间  例如:当前时间mCurTime为2017年08月04日18点22分20秒  30秒后结束  那么mStopTimeInFuture是mCurTime加上30*1000毫秒
         */
        private long mStopTimeInFuture;
    
        /**
         * boolean representing if the timer was cancelled
         */
        private boolean mCancelled = false;
    
        /**
         * Hander发送信息的what标识,保存结束时间的key
         */
        private int mHandlerSendCode;
    
    
        // handles counting down
        private Handler mHandler = new Handler() {
    
            @Override
            public void handleMessage(Message msg) {
    
                synchronized (ValidateCodeView.this) {
                    if (mCancelled) {
                        return;
                    }
                    //不同界面使用该空间时,发送的标识不同
                    if (msg.what != mHandlerSendCode) {
                        return;
                    }
    
                    final long millisLeft = mStopTimeInFuture - SystemClock.elapsedRealtime();
                    //SystemClock.elapsedRealtime(),开机持续时间,刚开机得到的时间为1970/01/01 00:00:00
                    //它包括了系统深度睡眠的时间。这个时钟是单调的,它保证一直计时,即使CPU处于省电模式,所以它是推荐使用的时间计时器。
                    if (millisLeft <= 0) {
                        onFinish();
                    } else {
                        long lastTickStart = SystemClock.elapsedRealtime();
                        setText(mPreStr + (millisLeft / 1000) + mSuffixStr);//前缀+剩余秒数+后缀
                        // 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(mHandlerSendCode), delay);
                    }
                }
            }
        };
    
        public ValidateCodeView(Context context) {
            super(context);
            initView(context, null, 0);
        }
    
    
        public ValidateCodeView(Context context, AttributeSet attrs) {
            super(context, attrs);
            initView(context, attrs, 0);
        }
    
        public ValidateCodeView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            initView(context, attrs, defStyleAttr);
        }
    
    
        private void initView(Context context, AttributeSet attrs, int defStyleAttr) {
            TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.ValidateCodeView, defStyleAttr, 0);
            long mTotalSeconds = a.getInteger(R.styleable.ValidateCodeView_mTotalSeconds, 30);
            mMillisInFuture = mTotalSeconds * 1000l + 1000;//总毫秒数
            mPreStr = a.getString(R.styleable.ValidateCodeView_mPreStr);//前缀文本
            mSuffixStr = a.getString(R.styleable.ValidateCodeView_mSuffixStr);//后缀文本
            mHandlerSendCode = a.getInteger(R.styleable.ValidateCodeView_mHandlerSendCode, 1);//布局文件中不写统一认为同一个验证码
            a.recycle();
            mSrcStr = getText().toString().trim();//获取原始文字,例如:发送验证码
            setClickable(true);//设置可点击
        }
    
    
        /**
         * Cancel the countdown.
         */
        public synchronized final void cancel() {
            mCancelled = true;
            mHandler.removeMessages(mHandlerSendCode);
        }
    
        /**
         * Start the countdown.
         */
        public synchronized final void start() {
            setEnabled(false);//设置控件不可用(不可点击)
            mCancelled = false;
            //间隔时间
            if (mMillisInFuture <= 0) {
                onFinish();
                return;
            }
            //结束时间
            mStopTimeInFuture = SystemClock.elapsedRealtime() + mMillisInFuture;
            //发送信息
            mHandler.sendMessage(mHandler.obtainMessage(mHandlerSendCode));
            return;
        }
    
    
        /**
         * Callback fired when the time is up.
         */
        public void onFinish() {
            setEnabled(true);
            setText(mSrcStr);
        }
    
    
        /**
         * 加入生命周期的方法,在Activity的onCreate方法中调用即可
         */
        public void onCreate() {
            //如果保存了当前页面的剩余时间则自动开始
            if (mStopTimes.containsKey(mHandlerSendCode)) {
                mStopTimeInFuture = mStopTimes.get(mHandlerSendCode);
                mCancelled = false;
                final long millisLeft = mStopTimeInFuture - SystemClock.elapsedRealtime();//剩余毫秒数
                //剩余毫秒数小于或等于零,结束,否则设置按钮不可用
                if (millisLeft <= 0) {
                    onFinish();
                    return;
                }
                setEnabled(false);
                mHandler.sendMessage(mHandler.obtainMessage(mHandlerSendCode));
            }
    
        }
    
    
        /**
         * 加入生命周期的方法,在Activity的onCreate方法中调用即可
         */
        public void onDestroy() {
            //保存剩余时间
            mStopTimes.put(mHandlerSendCode, mStopTimeInFuture);
        }
    }
    
    2、使用篇

    2.1在布局中引用控件

    <com.ewhalelibrary.widget.ValidateCodeView
            android:id="@+id/validateCodeView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:background="@color/red"
            android:gravity="center"
            android:padding="@dimen/px20dp"
            android:text="发送验证码"
            android:textColor="@color/select_code_text_color"
            app:mHandlerSendCode="2"
            app:mPreStr="剩余"
            app:mSuffixStr="秒后重新发送"
            app:mTotalSeconds="30" />
        <!--
            app:mHandlerSendCode="2"  默认为1,注册和修改密码的验证码,后台要分开的话,该值要设置成不同的数值
            app:mPreStr="剩余"  前缀
            app:mSuffixStr="秒后重新发送" 后缀
            app:mTotalSeconds="30" 总时长30秒
        -->
    

    2.2在Activity或Fragment中绑定生命周期

     @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            validateCodeView.onCreate();
        }
    
    
        @Override
        protected void onDestroy() {
            validateCodeView.onDestroy();
            super.onDestroy();
        }
    

    2.3调用发送验证码的接口成功后调用start方法即可

    validateCodeView.start();
    

    相关文章

      网友评论

        本文标题:Android倒计时控件的封装(发送验证码控件)

        本文链接:https://www.haomeiwen.com/subject/etqslxtx.html