Android 时钟TextClock 使用及源码分析

简介: TextClock主要XML属性如下:常用方法示例主界面布局文件主界面代码示例分析源码分析setFormat12Hour()setFormat24Hour()chooseFormat()abc()onTimeChanged()setContentDescription()setTimeZone()createTime()TimeZone.getTimeZone(timeZone)getCustomTimeZone(id)Calendar.getInstance()

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中添加)。


示例


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


微信图片_20220521142703.png


主界面布局文件


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_margin="@dimen/dimen_20"
    android:layout_height="match_parent">
    <TextView
        android:id="@+id/tv_is24HourModeEnabled"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="@dimen/dimen_10"
        android:textSize="@dimen/text_size_16"
        android:padding="@dimen/dimen_10"/>
    <TextClock
        android:id="@+id/tc_timeText_12"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:textColor="@color/black"
        android:textSize="30sp"
        android:textStyle="bold"/>
    <TextClock
        android:id="@+id/tc_dateText_12"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:textColor="@color/black"
        android:textSize="20sp"/>
    <TextView
        android:id="@+id/tv_12hour"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="@dimen/dimen_10"
        android:textSize="@dimen/text_size_16"
        android:padding="@dimen/dimen_10"/>
    <TextClock
        android:id="@+id/tc_timeText_24"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="@dimen/dimen_20"
        android:format12Hour="hh:mm:ss"
        android:format24Hour="HH:mm:ss"
        android:gravity="center"
        android:textColor="@color/black"
        android:textSize="30sp"
        android:textStyle="bold"/>
    <TextClock
        android:id="@+id/tc_dateText_24"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:format12Hour="yyyy/MM/dd E"
        android:format24Hour="yyyy/MM/dd E"
        android:gravity="center"
        android:textColor="@color/black"
        android:textSize="20sp"/>
    <TextView
        android:id="@+id/tv_24hour"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="@dimen/dimen_10"
        android:textSize="@dimen/text_size_16"
        android:padding="@dimen/dimen_10"/>
</LinearLayout>


主界面代码


public class TextClockActivity extends AppCompatActivity {
    private TextClock tc_timeText_12,tc_dateText_12,tc_timeText_24,tc_dateText_24;
    private TextView tv_12hour,tv_24hour,tv_is24HourModeEnabled;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_textview_textclock);//加载布局文件
        initView();
    }
    private void initView() {
        tv_is24HourModeEnabled = findViewById(R.id.tv_is24HourModeEnabled);
        tc_timeText_12 = findViewById(R.id.tc_timeText_12);
        tc_dateText_12 = findViewById(R.id.tc_dateText_12);
        tv_12hour = findViewById(R.id.tv_12hour);
        //setTimeZone使用(UTC-7)无效,
        //原因:源码未对UTC+(-)进行处理,下面有具体的源码分析
        tc_timeText_12.setTimeZone("America/Los_Angeles");//有效
        tc_dateText_12.setTimeZone("America/Los_Angeles");//有效
        //tc_timeText_12.setTimeZone("GMT+7:00");//有效
        //tc_dateText_12.setTimeZone("GMT+7:00");//有效
        tc_dateText_12.setFormat24Hour("HH:mm");
        tc_dateText_12.setFormat12Hour("yyyy/MM/dd E");
        // EEEE:星期五 ;E/EE/EEE:周五;a:上午/下午
        tc_dateText_12.setFormat24Hour("yyyy年MM月dd日 EEEE aa HH:mm:ss");
        String format12 = "\n12小时模式格式:"+tc_timeText_12.getFormat12Hour();
        format12 = format12+"\n24小时模式格式:"+tc_timeText_12.getFormat24Hour();
        format12 = format12+"\n时区:"+tc_timeText_12.getTimeZone();
        tv_12hour.setText("Format:"+format12);
        tc_timeText_24 = findViewById(R.id.tc_timeText_24);
        tc_dateText_24 = findViewById(R.id.tc_dateText_24);
        tv_24hour = findViewById(R.id.tv_24hour);
        String format = "\n24小时模式格式:"+tc_timeText_24.getFormat24Hour();
        format = format+"\n12小时模式格式:"+tc_timeText_24.getFormat12Hour();
        format = format+"\n时区:"+tc_timeText_24.getTimeZone();
        String timeZome =TimeZone.getDefault().getDisplayName(true, TimeZone.SHORT);
        format = format+"\n时区:"+timeZome;
        tv_24hour.setText("Format:"+format);
        String is24HourMode = String.format("系统当前是否使用 24 小时模式:%s。", tc_dateText_24.is24HourModeEnabled());
        tv_is24HourModeEnabled.setText(is24HourMode);
    }
}


