Android Wear计时器开发

记得在2013年12月的时候,有系列文章是介绍怎么开发一个智能手表的App,让用户可以在足球比赛中记录停表时间。随着Android Wear的问世,在可穿戴设备中开发一款这样的App确实是个很不错的想法,但是按照目前对于Android Wear的架构了解来说,似乎有些困难。所以本系列文章我们就重写这个应用,带领大家进入Android Wear的世界。

本文不会长篇大论地讲解我们要开发的这款App的用途,因为我们在之前的系列文章已经深入了解过了。这么说吧,这是一个计时类应用,在比赛开始的时候开始执行,在比赛的过程中可以暂停(停表),然后45分钟过去后会有震动提醒,然后比赛进行45分钟后也会有提醒。

在开始之前,很有必要先看看我们为什么要重写这个App而不是直接上代码。智能手表使用的是一个修改版的Android1.6的系统,所以它的架构很像一个运行Android1.6的手机,所以我们的App基于一个Activity,我们所有的工作都运行在这个Activity上。在开始学习智能手表开发之前,我们必须很清楚地知道,我们之前的设计在Android Wear上并不适用,尽管它也是支持Activity,但是在Android Wear上工作方式是不同的。在手机或者平板上,如果一个Activity从sleep状态回到唤醒状态,Activity会被重新唤醒,但是在Wear上却不是这样。一段时间过去后Wear设备会进入sleep,但是在设备唤醒后,处于sleep状态的Activity却不会再被唤醒了。

首先这个问题使我非常惊讶,我一直很想知道Activity有了这个限制后,还能开发实用的App吗?后来才发现这个问题完全是多虑的,我渐渐地发现,要开发一个实用的App也很简单——我们只需要转变我们的软件设计模式,使它更符合Android Wear的体系结构,而不是当做一个手机来看。

这里我们需要考虑的最基本的问题是,这个计时应用程序需要基于一个一直运行的服务来记录时间。但是基于长运行的服务不是一个好的方案,因为它会耗电。这里我们提到的记录时间这个关键词,也就是说,我们并不需要真的实现一个长运行的服务,只要在用户需要看的时候我们可以更新消息显示就行。在大部分的时间里,其实用户只需要了解大概过去了多长时间,只有在比赛暂停或者中场快结束的时候才需要显示更详细的信息。所以在大部分的时间里,我们只需要显示精确到分钟即可,然后在用户需要的时候才精确到秒。

我们要实现这个方法的基本方法就是使用AlarmManager每分钟触发一次更新通知事件,去更新分钟显示。这个通知事件还包括显示精确到秒的Activity,但是只有在用户滑动屏幕的时候才会显示整个通知。通过这种方式我们可以在必须显示的时候才去更新消息,所以对大部分设备来说,每分钟更新一次消息显示比一直运行一个服务更加省电。

下图显示充分证明了这点,首先我们需要打开通知,这样就可以得到精确到秒的显示了。

然而,在有信息显示或者设备休眠的时候,我们只需要显示精确到分钟就可以了。

有一件事情需要说明一下,就是这个App的名字已经改变了。之前在在I'm Watch的版本上叫做“Footy Timer”,现在改为“Match Timer”。因为在使用语音启动App的时候,Google的声音识别对“Footy”这个词很不敏感,我们用“ok Google,start Footy Timer”这个命令不能启动应用,而使用“ok Google,start Match Timer”就可以使用。

最后,很抱歉这篇文章没有代码,但是本系列文章会稍微有些变动。以前本人会在每篇文章末尾附上文章相关的代码段,这个请放心,之后的文章还是会这样的,因为这个是一个功能完善的App,而不是系列技术文章,所以在接下来的文章会包含一些代码示例和注释,在本系列文章完结的时候会附上整个项目的源码。

Match Timer 可以在Google Play上找到:https://play.google.com/store/apps/details?id=com.stylingandroid.matchtimer

