当前位置:   article > 正文

Android 时钟TextClock 使用及源码分析

textclock

TextClock

         TextClock 可以将当前日期和/或时间显示为格式化字符串。TextClock 遵循 24 小时格式系统设置。因此,提供「两种不同的格式化模式」

  • 以 24 小时制显示日期/时间,

  • 以 12 小时制显示日期/时间。

        可以调用「is24HourModeEnabled()」 来确定「系统当前是否处于 24 小时模式」

如何格式化日期和时间的规则如下:

  • 在 24 小时模式下:
    • 如果没获取时间,首先通过 getFormat24Hour()返回值

    • 获取失败,则通过 getFormat12Hour()获取返回值

    • 以上都获取失败则使用默认值,例如 ah:mm

  • 在 12 小时模式下:
    • 如果没获取时间,首先通过 getFormat12Hour()返回值

    • 获取失败,则通过 getFormat24Hour()获取返回值

    • 以上都获取失败则使用默认值,例如 HH:mm

主要XML属性如下:

「android:format12Hour」 设置12小时制的格式。

「android:format24Hour」 设置24小时制的格式。

「android:timeZone」 指定要使用的时区,设置后忽略系统时间变化。

常用方法

「setFormat12Hour(CharSequence format)」:设置12小时制的格式。

「setFormat24Hour(CharSequence format)」:设置24小时制的格式。

「setTimeZone(String timeZone)」:设置要在此时钟中使用的指定时区。

「getFormat12Hour()」:返回12小时制的格式。

「getFormat24Hour()」:返回24小时制的格式。

「getTimeZone()」:指示此视图当前使用的时区。

「is24HourModeEnabled()」:指「系统」当前是否使用 24 小时模式。

「onVisibilityAggregated(Boolean isVisible:)」:当此视图的用户可见性可能受到此视图本身、祖先视图或此视图附加到的窗口的更改的影响时调用。

「refreshTime()」:如有必要,更新显示的时间并使视图无效(在API 30中添加)。

示例

看上面介绍十分简单,咱们还是搞个实例了解一下吧,先看效果图。

主界面布局文件

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3.     android:orientation="vertical" android:layout_width="match_parent"
  4.     android:layout_margin="@dimen/dimen_20"
  5.     android:layout_height="match_parent">
  6.     <TextView
  7.         android:id="@+id/tv_is24HourModeEnabled"
  8.         android:layout_width="match_parent"
  9.         android:layout_height="wrap_content"
  10.         android:layout_marginTop="@dimen/dimen_10"
  11.         android:textSize="@dimen/text_size_16"
  12.         android:padding="@dimen/dimen_10"/>
  13.     <TextClock
  14.         android:id="@+id/tc_timeText_12"
  15.         android:layout_width="match_parent"
  16.         android:layout_height="wrap_content"
  17.         android:gravity="center"
  18.         android:textColor="@color/black"
  19.         android:textSize="30sp"
  20.         android:textStyle="bold"/>
  21.     <TextClock
  22.         android:id="@+id/tc_dateText_12"
  23.         android:layout_width="match_parent"
  24.         android:layout_height="wrap_content"
  25.         android:gravity="center"
  26.         android:textColor="@color/black"
  27.         android:textSize="20sp"/>
  28.     <TextView
  29.         android:id="@+id/tv_12hour"
  30.         android:layout_width="match_parent"
  31.         android:layout_height="wrap_content"
  32.         android:layout_marginTop="@dimen/dimen_10"
  33.         android:textSize="@dimen/text_size_16"
  34.         android:padding="@dimen/dimen_10"/>
  35.     <TextClock
  36.         android:id="@+id/tc_timeText_24"
  37.         android:layout_width="match_parent"
  38.         android:layout_height="wrap_content"
  39.         android:layout_marginTop="@dimen/dimen_20"
  40.         android:format12Hour="hh:mm:ss"
  41.         android:format24Hour="HH:mm:ss"
  42.         android:gravity="center"
  43.         android:textColor="@color/black"
  44.         android:textSize="30sp"
  45.         android:textStyle="bold"/>
  46.     <TextClock
  47.         android:id="@+id/tc_dateText_24"
  48.         android:layout_width="match_parent"
  49.         android:layout_height="wrap_content"
  50.         android:format12Hour="yyyy/MM/dd E"
  51.         android:format24Hour="yyyy/MM/dd E"
  52.         android:gravity="center"
  53.         android:textColor="@color/black"
  54.         android:textSize="20sp"/>
  55.     <TextView
  56.         android:id="@+id/tv_24hour"
  57.         android:layout_width="match_parent"
  58.         android:layout_height="wrap_content"
  59.         android:layout_marginTop="@dimen/dimen_10"
  60.         android:textSize="@dimen/text_size_16"
  61.         android:padding="@dimen/dimen_10"/>
  62. </LinearLayout>

