You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
xiaomi/ui/DateTimePicker.java

245 lines
16 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/*
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// 包声明表明该类所在的包名为net.micode.notes.ui
package net.micode.notes.ui;
import java.text.DateFormatSymbols;
import java.util.Calendar;
import net.micode.notes.R;
import android.content.Context;
import android.text.format.DateFormat;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.NumberPicker;
// DateTimePicker类继承自FrameLayout它是一个自定义的视图组件用于实现日期和时间的选择功能用户可以通过该组件方便地设置年、月、日、时、分以及选择12/24小时制等。
public class DateTimePicker extends FrameLayout {
// 定义默认的启用状态默认为启用true用于控制整个DateTimePicker组件及其内部子控件的可交互状态。
private static final boolean DEFAULT_ENABLE_STATE = true;
// 定义半天的小时数用于12小时制相关的时间计算和判断例如区分上午和下午的时间范围。
private static final int HOURS_IN_HALF_DAY = 12;
// 定义一天的总小时数用于24小时制相关的操作和判断。
private static final int HOURS_IN_ALL_DAY = 24;
// 定义一周的天数,用于日期选择器(如显示一周内的日期选项等)相关的操作。
private static final int DAYS_IN_ALL_WEEK = 7;
// 定义日期选择器NumberPicker的最小值通常从0开始表示一周内日期选项的最小索引。
private static final int DATE_SPINNER_MIN_VAL = 0;
// 定义日期选择器NumberPicker的最大值根据一周7天最大值为6索引从0开始
private static final int DATE_SPINNER_MAX_VAL = DAYS_IN_ALL_WEEK - 1;
// 定义24小时制视图下小时选择器NumberPicker的最小值即0点。
private static final int HOUR_SPINNER_MIN_VAL_24_HOUR_VIEW = 0;
// 定义24小时制视图下小时选择器NumberPicker的最大值即23点。
private static final int HOUR_SPINNER_MAX_VAL_24_HOUR_VIEW = 23;
// 定义12小时制视图下小时选择器NumberPicker的最小值通常为112小时制习惯从1开始计数
private static final int HOUR_SPINNER_MIN_VAL_12_HOUR_VIEW = 1;
// 定义12小时制视图下小时选择器NumberPicker的最大值即12点。
private static final int HOUR_SPINNER_MAX_VAL_12_HOUR_VIEW = 12;
// 定义分钟选择器NumberPicker的最小值即0分钟。
private static final int MINUT_SPINNER_MIN_VAL = 0;
// 定义分钟选择器NumberPicker的最大值即59分钟。
private static final int MINUT_SPINNER_MAX_VAL = 59;
// 定义上午/下午AM/PM选择器NumberPicker的最小值通常为0对应AM
private static final int AMPM_SPINNER_MIN_VAL = 0;
// 定义上午/下午AM/PM选择器NumberPicker的最大值通常为1对应PM
private static final int AMPM_SPINNER_MAX_VAL = 1;
// 用于显示日期的NumberPicker控件用户可以通过它选择具体的日期以一周内的某一天来表示
private final NumberPicker mDateSpinner;
// 用于显示小时的NumberPicker控件根据设置的12/24小时制显示相应范围的小时选项供用户选择。
private final NumberPicker mHourSpinner;
// 用于显示分钟的NumberPicker控件提供0到59分钟的选项供用户选择。
private final NumberPicker mMinuteSpinner;
// 用于显示上午/下午AM/PM的NumberPicker控件在12小时制下用于区分时间段仅在非24小时制时可见。
private final NumberPicker mAmPmSpinner;
// Calendar对象用于存储和操作当前选择的日期和时间信息方便进行各种日期时间的计算和设置。
private Calendar mDate;
// 用于存储一周内各天显示名称的字符串数组,例如“周一”“周二”等,会根据系统设置进行本地化显示。
private String[] mDateDisplayValues = new String[DAYS_IN_ALL_WEEK];
// 用于标记当前时间是否处于上午AM根据小时数等情况进行更新用于12小时制下的显示和逻辑判断。
private boolean mIsAm;
// 用于标记是否处于24小时制视图true表示24小时制false表示12小时制决定了小时选择器和上午/下午选择器的显示及相关逻辑。
private boolean mIs24HourView;
// 用于存储组件的启用状态,初始化为默认的启用状态,通过相应方法可以修改并控制组件及其内部控件的可操作性。
private boolean mIsEnabled = DEFAULT_ENABLE_STATE;
// 用于标记是否处于初始化阶段,在初始化过程中一些逻辑处理可能与正常使用阶段有所不同,避免不必要的重复操作或异常情况。
private boolean mInitialising;
// 定义一个接口类型的变量用于设置日期时间改变时的回调监听器外部类可以实现该接口来监听用户在DateTimePicker上操作导致的日期时间变化情况。
private OnDateTimeChangedListener mOnDateTimeChangedListener;
// 内部类实现了NumberPicker.OnValueChangeListener接口用于监听日期选择器mDateSpinner的值变化事件。
// 当用户在日期选择器上选择了不同的日期时会触发该监听器的onValueChange方法进而更新内部的日期信息mDate以及相关的显示控件并通知外部监听器日期时间已改变。
private NumberPicker.OnValueChangeListener mOnDateChangedListener = new NumberPicker.OnValueChangeListener() {
@Override
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
// 根据新选择的日期与旧日期的差值调整内部的Calendar对象mDate的日期信息实现日期的变更。
mDate.add(Calendar.DAY_OF_YEAR, newVal - oldVal);
// 更新日期显示相关的控件,确保界面上显示的日期信息是最新的。
updateDateControl();
// 调用onDateTimeChanged方法通知外部监听器日期时间已发生改变触发相应的回调逻辑如果有设置监听器的话
onDateTimeChanged();
}
};
// 内部类实现了NumberPicker.OnValueChangeListener接口用于监听小时选择器mHourSpinner的值变化事件。
// 当用户在小时选择器上选择了不同的小时值时会触发该监听器的onValueChange方法根据12/24小时制等不同情况进行日期、时间以及相关显示控件的更新并通知外部监听器日期时间已改变。
private NumberPicker.OnValueChangeListener mOnHourChangedListener = new NumberPicker.OnValueChangeListener() {
@Override
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
boolean isDateChanged = false;
Calendar cal = Calendar.getInstance();
if (!mIs24HourView) {
// 在12小时制下如果当前是下午!mIsAm且从11点HOURS_IN_HALF_DAY - 1切换到12点HOURS_IN_HALF_DAY则日期需要加一天标记日期已改变。
if (!mIsAm && oldVal == HOURS_IN_HALF_DAY - 1 && newVal == HOURS_IN_HALF_DAY) {
cal.setTimeInMillis(mDate.getTimeInMillis());
cal.add(Calendar.DAY_OF_YEAR, 1);
isDateChanged = true;
}
// 在12小时制下如果当前是上午mIsAm且从12点HOURS_IN_HALF_DAY切换到11点HOURS_IN_HALF_DAY - 1则日期需要减一天标记日期已改变。
else if (mIsAm && oldVal == HOURS_IN_HALF_DAY && newVal == HOURS_IN_HALF_DAY - 1) {
cal.setTimeInMillis(mDate.getTimeInMillis());
cal.add(Calendar.DAY_OF_YEAR, -1);
isDateChanged = true;
}
// 如果是从11点切换到12点或者从12点切换到11点还需要切换上午/下午AM/PM的标记并更新对应的显示控件。
if (oldVal == HOURS_IN_HALF_DAY - 1 && newVal == HOURS_IN_HALF_DAY ||
oldVal == HOURS_IN_HALF_DAY && newVal == HOURS_IN_HALF_DAY - 1) {
mIsAm =!mIsAm;
updateAmPmControl();
}
} else {
// 在24小时制下如果从23点HOURS_IN_ALL_DAY - 1切换到0点则日期需要加一天标记日期已改变。
if (oldVal == HOURS_IN_ALL_DAY - 1 && newVal == 0) {
cal.setTimeInMillis(mDate.getTimeInMillis());
cal.add(Calendar.DAY_OF_YEAR, 1);
isDateChanged = true;
}
// 在24小时制下如果从0点切换到23点则日期需要减一天标记日期已改变。
else if (oldVal == 0 && newVal == HOURS_IN_ALL_DAY - 1) {
cal.setTimeInMillis(mDate.getTimeInMillis());
cal.add(Calendar.DAY_OF_YEAR, -1);
isDateChanged = true;
}
}
// 根据当前选择的小时值以及上午/下午标记在12小时制下计算出用于设置到内部Calendar对象mDate的小时数并进行设置。
int newHour = mHourSpinner.getValue() % HOURS_IN_HALF_DAY + (mIsAm? 0 : HOURS_IN_HALF_DAY);
mDate.set(Calendar.HOUR_OF_DAY, newHour);
// 通知外部监听器日期时间已发生改变,触发相应的回调逻辑(如果有设置监听器的话)。
onDateTimeChanged();
// 如果日期发生了改变更新当前的年、月、日信息到组件内部的Calendar对象mDate确保整体日期时间信息的一致性。
if (isDateChanged) {
setCurrentYear(cal.get(Calendar.YEAR));
setCurrentMonth(cal.get(Calendar.MONTH));
setCurrentDay(cal.get(Calendar.DAY_OF_MONTH));
}
}
};
// 内部类实现了NumberPicker.OnValueChangeListener接口用于监听分钟选择器mMinuteSpinner的值变化事件。
// 当用户在分钟选择器上选择了不同的分钟值时会触发该监听器的onValueChange方法根据分钟值的变化情况进行日期、时间以及相关显示控件的更新并通知外部监听器日期时间已改变。
private NumberPicker.OnValueChangeListener mOnMinuteChangedListener = new NumberPicker.OnValueChangeListener() {
@Override
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
int minValue = mMinuteSpinner.getMinValue();
int maxValue = mMinuteSpinner.getMaxValue();
int offset = 0;
// 如果从最大分钟值59切换到最小分钟值0则小时数需要加1表示时间向后推移了一小时。
if (oldVal == maxValue && newVal == minValue) {
offset += 1;
}
// 如果从最小分钟值0切换到最大分钟值59则小时数需要减1表示时间向前推移了一小时。
else if (oldVal == minValue && newVal == maxValue) {
offset -= 1;
}
if (offset!= 0) {
// 根据分钟值变化导致的小时数偏移调整内部的Calendar对象mDate的小时信息实现时间的变更。
mDate.add(Calendar.HOUR_OF_DAY, offset);
// 更新小时选择器的值,使其显示与内部时间一致的小时数。
mHourSpinner.setValue(getCurrentHour());
// 更新日期显示相关的控件,确保界面上显示的日期信息是最新的,因为时间变更可能跨越了一天的边界。
updateDateControl();
int newHour = getCurrentHourOfDay();
// 根据更新后的小时数,判断是否处于上午或下午,更新上午/下午的标记,并相应地更新显示控件。
if (newHour >= HOURS_IN_HALF_DAY) {
mIsAm = false;
updateAmPmControl();
} else {
mIsAm = true;
updateAmPmControl();
}
}
// 设置内部的Calendar对象mDate的分钟信息为用户选择的新分钟值。
mDate.set(Calendar.MINUTE, newVal);
// 通知外部监听器日期时间已发生改变,触发相应的回调逻辑(如果有设置监听器的话)。
onDateTimeChanged();
}
};
// 内部类实现了NumberPicker.OnValueChangeListener接口用于监听上午/下午AM/PM选择器mAmPmSpinner的值变化事件。
// 当用户在上午/下午选择器上切换了选项时会触发该监听器的onValueChange方法相应地调整内部的日期时间信息以及更新相关显示控件并通知外部监听器日期时间已改变。
private NumberPicker.OnValueChangeListener mOnAmPmChangedListener = new NumberPicker.OnValueChangeListener() {
@Override
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
mIsAm =!mIsAm;
// 如果切换到上午AM则将小时数减去12小时从下午时间转换到上午时间对应的小时调整
if (mIsAm) {
mDate.add(Calendar.HOUR_OF_DAY, -HOURS_IN_HALF_DAY);
}
// 如果切换到下午PM则将小时数加上12小时从上午时间转换到下午时间对应的小时调整
else {
mDate.add(Calendar.HOUR_OF_DAY, HOURS_IN_HALF_DAY);
}
// 更新上午/下午显示相关的控件,确保界面上显示的信息与内部状态一致。
updateAmPmControl();
// 通知外部监听器日期时间已发生改变,触发相应的回调逻辑(如果有设置监听器的话)。
onDateTimeChanged();
}
};
// 定义一个接口用于外部类实现以便在DateTimePicker的日期时间发生改变时接收到通知并进行相应的处理。
public interface OnDateTimeChangedListener {
void onDateTimeChanged(DateTimePicker view, int year, int month,
int dayOfMonth, int hourOfDay, int minute);
}
// 构造函数创建一个DateTimePicker实例使用当前系统时间作为初始时间进行初始化。
public DateTimePicker(Context context) {
this(context, System.currentTimeMillis());
}
// 构造函数创建一个DateTimePicker实例并使用指定的日期时间以毫秒为单位的时间戳作为初始时间进行初始化。
public DateTimePicker(Context context, long date) {
this(context, date, DateFormat.is24HourFormat(context));
}
// 构造函数创建一个DateTimePicker实例使用指定的日期时间以毫秒为单位的时间戳和是否为24小时制作为参数进行初始化这是最完整的初始化构造函数。
public DateTimePicker(Context context, long date, boolean is24HourView) {
super(context);
// 获取一个Calendar实例用于存储和操作日期时间信息初始化为当前时间如果没有传入特定日期时间的话
mDate = Calendar.getInstance();
mInitialising = true;
// 根据当前小时数判断是否处于上午AM用于初始化上午/下午标记如果当前小时大于等于12则为下午即!mIsAm
mIsAm = getCurrentHourOfDay() >= HOURS