Android开发之设计数字时钟
Q7nl1s admin

数字时钟

[TOC]

本次作业的目标是使用 Timer 和 TimerTask 实现一个数字时钟应用程序。

运行效果

image-20230321133448265

功能需求

  1. 显示当前时间,包括 24 小时制时钟、上下午、秒数、日期和星期几。
  2. 实现数字时钟的刷新和更新,使其能够自动显示当前时间。
  3. 根据时间动态改变

实现思路:

1.布局设计

调整 ActionBar 的颜色

image-20230319225213555

修改 ActionBar 颜色

image-20230319225255285

在 themes.xml(night) 中增加 background 属性,以及设置黑夜字体为白色

image-20230321132330689

使用 ConstraintLayout 布局,包含 5 个 TextView 控件,分别用于显示当前时间、日期、星期几、上下午和秒数。

image-20230319214449574

对 weekday_text_view 和 date_text_view 设置基线对齐

image-20230321132521618

当前时间控件

image-20230319215507083
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<TextView
android:id="@+id/time_text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/digitalnormal"
android:letterSpacing="0.04"
android:text="00:00"
android:textSize="90sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.326"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.402" />

新建 Android Resource Directory 取名为 font 包,将电子数字字体下载后放置于 font 包中调用

image-20230319214830456

日期控件

image-20230319215442614
1
2
3
4
5
6
7
8
9
10
11
12
13
<TextView
android:id="@+id/date_text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="0000/00/00"
android:textSize="20sp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.664"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.536" />

星期几控件

image-20230319215424848
1
2
3
4
5
6
7
8
9
10
11
<TextView
android:id="@+id/weekday_text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="周一"
android:textSize="20sp"
android:textStyle="bold"
app:layout_constraintBaseline_toBaselineOf="@+id/date_text_view"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.234"
app:layout_constraintStart_toStartOf="parent" />

上下午控件

image-20230319215407872
1
2
3
4
5
6
7
8
9
10
11
12
13
<TextView
android:id="@+id/ap_text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="AM"
android:textSize="20sp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.813"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.373" />

秒数控件

image-20230319215349149
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<TextView
android:id="@+id/second_text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/digitalnormal"
android:letterSpacing="0.01"
android:text="00"
android:textSize="35sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="@+id/ap_text_view"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.438" />

2.定时器设计

使用 Timer 和 TimerTask 实现定时器,定时器每隔 1 秒更新一次 TextView 的值,以实现时钟自动刷新的效果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
private Timer timer;
TimerTask task;

public void init(){
TextView second_text_view = (TextView) findViewById( R.id.second_text_view ); // 秒数
TextView time_text_view = (TextView) findViewById( R.id.time_text_view ); // 24小时制时钟
TextView ap_text_view = (TextView) findViewById( R.id.ap_text_view ); // 上下午
TextView date_text_view = (TextView) findViewById( R.id.date_text_view ); // 年份月份日期
TextView weekday_text_view = (TextView) findViewById( R.id.weekday_text_view ); // 星期几

timer = new Timer(); //定时器
task = new TimerTask() { //定时任务--子线程(TimerTask是一个抽象类)
@Override
public void run() {
// 在定时任务线程中再启动一个 runOnUiThread 线程去更新UI
runOnUiThread(new Runnable() {
@Override
public void run() {
// 说明:Android规定只有主线程才能更新UI,在TimerTask线程中是不能直接更新UI的,
// 但可调用runOnUiThread方法将一个Runnable子线程任务放到主线程中执行。
...
}
}); //end runOnUiThread
}
};
}

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_clock);
init();
//0秒之后执行task任务,每隔1秒再执行一次task
timer.schedule(task, 0, 1000);
}

3.时间格式化

使用 SimpleDateFormat 类将当前时间格式化为需要的字符串,再设置到 TextView 控件上。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
@Override
public void run() {
// 说明:Android规定只有主线程才能更新UI,在TimerTask线程中是不能直接更新UI的,
// 但可调用runOnUiThread方法将一个Runnable子线程任务放到主线程中执行。

// 获取当前时间
Date now = new Date();
Calendar calendar = Calendar.getInstance();
calendar.setTime(now);

// 格式化时间和日期
Locale englishLocale = Locale.ENGLISH; // 区域设置(硬编码为英语),否则会输出上下午而不是 AM/PM
SimpleDateFormat sdfTime = new SimpleDateFormat("hh:mm", Locale.getDefault());
// 获取当前小时
SimpleDateFormat sdfTimeHour = new SimpleDateFormat("hh", Locale.getDefault());
SimpleDateFormat sdfDate = new SimpleDateFormat("yyyy/MM/dd", Locale.getDefault());
SimpleDateFormat sdfSeconds = new SimpleDateFormat("ss", Locale.getDefault());
SimpleDateFormat sdfWeekday = new SimpleDateFormat("EEEE", Locale.getDefault());
SimpleDateFormat sdfAmPm = new SimpleDateFormat("a", englishLocale);
String time = sdfTime.format(now);
int timeHour = Integer.parseInt(sdfTimeHour.format(now));
String date = sdfDate.format(now);
String seconds = sdfSeconds.format(now);
String weekday = sdfWeekday.format(now);
String amPm = sdfAmPm.format(now);

// 根据当前时间来自动切换 白天/黑夜 主题
if ( 6 <= timeHour && timeHour <= 17 ) {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO); // 开启白天主题
} else {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES); // 开启夜间主题
}
// 设置文本
time_text_view.setText(time);
date_text_view.setText(date);
second_text_view.setText(seconds);
// 把星期X替换为周X
String formattedWeekday;
if (weekday.startsWith("星期")) {
formattedWeekday = weekday.replace("星期", "周");
} else {
formattedWeekday = weekday;
}
weekday_text_view.setText(formattedWeekday);
ap_text_view.setText(amPm);
}