主界面代码

  1. public class TextClockActivity extends AppCompatActivity {
  2.     private TextClock tc_timeText_12,tc_dateText_12,tc_timeText_24,tc_dateText_24;
  3.     private TextView tv_12hour,tv_24hour,tv_is24HourModeEnabled;
  4.     @Override
  5.     protected void onCreate(Bundle savedInstanceState) {
  6.         super.onCreate(savedInstanceState);
  7.         setContentView(R.layout.activity_textview_textclock);//加载布局文件
  8.         initView();
  9.     }
  10.     private void initView() {
  11.         tv_is24HourModeEnabled = findViewById(R.id.tv_is24HourModeEnabled);
  12.         tc_timeText_12 = findViewById(R.id.tc_timeText_12);
  13.         tc_dateText_12 = findViewById(R.id.tc_dateText_12);
  14.         tv_12hour = findViewById(R.id.tv_12hour);
  15.         //setTimeZone使用(UTC-7)无效,
  16. //原因:源码未对UTC+(-)进行处理,下面有具体的源码分析
  17.         tc_timeText_12.setTimeZone("America/Los_Angeles");//有效
  18.         tc_dateText_12.setTimeZone("America/Los_Angeles");//有效
  19. //tc_timeText_12.setTimeZone("GMT+7:00");//有效
  20. //tc_dateText_12.setTimeZone("GMT+7:00");//有效
  21.         tc_dateText_12.setFormat24Hour("HH:mm");
  22.         tc_dateText_12.setFormat12Hour("yyyy/MM/dd E");
  23.         // EEEE:星期五 ;E/EE/EEE:周五;a:上午/下午
  24.         tc_dateText_12.setFormat24Hour("yyyy年MM月dd日 EEEE aa HH:mm:ss");
  25.         String format12 = "\n12小时模式格式:"+tc_timeText_12.getFormat12Hour();
  26.         format12 = format12+"\n24小时模式格式:"+tc_timeText_12.getFormat24Hour();
  27.         format12 = format12+"\n时区:"+tc_timeText_12.getTimeZone();
  28.         tv_12hour.setText("Format:"+format12);
  29.         tc_timeText_24 = findViewById(R.id.tc_timeText_24);
  30.         tc_dateText_24 = findViewById(R.id.tc_dateText_24);
  31.         tv_24hour = findViewById(R.id.tv_24hour);
  32.         String format = "\n24小时模式格式:"+tc_timeText_24.getFormat24Hour();
  33.         format = format+"\n12小时模式格式:"+tc_timeText_24.getFormat12Hour();
  34.         format = format+"\n时区:"+tc_timeText_24.getTimeZone();
  35.         String timeZome =TimeZone.getDefault().getDisplayName(true, TimeZone.SHORT);
  36.         format = format+"\n时区:"+timeZome;
  37.         tv_24hour.setText("Format:"+format);
  38.         String is24HourMode = String.format("系统当前是否使用 24 小时模式:%s。", tc_dateText_24.is24HourModeEnabled());
  39.         tv_is24HourModeEnabled.setText(is24HourMode);
  40.     }
  41. }

示例分析

手机系统默认是24小时格式

        例一的时间/日期显示格式在代码中设置;

        例一的12小时制格式返回:「ah:mm」 这个是默认值。

        例二的时间/日期显示格式在xml文件中设置;

        例二未设置时区所以时区返回null,可通过下面代码获取系统时区来显示

TimeZone.getDefault().getDisplayName(true, TimeZone.SHORT);

源码分析

setFormat12Hour()

  1. public void setFormat12Hour(CharSequence format) {
  2.         mFormat12 = format;
  3.         chooseFormat();
  4.         onTimeChanged();
  5.     }

setFormat24Hour()

  1. public void setFormat24Hour(CharSequence format) {
  2.         mFormat24 = format;
  3.         chooseFormat();
  4.         onTimeChanged();
  5.     }

        看完「setFormat12Hour」「setFormat24Hour」,你会发现他们除了各自格式赋值,后面都调用了「chooseFormat()」「onTimeChanged()」,下面咱看看这俩方法是干什么的。

