Android自定义Chronometer实现短信验证码秒表倒计时功能
作者:FreedoMan 发布时间:2022-02-22 10:52:47
标签:Android,Chronometer,倒计时
本文实例为大家分享了Chronometer实现倒计时功能,Android提供了实现按照秒计时的API,供大家参考,具体内容如下
一、自定义ChronometerView 继续自TextView
主要原理:先设置一个基准倒计时时间mBaseSeconds,内置handler 每隔1s发送一个空消息,mRemainSeconds--,同时刷新界面视图,回调给外部调用者,只到为零。外部调用者可通过start()/pause()/stop()来控制计时器的工作状态。
可以app中发送短信验证码的场景为例,做了一个很粗糙的界面,但功能都实现了。
/**
* @name 倒计时器(类似妙表倒数计时,支持暂停、停止、重新开始)
* @author Fanjb
* @date 2015年11月6日
*/
public class ChronometerView extends TextView {
/**
* A callback that notifies when the chronometer has decremented on its own.
*
* @author Fanjb
*/
public interface OnTickChangeListener {
/**
* remain seconds changed
*
* @param view
* @param remainSeconds
*/
public void onTickChanged(ChronometerView view, long remainSeconds);
}
private long mBase;
private long mRemainSeconds;
private boolean mStarted;
private boolean mReStart;
private boolean mVisible;
private boolean mIsEnable;
private OnTickChangeListener mTickListener;
public ChronometerView(Context context) {
this(context, null);
}
public ChronometerView(Context context, AttributeSet attrs) {
super(context, attrs, 0);
}
public ChronometerView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
updateText(mRemainSeconds);
}
@Override
protected void onWindowVisibilityChanged(int visibility) {
super.onWindowVisibilityChanged(visibility);
mVisible = visibility == VISIBLE;
updateStatus();
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
mVisible = false;
updateStatus();
}
/**
* 启动计时器
*/
public void start() {
if (mReStart && !mStarted) {
mRemainSeconds = mBase;
}
mStarted = true;
updateStatus();
}
/**
* 暂停计时器
*/
public void pause() {
if (mStarted) {
mStarted = mReStart = false;
updateStatus();
}
}
/**
* 停止计时器,再次调用 start()重新启动
*/
public void stop() {
mStarted = false;
mReStart = true;
updateStatus();
updateText(mRemainSeconds = 0);
dispatchTickListener();
}
/**
* 刷新内部状态
*/
private void updateStatus() {
boolean isEnable = mVisible && mStarted;
if (mIsEnable != isEnable) {
if (isEnable) {
mHandler.sendMessage(Message.obtain(mHandler, TICK_WHAT));
} else {
mHandler.removeMessages(TICK_WHAT);
}
mIsEnable = isEnable;
}
}
private static final int TICK_WHAT = 1;
private Handler mHandler = new Handler() {
public void handleMessage(android.os.Message msg) {
if (mRemainSeconds > 0) {
updateText(--mRemainSeconds);
dispatchTickListener();
sendMessageDelayed(Message.obtain(this, TICK_WHAT), 1000);
}
}
};
private void updateText(long now) {
String text = DateUtils.formatElapsedTime(now);
setText(text);
}
/**
* 在未启动状态下设置开始倒计时时间
*
* @param baseSeconds
*/
public void setBaseSeconds(long baseSeconds) {
if (baseSeconds > 0 && baseSeconds != mBase && !mStarted) {
mBase = mRemainSeconds = baseSeconds;
updateText(mRemainSeconds);
}
}
/**
* 剩余时间
*
* @return
*/
public long getRemainSeconds() {
return mRemainSeconds;
}
public void setOnTickChangeListener(OnTickChangeListener listener) {
mTickListener = listener;
}
public OnTickChangeListener getTickListener() {
return mTickListener;
}
private void dispatchTickListener() {
if (mTickListener != null) {
mTickListener.onTickChanged(this, getRemainSeconds());
}
}
@Override
public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
super.onInitializeAccessibilityEvent(event);
event.setClassName(ChronometerView.class.getName());
}
@Override
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(info);
info.setClassName(Chronometer.class.getName());
}
}
二、xml 中没有加入自定义的控件属性,同TextView
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<com.freedoman.widgets.calendar.ChronometerView
android:id="@+id/chronometer_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:background="@drawable/chronometer_view_bg"
android:enabled="true"
android:text="00:00" />
<Button
android:id="@+id/start_chronometer_view_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:text="Start" />
<Button
android:id="@+id/pause_chronometer_view_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:text="Pause" />
<Button
android:id="@+id/stop_chronometer_view_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:text="Stop" />
</LinearLayout>
三、在Activity中做一个简单的测试(可以发送短信验证码的实际应用场景为例)
public class ChronometerActivity extends Activity {
private ChronometerView mChronometerView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_clock);
// 自定义计时器
if (mChronometerView == null) {
mChronometerView = (ChronometerView) findViewById(R.id.chronometer_view);
mChronometerView.setBaseSeconds(60);
mChronometerView.setOnTickChangeListener(new OnTickChangeListener() {
@Override
public void onTickChanged(ChronometerView view, long curTimeMills) {
System.out.println(curTimeMills);
view.setEnabled(curTimeMills == 0 || curTimeMills == 60);
if (curTimeMills == 0) {
mChronometerView.setText("重新发送");
}
}
});
mChronometerView.setText("点击发送验证码");
}
findViewById(R.id.start_chronometer_view_btn).setOnClickListener(mClickListener);
findViewById(R.id.pause_chronometer_view_btn).setOnClickListener(mClickListener);
findViewById(R.id.stop_chronometer_view_btn).setOnClickListener(mClickListener);
}
private View.OnClickListener mClickListener = new OnClickListener() {
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.start_chronometer_view_btn:
if (mChronometerView != null) {
mChronometerView.start();
}
break;
case R.id.pause_chronometer_view_btn:
if (mChronometerView != null) {
mChronometerView.pause();
}
break;
case R.id.stop_chronometer_view_btn:
if (mChronometerView != null) {
mChronometerView.stop();
}
break;
}
}
};
}