上面我们解释了为什么要在Android Wear重写这个计时器app(因为之前已经在“I'm Watch”里面开发过了),下面我们就来看看代码。

我们以这个app的一个核心类开始,这个类负责控制计时器的状态。这个类包含了4个long类型的变量:第一个代表计时器开始的时间;第二个代表计时器停止的时间(在运行中的话,它就是0);第三个代表计时器停表的时间(如果当前没有停表,那它也是0),第四个代表总共停表的时长。通过这四个变量我们就可以维持计时器的状态了,还可以通过计算得到我们需要展示的其他信息。这个类的基本功能就是都是为了操作这些变量,即维持计时器的这些状态。


  public final class MatchTimer {
  .
  .
  .
  public static final int MINUTE_MILLIS = 60000;

  private long start;
  private long currentStoppage;
  private long totalStoppages;
  private long end;
  .
  .
  .
  public long getElapsed() {
    if (isRunning()) {
      return System.currentTimeMillis() - start;
    }
    if (end > 0) {
      return end - start;
    }
    return 0;
  }

  public boolean isRunning() {
    return start > 0 && end == 0;
  }

  public boolean isPaused() {
    return currentStoppage > 0;
  }

  public int getElapsedMinutes() {
    return (int) ((System.currentTimeMillis() - start) / MINUTE_MILLIS);
  }

  public long getTotalStoppages() {
    long now = System.currentTimeMillis();
    if (isPaused()) {
      return totalStoppages + (now - currentStoppage);
    }
    return totalStoppages;
  }

  public long getPlayed() {
    return getElapsed() - getTotalStoppages();
  }

  public long getStartTime() {
    return start;
  }
  .
  .
  .
  }

这些都是基本的java代码,就不费时间讲了。下面的函数更高级一些,可以操作计时器的状态。


  public final class MatchTimer {
  .
  .
  .
  public void start() {
    if (end > 0) {
      start = System.currentTimeMillis() - (end - start);
      end = 0;
    } else {
      start = System.currentTimeMillis();
    }
    save();
  }

  public void stop() {
    if (isPaused()) {
      resume();
    }
    end = System.currentTimeMillis();
    save();
  }

  public void pause() {
    currentStoppage = System.currentTimeMillis();
    save();
  }

  public void resume() {
    totalStoppages += System.currentTimeMillis() - currentStoppage;
    currentStoppage = 0L;
    save();
  }

  public void reset() {
    resetWithoutSave();
    save();
  }

  private void resetWithoutSave() {
    start = 0L;
    currentStoppage = 0L;
    totalStoppages = 0L;
    end = 0L;
  }
  }

这些还是基本的Java代码,也可以不用讲了。只有save()方法我们还没有见到,这是在类的最后写的,这个函数才值得的我们讲讲。

前一篇文章我们讨论了关于唤醒机制的问题,我们不需要去维持一个长连接或者后台服务,只需要维持这几个计时器的状态就可以了。我们使用SharedPreference来实现:


  public final class MatchTimer implements SharedPreferences.OnSharedPreferenceChangeListener {
  private static final String KEY_START = "com.stylingandroid.matchtimer.KEY_START";
  private static final String KEY_CURRENT_STOPPAGE = "com.stylingandroid.matchtimer.KEY_CURRENT_STOPPAGE";
  private static final String KEY_TOTAL_STOPPAGES = "com.stylingandroid.matchtimer.KEY_TOTAL_STOPPAGES";
  private static final String KEY_END = "com.stylingandroid.matchtimer.KEY_END";
  private static final String PREFERENCES = "MatchTimer";

  private final SharedPreferences preferences;

  public static MatchTimer newInstance(Context context) {
    SharedPreferences preferences = context.getSharedPreferences(PREFERENCES, Context.MODE_PRIVATE);
    long start = preferences.getLong(KEY_START, 0);
    long currentStoppage = preferences.getLong(KEY_CURRENT_STOPPAGE, 0);
    long totalStoppages = preferences.getLong(KEY_TOTAL_STOPPAGES, 0);
    long end = preferences.getLong(KEY_END, 0);
    return new MatchTimer(preferences, start, currentStoppage, totalStoppages, end);
  }

  private MatchTimer(SharedPreferences preferences, long start, long currentStoppage, long totalStoppages, long end) {
    this.preferences = preferences;
    this.start = start;
    this.currentStoppage = currentStoppage;
    this.totalStoppages = totalStoppages;
    this.end = end;
  }

  public void save() {
    preferences.edit()
        .putLong(KEY_START, start)
        .putLong(KEY_CURRENT_STOPPAGE, currentStoppage)
        .putLong(KEY_TOTAL_STOPPAGES, totalStoppages)
        .putLong(KEY_END, end)
        .apply();
  }

  public void registerForUpdates() {
    preferences.registerOnSharedPreferenceChangeListener(this);
  }

  public void unregisterForUpdates() {
    preferences.unregisterOnSharedPreferenceChangeListener(this);
  }

  @Override
  public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
    long value = sharedPreferences.getLong(key, 0L);
    if (key.equals(KEY_START)) {
      start = value;
    } else if (key.equals(KEY_END)) {
      end = value;
    } else if (key.equals(KEY_CURRENT_STOPPAGE)) {
      currentStoppage = value;
    } else if (key.equals(KEY_TOTAL_STOPPAGES)) {
      totalStoppages = value;
    }
  }
  .
  .
  .
}