4.多语言支持

由于时钟需要支持多语言,特别是上下午的显示,为了确保语言的准确性,需要将 SimpleDateFormat 中的语言设置为硬编码。

获取当前系统语言

1
SimpleDateFormat sdfTime = new SimpleDateFormat("hh:mm", Locale.getDefault());

自定义的区域语言

1
Locale englishLocale = Locale.ENGLISH; // 区域设置(硬编码为英语),否则会输出上下午而不是 AM/PM

实现细节

1.初始化时钟

在 onCreate() 方法中调用 init() 方法,该方法主要用于初始化 TextView 控件和定时器。

2.定时器

在定时器中,每秒更新一次时间和日期的字符串,并使用 runOnUiThread() 方法在主线程中更新 UI。

1
2
3
4
5
6
7
8
9
10
11
12
13
task = new TimerTask() { //定时任务--子线程(TimerTask是一个抽象类)
@Override
public void run() {
// 在定时任务线程中再启动一个 runOnUiThread 线程去更新UI
runOnUiThread(new Runnable() {
@Override
public void run() {
// 说明:Android规定只有主线程才能更新UI,在TimerTask线程中是不能直接更新UI的,
// 但可调用runOnUiThread方法将一个Runnable子线程任务放到主线程中执行。
}
}); //end runOnUiThread
}
};

3.多语言支持

在 SimpleDateFormat 构造函数中,使用 Locale.getDefault() 获取当前系统语言,并将 SimpleDateFormat 中的语言设置为该语言。

1
2
3
4
5
6
7
8
9
 // 格式化时间和日期
Locale englishLocale = Locale.ENGLISH; // 区域设置(硬编码为英语),否则会输出上下午而不是 AM/PM
SimpleDateFormat sdfTime = new SimpleDateFormat("hh:mm", Locale.getDefault());
// 获取当前小时
SimpleDateFormat sdfTimeHour = new SimpleDateFormat("hh", Locale.getDefault());
SimpleDateFormat sdfDate = new SimpleDateFormat("yyyy/MM/dd", Locale.getDefault());
SimpleDateFormat sdfSeconds = new SimpleDateFormat("ss", Locale.getDefault());
SimpleDateFormat sdfWeekday = new SimpleDateFormat("EEEE", Locale.getDefault());
SimpleDateFormat sdfAmPm = new SimpleDateFormat("a", englishLocale);

4.星期几的转换

在将星期几显示到 TextView 控件中之前,需要先将“星期X”转换为“周X”,使用 String 的 replace() 方法进行替换。

1
2
3
4
5
6
7
8
// 把星期X替换为周X
String formattedWeekday;
if (weekday.startsWith("星期")) {
formattedWeekday = weekday.replace("星期", "周");
} else {
formattedWeekday = weekday;
}
weekday_text_view.setText(formattedWeekday);

5.根据当前时间自动切换主题

获取当前时间,如果在 6:00-17:00 间则设置白天主题,否则设置为夜间主题

1
2
3
4
5
if ( 6 <= timeHour && timeHour <= 17 ) {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO); // 开启白天主题
} else {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES); // 开启夜间主题
}

代码分析

1.布局文件

activity_clock.xml

使用 ConstraintLayout 布局,包含 5 个 TextView 控件,分别用于显示当前时间、日期、星期几、上下午和秒数。其中,上下午的控件使用了硬编码的方式来确保多语言的准确性。

2.Java 代码

ClockActivity.java

该类主要实现时钟的逻辑和更新。在 onCreate() 方法中,初始化 TextView 控件和定时器,定时器每隔 1 秒更新一次时间和日期,并使用 runOnUiThread() 方法在主线程中更新 UI。同时,在 init() 方法中,使用 SimpleDateFormat 将当前时间格式化为需要的字符串,再设置到 TextView 控件上。在星期几的显示上,使用 String 的 replace() 方法进行替换,将“星期X”转换为“周X”。获取当前时间,如果在 6:00-17:00 间则设置白天主题,否则设置为夜间主题。

总结

本次作业中,我们使用 Timer 和 TimerTask 实现了一个数字时钟应用程序。通过使用 SimpleDateFormat 将时间格式化为需要的字符串,再设置到 TextView 控件上,并使用定时器每隔 1 秒更新一次时间和日期,实现了时钟自动刷新的效果。同时,我们还学习了如何在多语言环境下正确地显示上下午,并使用硬编码来实现语言设置,将上下午转化为 AM/PM 输出,以及通过获取当前的时间来动态更改主题。

 Comments
Comment plugin failed to load
Loading comment plugin
Powered by Hexo & Theme Keep
Unique Visitor Page View