chooseFormat()

        先进行了判断是不是24小时制,后面用到了「abc()」;mDescFormat12,mDescFormat24用于内容描述,下面讲到。咱们接着往下看abc()

  1.  /**
  2.      * Selects either one of {@link #getFormat12Hour()} or {@link #getFormat24Hour()}
  3.      * depending on whether the user has selected 24-hour format.
  4.      */
  5.     private void chooseFormat() {
  6.         final boolean format24Requested = is24HourModeEnabled();
  7.         LocaleData ld = LocaleData.get(getContext().getResources().getConfiguration().locale);
  8.         if (format24Requested) {
  9.             mFormat = abc(mFormat24, mFormat12, ld.timeFormat_Hm);
  10.             mDescFormat = abc(mDescFormat24, mDescFormat12, mFormat);
  11.         } else {
  12.             mFormat = abc(mFormat12, mFormat24, ld.timeFormat_hm);
  13.             mDescFormat = abc(mDescFormat12, mDescFormat24, mFormat);
  14.         }
  15.         boolean hadSeconds = mHasSeconds;
  16.         mHasSeconds = DateFormat.hasSeconds(mFormat);
  17.         if (mShouldRunTicker && hadSeconds != mHasSeconds) {
  18.             if (hadSeconds) getHandler().removeCallbacks(mTicker);
  19.             else mTicker.run();
  20.         }
  21.     }

abc()

  • a不为null,返回a;否则继续往下看;

  • b不为null,返回b;否则返回c;

  • 例如:24小时制:a=24小时格式;b=12小时格式,c默认格式

        说白了就是上面的:「如何格式化日期和时间的规则。」

  1.     /**
  2.      * Returns a if not null, else return b if not null, else return c.
  3.      */
  4.     private static CharSequence abc(CharSequence a, CharSequence b, CharSequence c) {
  5.         return a == null ? (b == null ? c : b) : a;
  6.     }

onTimeChanged()

        获取系统当前时间并展示。这里多了一个「setContentDescription()」;咱后面看看是干嘛的

  1.  /**
  2.      * Update the displayed time if this view and its ancestors and window is visible
  3.      */
  4.     @UnsupportedAppUsage
  5.     private void onTimeChanged() {
  6.         mTime.setTimeInMillis(System.currentTimeMillis());
  7.         setText(DateFormat.format(mFormat, mTime));
  8.         setContentDescription(DateFormat.format(mDescFormat, mTime));
  9.     }

setContentDescription()

        设置 控件 的内容描述。

  1.   @RemotableViewMethod
  2.     public void setContentDescription(CharSequence contentDescription) {
  3.         //mContentDescription:简要描述视图,主要用于辅助功能支持。 
  4.         if (mContentDescription == null) {
  5.             if (contentDescription == null) {
  6.                 return;
  7.             }
  8.         } else if (mContentDescription.equals(contentDescription)) {
  9.             return;
  10.         }
  11.         mContentDescription = contentDescription;
  12.         final boolean nonEmptyDesc = contentDescription != null && contentDescription.length() > 0;
  13.         if (nonEmptyDesc && getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
  14.             setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
  15.             notifySubtreeAccessibilityStateChangedIfNeeded();
  16.         } else {
  17.             notifyViewAccessibilityStateChangedIfNeeded(
  18.                     AccessibilityEvent.CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION);
  19.         }
  20.     }

setTimeZone()

        设置时区

  1. public void setTimeZone(String timeZone) 
  2. {
  3.     mTimeZone = timeZone;
  4.     createTime(timeZone);
  5.     onTimeChanged();
  6. }

        设置时区调用了两个方法「createTime()」「onTimeChanged()」,onTimeChanged()获取系统当前时间并展示,咱们在上面讲过了,这里咱重点看createTime()

createTime()

        判断是使用设置的时区还是用系统时区

  1. private void createTime(String timeZone) {
  2.         if (timeZone != null) {
  3.             mTime = Calendar.getInstance(TimeZone.getTimeZone(timeZone));
  4.         } else {
  5.             mTime = Calendar.getInstance();
  6.         }
  7.     }

        从上面看出不管timeZong是否为null都会调用「Calendar.getInstance()」,区别在是否传参,还有「TimeZone.getTimeZone(timeZone)」 在下面咱们看看这两个方法。