猜你喜欢
- 前言目前Flutter三大主流状态管理框架分别是provider、flutter_bloc、getx,三大状态管理框架各有优劣,本篇文章将介
- SpringBatch介绍:SpringBatch 是一个大数据量的并行处理框架。通常用于数据的离线迁移,和数据处理,⽀持事务、并发、流程、
- 本文实例为大家分享了Android弹出菜单效果的具体代码,供大家参考,具体内容如下功能描述:用户单击按钮弹出菜单。当用户选择一个菜单项,会触
- 一、问题描述在C#中is,as,using关键字具有其特点及使用场景,其中is关键字用于检查该对象是否与给定类型兼容,as关键字用于将对象转
- 在redirect重定向的时候携带参数问题SpringMVC 中常用到 redirect来实现重定向。但使用场景各有需求,如果只是简单的页面
- 目录No1. 自定义控件模板No2. 重写控件No3. 附加属性来试试总结文章默认你已经入门WPF了WPF日常开发,经常遇到默认的控件功能不
- 1.面对对象的初步认识1.1什么是面向对象用面向对象的思想来涉及程序,更符合人们对事物的认知,对于大型程序的设计、扩展以及维护都非常友好。1
- 方法一:需要调用win32api,winform、wpf通用[DllImport("user32.dll")]publi
- 为Repository添加自定义方法一、为某个Repository添加自定义方法1、定义一个接口PersonDao,声明要添加的方法。pub
- @NonNull导致无法序列化的问题以上这个代码在接参的时候报了一个缺少无参构造函数无法序列化的错误将.class反编译可以看到编译后的源码
- 1. 使用蓝牙的响应权限<uses-permission android:name="android.permission.
- 本文实例为大家分享了Android Studio实现带边框的圆形头像的具体代码,供大家参考,具体内容如下效果显示:(没有边框的)(有边框的)
- (注意:本文基于JDK1.8)前言任何一个容器类对象用于持有元素后,总是需要遍历元素的,即挨个去访问每个元素1次,而遍历元素,除了常规的依赖
- Android 自定义组件成JAR包的实现方法,这里对自己实现的Android View 组件进行JAR 包的处理。
- 我们玩玩手机的录像功能吧。做个DEMO。 看看录制过程: mediarecorder = new MediaRecorder();// 创建
- 程序目的从java字节码层理解,为何i = i++后,结果是+1之前的数值。而i=++i后,结果是+1之后的值。关键指令iload_<
- 1. 源起:仍然是模块化编程所引发的需求。产品经理难伺候,女产品经理更甚之~:p纯属戏谑,技术方案与产品经理无关,芋头莫怪!VCU10项目重
- ionic App 解决android端在真机上 tab处于顶部的Bug在app.js 页面中添加以下代码.config(function(
- 本文实例讲述了使用SAX来解析XML。通常来说在Android里面可以使用SAX和DOM,DOM需要把整个XML文件读入内存再解析,比较消耗
- 本文实例讲述了C#实现的Win32控制台线程计时器功能。分享给大家供大家参考,具体如下:在C#中提供了三种类型的计时器:1、基于 Windo