我们需要的就是newInstance()方法从SharedPreference中构造一个MatchTimer实例,我们还需要save()方法,可以帮我们把当前的计时器状态保存到SharedPreference中。

最后我们要说明的是,如果某一部分持有MatchTimer对象的引用,但是其他对象已经改变了计时器的状态,就可能会发生异常(见下一篇文章)。所以我们还需要提供一些方法去注册和注销MatchTImer的实例,在Sharedpreference的值改变时去接收计时器状态的变化。

现在我们已经定义了一个基本的计时器了,下一篇文章我们会介绍怎么保持计时器的状态以及在需要的时候去唤醒这些状态。

Match Timer 可以在Google Play上下载:Match Timer.

在本系列前几篇文章中,我们介绍了Android Wear计时器app,对设计思路和app的结构进行了分析。本文将讲解如何定时唤醒程序提醒用户。

对于为什么不用后台服务的方式一直运行,我们已经进行了解释——这种方式非常耗电。因此,我们必须要有一个定时唤醒机制。我们可以使用AlarmManager来实现这个机制,定时执行一个Intent,然后通知BroadcastReceiver。之所以选择BroadcastReceiver而不用IntentService,是因为我们要运行的任务是轻量级的而且生命周期非常短暂。使用BroadcastReceiver可以避免每次执行任务的时候都经历Service的整个生命周期。因此,对于我们这种轻量级的任务来说非常合适——我们执行的任务都在毫秒级。

BroadcastReceiver的核心在于onReceiver方法,我们需要在这里安排各种事件响应。


  public class MatchTimerReceiver extends BroadcastReceiver {
  public static final int MINUTE_MILLIS = 60000;
  private static final long DURATION = 45 * MINUTE_MILLIS;

  private static final Intent UPDATE_INTENT = new Intent(ACTION_UPDATE);
  private static final Intent ELAPSED_ALARM = new Intent(ACTION_ELAPSED_ALARM);
  private static final Intent FULL_TIME_ALARM = new Intent(ACTION_FULL_TIME_ALARM);

  private static final int REQUEST_UPDATE = 1;
  private static final int REQUEST_ELAPSED = 2;
  private static final int REQUEST_FULL_TIME = 3;

  public static void setUpdate(Context context) {
    context.sendBroadcast(UPDATE_INTENT);
  }
  .
  .
  .
  private void reset(MatchTimer timer) {
    timer.reset();
  }

  private void resume(Context context, MatchTimer timer) {
    timer.resume();
    long playedEnd = timer.getStartTime() + timer.getTotalStoppages() + DURATION;
    if (playedEnd > System.currentTimeMillis()) {
      setAlarm(context, REQUEST_FULL_TIME, FULL_TIME_ALARM, playedEnd);
    }
  }

  private void pause(Context context, MatchTimer timer) {
    timer.pause();
    cancelAlarm(context, REQUEST_FULL_TIME, FULL_TIME_ALARM);
    long elapsedEnd = timer.getStartTime() + DURATION;
    if (!isAlarmSet(context, REQUEST_ELAPSED, ELAPSED_ALARM) && elapsedEnd > System.currentTimeMillis()) {
      setAlarm(context, REQUEST_ELAPSED, ELAPSED_ALARM, elapsedEnd);
    }
  }

  private void stop(Context context, MatchTimer timer) {
    timer.stop();
    cancelAlarm(context, REQUEST_UPDATE, UPDATE_INTENT);
    cancelAlarm(context, REQUEST_ELAPSED, ELAPSED_ALARM);
    cancelAlarm(context, REQUEST_FULL_TIME, FULL_TIME_ALARM);
  }

  private void start(Context context, MatchTimer timer) {
    timer.start();
    long elapsedEnd = timer.getStartTime() + DURATION;
    setRepeatingAlarm(context, REQUEST_UPDATE, UPDATE_INTENT);
    if (timer.getTotalStoppages() > 0 && !timer.isPaused()) {
      long playedEnd = timer.getStartTime() + timer.getTotalStoppages() + DURATION;
      if (playedEnd > System.currentTimeMillis()) {
        setAlarm(context, REQUEST_FULL_TIME, FULL_TIME_ALARM, playedEnd);
      }
      if (elapsedEnd > System.currentTimeMillis()) {
        setAlarm(context, REQUEST_ELAPSED, ELAPSED_ALARM, elapsedEnd);
      }
    } else {
      if (elapsedEnd > System.currentTimeMillis()) {
        setAlarm(context, REQUEST_FULL_TIME, FULL_TIME_ALARM, elapsedEnd);
      }
    }
  }
  .
  .
  .
  }