TimeZone.getTimeZone(timeZone)

这个是重点,这里面包含了:

  • zone = ZoneInfoDb.getInstance().makeTimeZone(id);设置时区成功(如:America/Los_Angeles)

  • zone = getCustomTimeZone(id);设置时区成功(如:GMT+7:00)

  • UTC失败,从这里看到设置UTC+(-)时区未做设置所以都无效。

  1. public static synchronized TimeZone getTimeZone(String id) {
  2.         if (id == null) {
  3.             throw new NullPointerException("id == null");
  4.         }
  5.         // Special cases? These can clone an existing instance.
  6.         if (id.length() == 3) {
  7.             if (id.equals("GMT")) {
  8.                 return (TimeZone) GMT.clone();
  9.             }
  10.             if (id.equals("UTC")) {
  11.                 return (TimeZone) UTC.clone();
  12.             }
  13.         }
  14.         // In the database?
  15.         TimeZone zone = null;
  16.         try {
  17.             zone = ZoneInfoDb.getInstance().makeTimeZone(id);
  18.         } catch (IOException ignored) {
  19.         }
  20.         // Custom time zone?
  21.         if (zone == null && id.length() > 3 && id.startsWith("GMT")) {
  22.             zone = getCustomTimeZone(id);
  23.         }
  24.         // We never return null; on failure we return the equivalent of "GMT".
  25.         return (zone != null) ? zone : (TimeZone) GMT.clone();
  26.     }

        下面咱们看看getCustomTimeZone(id)。

getCustomTimeZone(id)

        从下面代码可以看到 返回一个新的 SimpleTimeZone:格式为“GMT[+|-]hh[[:]mm]”的 ID,或者返回null。

  1. private static TimeZone getCustomTimeZone(String id) {
  2.         Matcher m = NoImagePreloadHolder.CUSTOM_ZONE_ID_PATTERN.matcher(id);
  3.         if (!m.matches()) {
  4.             return null;
  5.         }
  6.         int hour;
  7.         int minute = 0;
  8.         try {
  9.             hour = Integer.parseInt(m.group(1));
  10.             if (m.group(3) != null) {
  11.                 minute = Integer.parseInt(m.group(3));
  12.             }
  13.         } catch (NumberFormatException impossible) {
  14.             throw new AssertionError(impossible);
  15.         }
  16.         if (hour < 0 || hour > 23 || minute < 0 || minute > 59) {
  17.             return null;
  18.         }
  19.         char sign = id.charAt(3);
  20.         int raw = (hour * 3600000) + (minute * 60000);
  21.         if (sign == '-') {
  22.             raw = -raw;
  23.         }
  24.         String cleanId = String.format(Locale.ROOT, "GMT%c%02d:%02d", sign, hour, minute);
  25.         return new SimpleTimeZone(raw, cleanId);
  26.     }

        上面的就是对传参进行处理下面咱们看看刚刚说到的Calendar.getInstance()

Calendar.getInstance()       

  1. //设置的时区和系统时区对比看看是不是当前时区
  2. public static Calendar getInstance(TimeZone zone)
  3. {
  4.         return createCalendar(zone, Locale.getDefault(Locale.Category.FORMAT));
  5. }

        接着看下面的:createCalendar()

  1. // END Android-added: add getJapaneseImperialInstance()
  2. private static Calendar createCalendar(TimeZone zone, Locale aLocale)
  3. {
  4.         // BEGIN Android-changed: only support GregorianCalendar here
  5.         return new GregorianCalendar(zone, aLocale);
  6.         // END Android-changed: only support GregorianCalendar here
  7.     }

        再往下看GregorianCalendar(zone, aLocale)

  1.  /**
  2.      * Constructs a <code>GregorianCalendar</code> based on the current time
  3.      * in the given time zone with the given locale.
  4.      *
  5.      * @param zone the given time zone.
  6.      * @param aLocale the given locale.
  7.      */
  8.     public GregorianCalendar(TimeZone zone, Locale aLocale) {
  9.         super(zone, aLocale);
  10.         gdate = (BaseCalendar.Date) gcal.newCalendarDate(zone);
  11.         //根据给定的 long 值设置当前时间。 
  12.         setTimeInMillis(System.currentTimeMillis());
  13.     }

        以上就是本文的全部内容,希望对大家学习Android TextClock有所帮助和启发。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/269733
推荐阅读
相关标签
  

闽ICP备14008679号