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-Notes/DateTimePicker.java

600 lines
22 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.
*/
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; // 导入数字选择器类
public class DateTimePicker extends FrameLayout {
// 定义DateTimePicker类继承自FrameLayout
private static final boolean DEFAULT_ENABLE_STATE = true; // 默认启用状态
private static final int HOURS_IN_HALF_DAY = 12; // 半天的小时数
private static final int HOURS_IN_ALL_DAY = 24; // 全天的小时数
private static final int DAYS_IN_ALL_WEEK = 7; // 一周的天数
private static final int DATE_SPINNER_MIN_VAL = 0; // 日期选择器的最小值
private static final int DATE_SPINNER_MAX_VAL = DAYS_IN_ALL_WEEK - 1;
// 日期选择器的最大值
private static final int HOUR_SPINNER_MIN_VAL_24_HOUR_VIEW = 0;
// 24小时制小时选择器的最小值
private static final int HOUR_SPINNER_MAX_VAL_24_HOUR_VIEW = 23;
// 24小时制小时选择器的最大值
private static final int HOUR_SPINNER_MIN_VAL_12_HOUR_VIEW = 1;
// 12小时制小时选择器的最小值
private static final int HOUR_SPINNER_MAX_VAL_12_HOUR_VIEW = 12;
// 12小时制小时选择器的最大值
private static final int MINUT_SPINNER_MIN_VAL = 0; // 分钟选择器的最小值
private static final int MINUT_SPINNER_MAX_VAL = 59; // 分钟选择器的最大值
private static final int AMPM_SPINNER_MIN_VAL = 0; // 上午/下午选择器的最小值
private static final int AMPM_SPINNER_MAX_VAL = 1; // 上午/下午选择器的最大值
private final NumberPicker mDateSpinner; // 日期选择器
private final NumberPicker mHourSpinner; // 小时选择器
private final NumberPicker mMinuteSpinner; // 分钟选择器
private final NumberPicker mAmPmSpinner; // 上午/下午选择器
private Calendar mDate; // 日期对象
private String[] mDateDisplayValues = new String[DAYS_IN_ALL_WEEK];
// 存储日期显示值的数组
private boolean mIsAm;
// 标识是否为上午
private boolean mIs24HourView;
// 标识是否为24小时制视图
private boolean mIsEnabled = DEFAULT_ENABLE_STATE;
// 控件是否启用
private boolean mInitialising;
// 控件是否正在初始化
private OnDateTimeChangedListener mOnDateTimeChangedListener;
// 日期时间改变监听器
// 日期变化监听器
private NumberPicker.OnValueChangeListener mOnDateChangedListener = new NumberPicker.OnValueChangeListener() {
@Override
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
mDate.add(Calendar.DAY_OF_YEAR, newVal - oldVal); // 更新日期
updateDateControl(); // 更新日期控制
onDateTimeChanged(); // 通知日期时间已改变
}
};
private NumberPicker.OnValueChangeListener mOnHourChangedListener = new NumberPicker.OnValueChangeListener() {
@Override
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
boolean isDateChanged = false; // 标识日期是否改变
Calendar cal = Calendar.getInstance(); // 获取当前日历实例
// 如果不是24小时制
if (!mIs24HourView) {
// 如果从 PM 切换到 AM日期需要加一天
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; // 日期改变标识为真
}
// 如果从 AM 切换到 PM日期需要减一天
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; // 日期改变标识为真
}
// 切换 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(); // 更新 AM/PM 控件
}
} else {
// 如果在24小时制下处理日期变化
if (oldVal == HOURS_IN_ALL_DAY - 1 && newVal == 0) {
cal.setTimeInMillis(mDate.getTimeInMillis());
cal.add(Calendar.DAY_OF_YEAR, 1);
isDateChanged = true; // 日期改变标识为真
} else if (oldVal == 0 && newVal == HOURS_IN_ALL_DAY - 1) {
cal.setTimeInMillis(mDate.getTimeInMillis());
cal.add(Calendar.DAY_OF_YEAR, -1);
isDateChanged = true; // 日期改变标识为真
}
}
// 设置新小时
int newHour = mHourSpinner.getValue() % HOURS_IN_HALF_DAY + (mIsAm ? 0 : HOURS_IN_HALF_DAY);
mDate.set(Calendar.HOUR_OF_DAY, newHour); // 更新日期对象中的小时
onDateTimeChanged(); // 通知日期时间已改变
// 如果日期发生了变化,更新日期控件
if (isDateChanged) {
setCurrentYear(cal.get(Calendar.YEAR));
setCurrentMonth(cal.get(Calendar.MONTH));
setCurrentDay(cal.get(Calendar.DAY_OF_MONTH));
}
}
};
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; // 偏移量初始化为0
// 判断是否从最大值切换到最小值
if (oldVal == maxValue && newVal == minValue) {
offset += 1; // 增加偏移量
}
// 判断是否从最小值切换到最大值
else if (oldVal == minValue && newVal == maxValue) {
offset -= 1; // 减少偏移量
}
// 如果有偏移量,更新小时
if (offset != 0) {
mDate.add(Calendar.HOUR_OF_DAY, offset); // 更新日期对象中的小时
mHourSpinner.setValue(getCurrentHour()); // 更新小时选择器的值
updateDateControl(); // 更新日期控件
// 根据当前小时判断 AM/PM 状态
int newHour = getCurrentHourOfDay();
if (newHour >= HOURS_IN_HALF_DAY) {
mIsAm = false; // 设置为下午
updateAmPmControl(); // 更新 AM/PM 控件
} else {
mIsAm = true; // 设置为上午
updateAmPmControl(); // 更新 AM/PM 控件
}
}
mDate.set(Calendar.MINUTE, newVal); // 更新日期对象中的分钟
onDateTimeChanged(); // 通知日期时间已改变
}
};
private NumberPicker.OnValueChangeListener mOnAmPmChangedListener = new NumberPicker.OnValueChangeListener() {
@Override
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
mIsAm = !mIsAm; // 切换 AM/PM 状态
// 根据 AM/PM 状态调整小时
if (mIsAm) {
mDate.add(Calendar.HOUR_OF_DAY, -HOURS_IN_HALF_DAY);
// 设置为上午减去12小时
} else {
mDate.add(Calendar.HOUR_OF_DAY, HOURS_IN_HALF_DAY);
// 设置为下午加上12小时
}
updateAmPmControl(); // 更新 AM/PM 控件
onDateTimeChanged(); // 通知日期时间已改变
}
};
// 定义一个接口,用于日期时间变化的监听
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());
}
// 带有日期参数的构造函数
public DateTimePicker(Context context, long date) {
// 调用带有日期和24小时制标志的构造函数
this(context, date, DateFormat.is24HourFormat(context));
}
// 带有日期和24小时制标志的构造函数
public DateTimePicker(Context context, long date, boolean is24HourView) {
super(context); // 调用父类构造函数
mDate = Calendar.getInstance(); // 获取当前日历实例
mInitialising = true; // 初始化标志
mIsAm = getCurrentHourOfDay() >= HOURS_IN_HALF_DAY; // 判断当前是否为上午
inflate(context, R.layout.datetime_picker, this); // 加载自定义布局
// 日期选择器的初始化
mDateSpinner = (NumberPicker) findViewById(R.id.date);
mDateSpinner.setMinValue(DATE_SPINNER_MIN_VAL); // 设置最小值
mDateSpinner.setMaxValue(DATE_SPINNER_MAX_VAL); // 设置最大值
mDateSpinner.setOnValueChangedListener(mOnDateChangedListener); // 设置日期变化监听器
// 小时选择器的初始化
mHourSpinner = (NumberPicker) findViewById(R.id.hour);
mHourSpinner.setOnValueChangedListener(mOnHourChangedListener); // 设置小时变化监听器
// 分钟选择器的初始化
mMinuteSpinner = (NumberPicker) findViewById(R.id.minute);
mMinuteSpinner.setMinValue(MINUT_SPINNER_MIN_VAL); // 设置最小值
mMinuteSpinner.setMaxValue(MINUT_SPINNER_MAX_VAL); // 设置最大值
mMinuteSpinner.setOnLongPressUpdateInterval(100); // 长按更新间隔
mMinuteSpinner.setOnValueChangedListener(mOnMinuteChangedListener); // 设置分钟变化监听器
// AM/PM 选择器的初始化
String[] stringsForAmPm = new DateFormatSymbols().getAmPmStrings(); // 获取 AM/PM 字符串
mAmPmSpinner = (NumberPicker) findViewById(R.id.amPm);
mAmPmSpinner.setMinValue(AMPM_SPINNER_MIN_VAL); // 设置最小值
mAmPmSpinner.setMaxValue(AMPM_SPINNER_MAX_VAL); // 设置最大值
mAmPmSpinner.setDisplayedValues(stringsForAmPm); // 显示 AM/PM 值
mAmPmSpinner.setOnValueChangedListener(mOnAmPmChangedListener); // 设置 AM/PM 变化监听器
}
// update controls to initial state
updateDateControl(); // 更新日期控件
updateHourControl(); // 更新小时控件
updateAmPmControl(); // 更新 AM/PM 控件
set24HourView(is24HourView); // 设置为24小时制或12小时制
// set to current time
setCurrentDate(date);// 根据传入的日期设置当前日期
setEnabled(isEnabled());// 根据是否启用状态设置控件
// set the content descriptions
mInitialising = false;// 初始化完成标志设为false
}
@Override
public void setEnabled(boolean enabled) {
if (mIsEnabled == enabled) { // 如果当前状态与目标状态相同,则不做任何操作
return;
}
super.setEnabled(enabled); // 调用父类的设置启用状态方法
mDateSpinner.setEnabled(enabled); // 设置日期选择器的启用状态
mMinuteSpinner.setEnabled(enabled); // 设置分钟选择器的启用状态
mHourSpinner.setEnabled(enabled); // 设置小时选择器的启用状态
mAmPmSpinner.setEnabled(enabled); // 设置 AM/PM 选择器的启用状态
mIsEnabled = enabled; // 更新当前启用状态
}
@Override
public boolean isEnabled() {
return mIsEnabled;// 返回当前启用状态
}
/**
* Get the current date in millis
*
* @return the current date in millis
*/
public long getCurrentDateInTimeMillis() {
return mDate.getTimeInMillis();// 返回日期对象的毫秒值
}
/**
* Set the current date
*
* @param date The current date in millis
*/
public void setCurrentDate(long date) {
Calendar cal = Calendar.getInstance(); // 获取当前日历实例
cal.setTimeInMillis(date); // 根据传入的毫秒值设置时间
// 设置当前日期和时间
setCurrentDate(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH),cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE));
}
/**
* Set the current date
*
* @param year The current year
* @param month The current month
* @param dayOfMonth The current dayOfMonth
* @param hourOfDay The current hourOfDay
* @param minute The current minute
*/
public void setCurrentDate(int year, int month,int dayOfMonth, int hourOfDay, int minute) {
// 设置当前年份
setCurrentYear(year);
// 设置当前月份
setCurrentMonth(month);
// 设置当前日期
setCurrentDay(dayOfMonth);
// 设置当前小时
setCurrentHour(hourOfDay);
// 设置当前分钟
setCurrentMinute(minute);
}
/**
* Get current year
*
* @return The current year
*/
public int getCurrentYear() { // 返回当前日期对象中的年份
return mDate.get(Calendar.YEAR);
}
/**
* Set current year
*
* @param year The current year
*/
public void setCurrentYear(int year) {
// 如果不是初始化状态并且输入的年份与当前年份相同,则不做任何操作
if (!mInitialising && year == getCurrentYear()) {
return;
}
// 设置当前年份
mDate.set(Calendar.YEAR, year);
// 更新日期控件
updateDateControl();
// 通知日期和时间已改变
onDateTimeChanged();
}
/**
* Get current month in the year
*
* @return The current month in the year
*/
public int getCurrentMonth() {
return mDate.get(Calendar.MONTH);
}
/**
* Set current month in the year
*
* @param month The month in the year
*/
public void setCurrentMonth(int month) {
// 如果不是初始化状态并且输入的月份与当前月份相同,则不做任何操作
if (!mInitialising && month == getCurrentMonth()) {
return;
}
// 设置当前月份
mDate.set(Calendar.MONTH, month);
// 更新日期控件
updateDateControl();
// 通知日期和时间已改变
onDateTimeChanged();
}
/**
* Get current day of the month
*
* @return The day of the month
*/
public int getCurrentDay() {
return mDate.get(Calendar.DAY_OF_MONTH);
}
/**
* Set current day of the month
*
* @param dayOfMonth The day of the month
*/
public void setCurrentDay(int dayOfMonth) {
// 如果不是初始化状态,并且输入的日期与当前日期相同,则不做任何操作
if (!mInitialising && dayOfMonth == getCurrentDay()) {
return;
}
// 设置当前日期
mDate.set(Calendar.DAY_OF_MONTH, dayOfMonth);
// 更新日期控件
updateDateControl();
// 通知日期和时间已改变
onDateTimeChanged();
}
/**
* Get current hour in 24 hour mode, in the range (0~23)
* @return The current hour in 24 hour mode
*/
public int getCurrentHourOfDay() {
return mDate.get(Calendar.HOUR_OF_DAY);
}
private int getCurrentHour() {
if (mIs24HourView) {
// 如果是24小时制直接返回当前小时
return getCurrentHourOfDay();
} else {
// 如果是12小时制
int hour = getCurrentHourOfDay();
if (hour > HOURS_IN_HALF_DAY) {
// 如果小时大于12则转换为12小时制
return hour - HOURS_IN_HALF_DAY;
} else {
// 如果小时为0返回12否则返回当前小时
return hour == 0 ? HOURS_IN_HALF_DAY : hour;
}
}
}
/**
* Set current hour in 24 hour mode, in the range (0~23)
*
* @param hourOfDay
*/
public void setCurrentHour(int hourOfDay) {
// 如果不是初始化状态,并且输入的小时与当前小时相同,则不做任何操作
if (!mInitialising && hourOfDay == getCurrentHourOfDay()) {
return;
}
// 设置当前小时
mDate.set(Calendar.HOUR_OF_DAY, hourOfDay);
// 如果不是24小时制处理AM/PM的显示
if (!mIs24HourView) {
if (hourOfDay >= HOURS_IN_HALF_DAY) {
// 设置为PM
mIsAm = false;
if (hourOfDay > HOURS_IN_HALF_DAY) {
// 转换为12小时制
hourOfDay -= HOURS_IN_HALF_DAY;
}
} else {
// 设置为AM
mIsAm = true;
if (hourOfDay == 0) {
// 0点转换为12点
hourOfDay = HOURS_IN_HALF_DAY;
}
}
// 更新AM/PM控件
updateAmPmControl();
}
// 更新小时选择器的值
mHourSpinner.setValue(hourOfDay);
// 通知时间已改变
onDateTimeChanged();
}
/**
* Get currentMinute
*
* @return The Current Minute
*/
public int getCurrentMinute() {
return mDate.get(Calendar.MINUTE);
}
/**
* Set current minute
*/
public void setCurrentMinute(int minute) {
// 如果不是初始化状态,并且输入的分钟与当前分钟相同,则不做任何操作
if (!mInitialising && minute == getCurrentMinute()) {
return;
}
// 更新分钟选择器的值
mMinuteSpinner.setValue(minute);
// 设置当前分钟
mDate.set(Calendar.MINUTE, minute);
// 通知时间已改变
onDateTimeChanged();
}
/**
* @return true if this is in 24 hour view else false.
*/
public boolean is24HourView () {
return mIs24HourView;
}
/**
* Set whether in 24 hour or AM/PM mode.
*
* @param is24HourView True for 24 hour mode. False for AM/PM mode.
*/
public void set24HourView(boolean is24HourView) {
// 如果当前状态与传入参数相同,则不做任何操作
if (mIs24HourView == is24HourView) {
return;
}
// 更新24小时制标志
mIs24HourView = is24HourView;
// 根据24小时制的选择设置AM/PM选择器的可见性
mAmPmSpinner.setVisibility(is24HourView ? View.GONE : View.VISIBLE);
// 获取当前小时,并更新控件
int hour = getCurrentHourOfDay();
updateHourControl(); // 更新小时选择器的设置
setCurrentHour(hour); // 设置当前小时
updateAmPmControl(); // 更新AM/PM控件的显示
}
/**
* 更新日期选择器的显示值
*/
private void updateDateControl() {
Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(mDate.getTimeInMillis());
// 将日期设置为当前日期前一半周的开始
cal.add(Calendar.DAY_OF_YEAR, -DAYS_IN_ALL_WEEK / 2 - 1);
mDateSpinner.setDisplayedValues(null);// 清空旧的显示值
for (int i = 0; i < DAYS_IN_ALL_WEEK; ++i) {
cal.add(Calendar.DAY_OF_YEAR, 1); // 增加一天、
// 格式化日期,存入显示数组
mDateDisplayValues[i] = (String) DateFormat.format("MM.dd EEEE", cal);
}
// 设置新的显示值
mDateSpinner.setDisplayedValues(mDateDisplayValues);
mDateSpinner.setValue(DAYS_IN_ALL_WEEK / 2); // 设置初始显示值为中间日期
mDateSpinner.invalidate();// 刷新日期选择器
}
/**
* 更新AM/PM控制
*/
private void updateAmPmControl() {
// 如果是24小时制则隐藏AM/PM选择器
if (mIs24HourView) {
mAmPmSpinner.setVisibility(View.GONE);
} else {
// 否则根据当前时间选择AM或PM
int index = mIsAm ? Calendar.AM : Calendar.PM;
mAmPmSpinner.setValue(index);// 设置AM/PM选择器的值
mAmPmSpinner.setVisibility(View.VISIBLE);// 显示选择器
}
}
/**
* 更新小时控制
*/
private void updateHourControl() {
// 根据是否为24小时制更新小时选择器的最小和最大值
if (mIs24HourView) {
mHourSpinner.setMinValue(HOUR_SPINNER_MIN_VAL_24_HOUR_VIEW);
// 设置24小时制的最小值
mHourSpinner.setMaxValue(HOUR_SPINNER_MAX_VAL_24_HOUR_VIEW);
// 设置24小时制的最大值
} else {
mHourSpinner.setMinValue(HOUR_SPINNER_MIN_VAL_12_HOUR_VIEW);
// 设置12小时制的最小值
mHourSpinner.setMaxValue(HOUR_SPINNER_MAX_VAL_12_HOUR_VIEW);
// 设置12小时制的最大值
}
}
/**
* Set the callback that indicates the 'Set' button has been pressed.
* @param callback the callback, if null will do nothing
*/
// 设置日期时间变化监听器
public void setOnDateTimeChangedListener(OnDateTimeChangedListener callback) {
mOnDateTimeChangedListener = callback;// 将回调对象赋值给成员变量
}
// 当日期时间发生变化时调用
private void onDateTimeChanged() {
// 检查监听器是否不为空
if (mOnDateTimeChangedListener != null) {
// 调用监听器的方法,并传递当前的年、月、日、小时和分钟
mOnDateTimeChangedListener.onDateTimeChanged(this, getCurrentYear(),
getCurrentMonth(), getCurrentDay(), getCurrentHourOfDay(), getCurrentMinute());
}
// 当前年份、 当前月份、当前日期、当前小时、当前分钟
}