代码还是非常直观易于理解的。首先实例化一个MatchTimer对象(从SharedPreference中读取数据),然后分别传给对应的事件处理Handler。之后等待动作发生,最后更新Notification。

这里会处理8个事件动作,其中5个负责控制计时器的状态(START、STOP、PAUSE、RESUME、RESET);一个负责更新Notification,剩下两个负责到45分钟唤醒后震动提示。

我们先从这几个控制状态开始:


  public class MatchTimerReceiver extends BroadcastReceiver {
  public static final int MINUTE_MILLIS = 60000;
  private static final long DURATION = 45 * MINUTE_MILLIS;

  private static final Intent UPDATE_INTENT = new Intent(ACTION_UPDATE);
  private static final Intent ELAPSED_ALARM = new Intent(ACTION_ELAPSED_ALARM);
  private static final Intent FULL_TIME_ALARM = new Intent(ACTION_FULL_TIME_ALARM);

  private static final int REQUEST_UPDATE = 1;
  private static final int REQUEST_ELAPSED = 2;
  private static final int REQUEST_FULL_TIME = 3;

  public static void setUpdate(Context context) {
    context.sendBroadcast(UPDATE_INTENT);
  }
  .
  .
  .
  private void reset(MatchTimer timer) {
    timer.reset();
  }

  private void resume(Context context, MatchTimer timer) {
    timer.resume();
    long playedEnd = timer.getStartTime() + timer.getTotalStoppages() + DURATION;
    if (playedEnd > System.currentTimeMillis()) {
      setAlarm(context, REQUEST_FULL_TIME, FULL_TIME_ALARM, playedEnd);
    }
  }

  private void pause(Context context, MatchTimer timer) {
    timer.pause();
    cancelAlarm(context, REQUEST_FULL_TIME, FULL_TIME_ALARM);
    long elapsedEnd = timer.getStartTime() + DURATION;
    if (!isAlarmSet(context, REQUEST_ELAPSED, ELAPSED_ALARM) && elapsedEnd > System.currentTimeMillis()) {
      setAlarm(context, REQUEST_ELAPSED, ELAPSED_ALARM, elapsedEnd);
    }
  }

  private void stop(Context context, MatchTimer timer) {
    timer.stop();
    cancelAlarm(context, REQUEST_UPDATE, UPDATE_INTENT);
    cancelAlarm(context, REQUEST_ELAPSED, ELAPSED_ALARM);
    cancelAlarm(context, REQUEST_FULL_TIME, FULL_TIME_ALARM);
  }

  private void start(Context context, MatchTimer timer) {
    timer.start();
    long elapsedEnd = timer.getStartTime() + DURATION;
    setRepeatingAlarm(context, REQUEST_UPDATE, UPDATE_INTENT);
    if (timer.getTotalStoppages() > 0 && !timer.isPaused()) {
      long playedEnd = timer.getStartTime() + timer.getTotalStoppages() + DURATION;
      if (playedEnd > System.currentTimeMillis()) {
        setAlarm(context, REQUEST_FULL_TIME, FULL_TIME_ALARM, playedEnd);
      }
      if (elapsedEnd > System.currentTimeMillis()) {
        setAlarm(context, REQUEST_ELAPSED, ELAPSED_ALARM, elapsedEnd);
      }
    } else {
      if (elapsedEnd > System.currentTimeMillis()) {
        setAlarm(context, REQUEST_FULL_TIME, FULL_TIME_ALARM, elapsedEnd);
      }
    }
  }
  .
  .
  .
  }