示例分析


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


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


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


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


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


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


源码分析


setFormat12Hour()


public void setFormat12Hour(CharSequence format) {
        mFormat12 = format;
        chooseFormat();
        onTimeChanged();
    }


setFormat24Hour()


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



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


chooseFormat()


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


 /**
     * Selects either one of {@link #getFormat12Hour()} or {@link #getFormat24Hour()}
     * depending on whether the user has selected 24-hour format.
     */
    private void chooseFormat() {
        final boolean format24Requested = is24HourModeEnabled();
        LocaleData ld = LocaleData.get(getContext().getResources().getConfiguration().locale);
        if (format24Requested) {
            mFormat = abc(mFormat24, mFormat12, ld.timeFormat_Hm);
            mDescFormat = abc(mDescFormat24, mDescFormat12, mFormat);
        } else {
            mFormat = abc(mFormat12, mFormat24, ld.timeFormat_hm);
            mDescFormat = abc(mDescFormat12, mDescFormat24, mFormat);
        }
        boolean hadSeconds = mHasSeconds;
        mHasSeconds = DateFormat.hasSeconds(mFormat);
        if (mShouldRunTicker && hadSeconds != mHasSeconds) {
            if (hadSeconds) getHandler().removeCallbacks(mTicker);
            else mTicker.run();
        }
    }


abc()


  • a不为null,返回a;否则继续往下看;
  • b不为null,返回b;否则返回c;
  • 例如:24小时制:a=24小时格式;b=12小时格式,c默认格式

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


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


onTimeChanged()


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


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


setContentDescription()


       设置 控件 的内容描述。


  @RemotableViewMethod
    public void setContentDescription(CharSequence contentDescription) {
        //mContentDescription:简要描述视图,主要用于辅助功能支持。 
        if (mContentDescription == null) {
            if (contentDescription == null) {
                return;
            }
        } else if (mContentDescription.equals(contentDescription)) {
            return;
        }
        mContentDescription = contentDescription;
        final boolean nonEmptyDesc = contentDescription != null && contentDescription.length() > 0;
        if (nonEmptyDesc && getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
            setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
            notifySubtreeAccessibilityStateChangedIfNeeded();
        } else {
            notifyViewAccessibilityStateChangedIfNeeded(
                    AccessibilityEvent.CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION);
        }
    }


setTimeZone()


       设置时区


public void setTimeZone(String timeZone) 
{
    mTimeZone = timeZone;
    createTime(timeZone);
    onTimeChanged();
}


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


createTime()


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


private void createTime(String timeZone) {
        if (timeZone != null) {
            mTime = Calendar.getInstance(TimeZone.getTimeZone(timeZone));
        } else {
            mTime = Calendar.getInstance();
        }
    }


从上面看出不管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+(-)时区未做设置所以都无效。


public static synchronized TimeZone getTimeZone(String id) {
        if (id == null) {
            throw new NullPointerException("id == null");
        }
        // Special cases? These can clone an existing instance.
        if (id.length() == 3) {
            if (id.equals("GMT")) {
                return (TimeZone) GMT.clone();
            }
            if (id.equals("UTC")) {
                return (TimeZone) UTC.clone();
            }
        }
        // In the database?
        TimeZone zone = null;
        try {
            zone = ZoneInfoDb.getInstance().makeTimeZone(id);
        } catch (IOException ignored) {
        }
        // Custom time zone?
        if (zone == null && id.length() > 3 && id.startsWith("GMT")) {
            zone = getCustomTimeZone(id);
        }
        // We never return null; on failure we return the equivalent of "GMT".
        return (zone != null) ? zone : (TimeZone) GMT.clone();
    }


下面咱们看看getCustomTimeZone(id)。


getCustomTimeZone(id)


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