这些方法主要有两个功能:首先设置MatchTimer的状态,然后设置时间提醒的闹铃,改变参数就可以播放闹铃。这个功能还可以封装成一个工具方法,叫setUpdate()。这样外部也可以触发计时器的更新。

我们使用标准AlarmManager的方法来设置闹铃:


  public class MatchTimerReceiver extends BroadcastReceiver {
  .
  .
  .
  public static final int MINUTE_MILLIS = 60000;
  .
  .
  .

  private void setRepeatingAlarm(Context context, int requestCode, Intent intent) {
    AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
    PendingIntent pendingIntent = PendingIntent.getBroadcast(context, requestCode, intent, PendingIntent.FLAG_UPDATE_CURRENT);
    alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), MINUTE_MILLIS, pendingIntent);
  }

  private boolean isAlarmSet(Context context, int requestCode, Intent intent) {
    return PendingIntent.getBroadcast(context, requestCode, intent, PendingIntent.FLAG_NO_CREATE) != null;
  }

  private void setAlarm(Context context, int requestCode, Intent intent, long time) {
    AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
    PendingIntent pendingIntent = PendingIntent.getBroadcast(context, requestCode, intent, PendingIntent.FLAG_UPDATE_CURRENT);
    alarmManager.setExact(AlarmManager.RTC_WAKEUP, time, pendingIntent);
  }

  private void cancelAlarm(Context context, int requestCode, Intent intent) {
    PendingIntent pendingIntent = PendingIntent.getBroadcast(context, requestCode, intent, PendingIntent.FLAG_NO_CREATE);
    if (pendingIntent != null) {
      AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
      alarmManager.cancel(pendingIntent);
      pendingIntent.cancel();
    }
  }
  .
  .
  .
  }

这里值得讨论的是setRepeatingAlarm()这个方法。因为在Wear在实现方式上有点不一样。我们会在Start事件中每秒钟触发一次闹铃更新Notification动作,所以这里需要记录具体已经过去了多少分钟。正常来说我们会每隔60秒触发一次这个动作,但是在Wear上不能这么做。原因是——当设备在唤醒着的时候可以这样做,但是如果设备进入睡眠状态就需要重新计算下一分钟的边界值。这就需要异步更新部件,然后设备只需要每分钟唤醒一次。一分钟结束后在计时器需要更新状态的时候触发操作。

对于我们的计时器应用来说,显示的分钟数会比实际时间少1分钟。但是显示分钟并不要求非常实时(但显示秒数时需要非常精确),所以我们可以这样操作:

完整的alarm Handler是这样使用振动服务的:


  public class MatchTimerReceiver extends BroadcastReceiver {
  .
  .
  .
  private static final long[] ELAPSED_PATTERN = {0, 500, 250, 500, 250, 500};
  private static final long[] FULL_TIME_PATTERN = {0, 1000, 500, 1000, 500, 1000};

  private void elapsedAlarm(Context context) {
    Vibrator vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
    vibrator.vibrate(ELAPSED_PATTERN, -1);
  }

  private void fullTimeAlarm(Context context) {
    Vibrator vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
    vibrator.vibrate(FULL_TIME_PATTERN, -1);
  }
  .
  .
  .
  }

最后,我们通过这个方法来构造Notification然后呈现给用户:


  public class MatchTimerReceiver extends BroadcastReceiver {
  public static final int NOTIFICATION_ID = 1;
  .
  .
  .
  private void updateNotification(Context context, MatchTimer timer) {
    NotificationBuilder builder = new NotificationBuilder(context, timer);
    Notification notification = builder.buildNotification();
    NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
    notificationManager.notify(NOTIFICATION_ID, notification);
  }
  }

Notification是Wear计时器的一个重要的部分,这里还需要一个自定义类来构造这些Notification通知。下一篇文章我们会讲如何在计时器app中使用Notification。

Match Timer可以在Google Play上下载:Match Timer

(0)