private static TimeZone getCustomTimeZone(String id) {
        Matcher m = NoImagePreloadHolder.CUSTOM_ZONE_ID_PATTERN.matcher(id);
        if (!m.matches()) {
            return null;
        }
        int hour;
        int minute = 0;
        try {
            hour = Integer.parseInt(m.group(1));
            if (m.group(3) != null) {
                minute = Integer.parseInt(m.group(3));
            }
        } catch (NumberFormatException impossible) {
            throw new AssertionError(impossible);
        }
        if (hour < 0 || hour > 23 || minute < 0 || minute > 59) {
            return null;
        }
        char sign = id.charAt(3);
        int raw = (hour * 3600000) + (minute * 60000);
        if (sign == '-') {
            raw = -raw;
        }
        String cleanId = String.format(Locale.ROOT, "GMT%c%02d:%02d", sign, hour, minute);
        return new SimpleTimeZone(raw, cleanId);
    }


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


Calendar.getInstance()  

 

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



  接着看下面的:createCalendar()


// END Android-added: add getJapaneseImperialInstance()
private static Calendar createCalendar(TimeZone zone, Locale aLocale)
{
        // BEGIN Android-changed: only support GregorianCalendar here
        return new GregorianCalendar(zone, aLocale);
        // END Android-changed: only support GregorianCalendar here
    }


 再往下看GregorianCalendar(zone, aLocale)


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


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

相关文章
|
存储 监控 Android开发
Android卡顿优化 | ANR分析与实战(附ANR-WatchDog源码分析及实战、与AndroidPerformanceMonitor的区别)
Android卡顿优化 | ANR分析与实战(附ANR-WatchDog源码分析及实战、与AndroidPerformanceMonitor的区别)
|
Android开发
Android PackageManagerService源码分析和APK安装原理详解
Android PackageManagerService源码分析和APK安装原理详解
516 1
|
XML 存储 搜索推荐
|
设计模式 XML 缓存
Android体系课学习 之 网络请求库Retrofit源码分析-看这一篇就够了
- 网络请求在我们开发中起的很大比重,有一个好的网络框架可以节省我们的开发工作量,也可以避免一些在开发中不该出现的bug - *Retrofit*是一个轻量级框架,基于*OkHttp*的一个*Restful*框架
|
存储 缓存 Java
Android体系课-开源框架-这是一份详细的Glide源码分析文章
最近在`组件化`开发中准备封装一个`图片加载库`,于是乎就有了这篇文章 本篇文章对`Glide`源码过程做了一个详细的讲解,也是为了记录下自己对`Glide`的理解,以后忘记还可以从这里查找。
|
XML Java Android开发
Android8.1 MTK平台 SystemUI源码分析之 网络信号栏显示刷新(下)
Android8.1 MTK平台 SystemUI源码分析之 网络信号栏显示刷新(下)
237 0
|
XML 数据库 网络虚拟化
Android8.1 MTK平台 SystemUI源码分析之 网络信号栏显示刷新(上)
Android8.1 MTK平台 SystemUI源码分析之 网络信号栏显示刷新
334 0
|
XML Java Linux
Android8.1 MTK平台 SystemUI源码分析之 电池时钟刷新
Android8.1 MTK平台 SystemUI源码分析之 电池时钟刷新
395 0
|
Java 调度 Android开发
android体系课-系统启动流程-之zygote进程启动过程源码分析
笔者刚开始学习Android的时候也和大部分同学一样,只会使用一些应用层面的知识,对于一些比较常见的开源框架如<mark>RxJava</mark>,<mark>OkHttp</mark>,<mark>Retrofit</mark>,以及后来谷歌推出的<mark>协程</mark>等,都只在使用层面,对于他们<mark>内部原理</mark>,基本没有去了解觉得够用就可以了,又比如Activity,Service等四大组件的使用原理,系统开机过程,Launcher启动过程等知之甚少,知其然而不知其所以然,结果就是出现某些问题,不知道从哪里找原因,只能依赖万能的百度,但是百度看多了,你会发现自己
|
Java 调度 Android开发
android体系课-系统启动流程-之SystemServer启动过程源码分析
笔者刚开始学习Android的时候也和大部分同学一样,只会使用一些应用层面的知识,对于一些比较常见的开源框架如<mark>RxJava</mark>,<mark>OkHttp</mark>,<mark>Retrofit</mark>,以及后来谷歌推出的<mark>协程</mark>等,都只在使用层面,对于他们<mark>内部原理</mark>,基本没有去了解觉得够用就可以了,又比如Activity,Service等四大组件的使用原理,系统开机过程,Launcher启动过程等知之甚少,知其然而不知其所以然,结果就是出现某些问题,不知道从哪里找原因,只能依赖万能的百度,但是百度看多了,你会发现自己