相关推荐

  • android之计时器(Chronometer)的使用以及常用的方法

    在Android的SDK中,为我们提供了一个计时器,这个计时器称为Chronometer,我们可以成它为Android的一个组件,同时它也具备自己独有的方法.下面我们举例介绍下这个计时器的使用以及常用的方法. 跟其他UI组件一样,当我们要使用它的时候,在布局文件中对应的位置声明定义计时器的位置与属性. 复制代码 代码如下: <Chronometer android:id="@+id/chronometer" android:layout_width="wrap_cont

  • 学习使用Android Chronometer计时器

    本文实例为大家分享了Android Chronometer计时器基本使用方法,供大家参考,具体内容如下 在默认情况下,Chronometer组件只输出MM:SS或H:MM:SS的时间格式.例如,当计时到1分20秒时,Chronometer组件会显示01:20.如果想改变显示的信息内容,可以使用Chronometer类的setFormat方法.该方法需要一个String变量,并使用"%s"表示计时信息.例如,使用setFormat("计时信息:%s")设置显示信息,C

  • Android时分秒计时器的两种实现方法

    可能我们在开发中会时常用到计时器这玩意儿,比如在录像的时候,我们可能需要在右上角显示一个计时器.这个东西其实实现起来非常简单. 只需要用一个控件Chronometer,是的,就这么简单,我都不好意思讲述一下了. <Chronometer android:layout_width="wrap_content" android:layout_height="wrap_content" android:format="%s" android:id

  • Android利用SurfaceView实现简单计时器

    自学了android有几个月了,跟着网上的节奏,应该早点写些博客来提高自己的水准的.但苦于技术水准始终不自信(也是不过关的结果吧),就一直只是将自己学习过程中的问题和重要的知识点写在自己的笔记文档中. 但,总感觉一个人写下来成就感还是欠缺了那么一些,而且有些问题及解答方法抛出来,是有可能得到更多好的反馈及解决方案的.于是,本着不作不会死的心态,一步一步在技术成长的道路前行-->这篇博客就是其中一步! 若博客中有些技术知识点有误或者有更优化的解答方案,还望各位小伙伴多多指出. 以下是正题了: 目标

  • android开发教程之间隔执行程序(android计时器)

    下面是每隔一段时间就执行某个操作,直到关闭定时操作: 复制代码 代码如下: final Handler handler = new Handler();     Runnable runnable = new Runnable(){         @Override         public void run() {             // TODO Auto-generated method stub             // 在此处添加执行的代码             ha

  • Android实现的秒表计时器示例

    本文实例讲述了Android实现的秒表计时器.分享给大家供大家参考,具体如下: package com.liu.time; import java.util.Timer; import java.util.TimerTask; import android.app.Activity; import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; imp

  • Android中CountDownTimer倒计时器用法实例

    本文实例讲述了Android中CountDownTimer倒计时器用法.分享给大家供大家参考,具体如下: 在平时我们编程的时候,经常会用到倒计时这个功能,很多人不知道Android已经帮封装好了一个类,往往都自己写.现在发现了这个类,大家共享一下: 在一个TextView不断显示剩下的时间,代码如下: private TextView vertifyView; private CountDownTimer timer = new CountDownTimer(10000, 1000) { @Ov

  • Android 编程下的计时器代码

    同样,为了防止用户恶意的频繁发送激活码,应用中需要对用户发送激活码的时间间隔进行限制,这时就需要用到倒计时器了,大概流程是这样的:页面初始化的时候,按钮为可点击状态,用户在点击"发送激活码"后按钮变为不可点击状态,同时按钮上的文字变为倒计时状态,倒计时结束后,按钮变为可点击状态,文字变为"发送激活码".具体逻辑看下面的代码: 复制代码 代码如下: package cn.sunzn.countdown; import android.app.Activity;impo

  • Android编程之简单计时器实现方法

    本文实例讲述了Android编程之简单计时器实现方法.分享给大家供大家参考,具体如下: 这里利用ContextMenu(上下文菜单),Chronometer实现简单计数器. Main.xml: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android

  • android计时器,时间计算器的实现方法

    需求:默认为"00:00:00",点击开始按钮时清零后开始计时,出现如10:28:34.点击停止的时候停止计时.问题:使用Calendar DateFormat的方法,不设置时区获取到的小时是本地时区的(东八区的就是8),设置成GMT标准时区获取到的时间是12小时(12:00:00),设置24小时制无效.在开始时间加减各种小时都无效,而且计时只能到12小时就自动跳上去了,始终无法出现默认状态00:00:00开始计时的效果.尝试各种时间设置方法无效后只能自己写一个根据秒数转换时间格式字符

随机推荐