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.
qw1-notes/java/net/micode/notes/ui/DateTimePicker.java

691 lines
24 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;
// 导入Android上下文类用于获取应用程序的环境信息
import android.content.Context;
// 导入Android用于处理日期格式的类
import android.text.format.DateFormat;
// 导入Android视图类是所有视图组件的基类
import android.view.View;
// 导入Android帧布局类用于将子视图堆叠在左上角
import android.widget.FrameLayout;
// 导入Android数字选择器类用于选择数字
import android.widget.NumberPicker;
/**
* 自定义的日期时间选择器类继承自FrameLayout
* 该类用于创建一个包含日期、小时、分钟和上午/下午选择的选择器
*/
public class DateTimePicker extends 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;
// 24小时制下小时选择器的最小值
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;
// 12小时制下小时选择器的最小值
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;
// 分钟选择器的最小值
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;
// 标记是否为24小时制显示
private boolean mIs24HourView;
// 标记选择器是否启用
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) {
// 处理从晚上11点到中午12点的情况
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点到晚上11点的情况
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点的情况切换上午/下午状态
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();
}
}
// 如果是24小时制
else {
// 处理从晚上23点到凌晨0点的情况
if (oldVal == HOURS_IN_ALL_DAY - 1 && newVal == 0) {
cal.setTimeInMillis(mDate.getTimeInMillis());
cal.add(Calendar.DAY_OF_YEAR, 1);
isDateChanged = true;
}
// 处理从凌晨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;
}
}
// 计算新的小时值
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;
// 处理从59分钟到0分钟的情况
if (oldVal == maxValue && newVal == minValue) {
offset += 1;
}
// 处理从0分钟到59分钟的情况
else if (oldVal == minValue && newVal == maxValue) {
offset -= 1;
}
// 如果有偏移量
if (offset != 0) {
// 更新日历的小时值
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();
}
}
// 更新日历的分钟值
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;
// 根据上午/下午状态更新日历的小时值
if (mIsAm) {
mDate.add(Calendar.HOUR_OF_DAY, -HOURS_IN_HALF_DAY);
} else {
mDate.add(Calendar.HOUR_OF_DAY, HOURS_IN_HALF_DAY);
}
// 更新上午/下午选择器的显示
updateAmPmControl();
// 触发日期时间改变事件
onDateTimeChanged();
}
};
/**
* 日期时间改变监听器接口,用于在日期时间改变时回调
*/
public interface OnDateTimeChangedListener {
/**
* 当日期时间改变时调用
* @param view 日期时间选择器视图
* @param year 年份
* @param month 月份
* @param dayOfMonth 日期
* @param hourOfDay 小时
* @param minute 分钟
*/
void onDateTimeChanged(DateTimePicker view, int year, int month,
int dayOfMonth, int hourOfDay, int minute);
}
/**
* 构造函数,使用当前时间初始化
* @param context 上下文对象
*/
public DateTimePicker(Context context) {
// 调用另一个构造函数,使用当前时间戳
this(context, System.currentTimeMillis());
}
/**
* 构造函数,使用指定的时间戳初始化
* @param context 上下文对象
* @param date 时间戳
*/
public DateTimePicker(Context context, long date) {
// 调用另一个构造函数同时判断是否为24小时制
this(context, date, DateFormat.is24HourFormat(context));
}
/**
* 构造函数使用指定的时间戳和是否为24小时制初始化
* @param context 上下文对象
* @param date 时间戳
* @param is24HourView 是否为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);
// 获取上午/下午的显示字符串数组
String[] stringsForAmPm = new DateFormatSymbols().getAmPmStrings();
// 初始化上午/下午选择器
mAmPmSpinner = (NumberPicker) findViewById(R.id.amPm);
mAmPmSpinner.setMinValue(AMPM_SPINNER_MIN_VAL);
mAmPmSpinner.setMaxValue(AMPM_SPINNER_MAX_VAL);
mAmPmSpinner.setDisplayedValues(stringsForAmPm);
mAmPmSpinner.setOnValueChangedListener(mOnAmPmChangedListener);
// 更新日期选择器的显示
updateDateControl();
// 更新小时选择器的显示
updateHourControl();
// 更新上午/下午选择器的显示
updateAmPmControl();
// 设置是否为24小时制
set24HourView(is24HourView);
// 设置当前日期和时间
setCurrentDate(date);
// 设置选择器的启用状态
setEnabled(isEnabled());
// 设置内容描述
mInitialising = false;
}
/**
* 设置选择器的启用状态
* @param enabled 是否启用
*/
@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);
// 更新当前的启用状态
mIsEnabled = enabled;
}
/**
* 获取选择器的启用状态
* @return 是否启用
*/
@Override
public boolean isEnabled() {
return mIsEnabled;
}
/**
* 获取当前选择的日期和时间的时间戳
* @return 时间戳
*/
public long getCurrentDateInTimeMillis() {
return mDate.getTimeInMillis();
}
/**
* 设置当前选择的日期和时间
* @param date 时间戳
*/
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));
}
/**
* 设置当前选择的日期和时间
* @param year 年份
* @param month 月份
* @param dayOfMonth 日期
* @param hourOfDay 小时
* @param minute 分钟
*/
public void setCurrentDate(int year, int month,
int dayOfMonth, int hourOfDay, int minute) {
// 设置年份
setCurrentYear(year);
// 设置月份
setCurrentMonth(month);
// 设置日期
setCurrentDay(dayOfMonth);
// 设置小时
setCurrentHour(hourOfDay);
// 设置分钟
setCurrentMinute(minute);
}
/**
* 获取当前选择的年份
* @return 年份
*/
public int getCurrentYear() {
return mDate.get(Calendar.YEAR);
}
/**
* 设置当前选择的年份
* @param year 年份
*/
public void setCurrentYear(int year) {
// 如果不是初始化状态且年份没有改变,直接返回
if (!mInitialising && year == getCurrentYear()) {
return;
}
// 设置日历对象的年份
mDate.set(Calendar.YEAR, year);
// 更新日期选择器的显示
updateDateControl();
// 触发日期时间改变事件
onDateTimeChanged();
}
/**
* 获取当前选择的月份
* @return 月份
*/
public int getCurrentMonth() {
return mDate.get(Calendar.MONTH);
}
/**
* 设置当前选择的月份
* @param month 月份
*/
public void setCurrentMonth(int month) {
// 如果不是初始化状态且月份没有改变,直接返回
if (!mInitialising && month == getCurrentMonth()) {
return;
}
// 设置日历对象的月份
mDate.set(Calendar.MONTH, month);
// 更新日期选择器的显示
updateDateControl();
// 触发日期时间改变事件
onDateTimeChanged();
}
/**
* 获取当前选择的日期
* @return 日期
*/
public int getCurrentDay() {
return mDate.get(Calendar.DAY_OF_MONTH);
}
/**
* 设置当前选择的日期
* @param dayOfMonth 日期
*/
public void setCurrentDay(int dayOfMonth) {
// 如果不是初始化状态且日期没有改变,直接返回
if (!mInitialising && dayOfMonth == getCurrentDay()) {
return;
}
// 设置日历对象的日期
mDate.set(Calendar.DAY_OF_MONTH, dayOfMonth);
// 更新日期选择器的显示
updateDateControl();
// 触发日期时间改变事件
onDateTimeChanged();
}
/**
* 获取当前选择的24小时制小时值
* @return 24小时制小时值
*/
public int getCurrentHourOfDay() {
return mDate.get(Calendar.HOUR_OF_DAY);
}
/**
* 获取当前选择的小时值根据是否为24小时制
* @return 小时值
*/
private int getCurrentHour() {
// 如果是24小时制
if (mIs24HourView){
return getCurrentHourOfDay();
}
// 如果是12小时制
else {
int hour = getCurrentHourOfDay();
if (hour > HOURS_IN_HALF_DAY) {
return hour - HOURS_IN_HALF_DAY;
} else {
return hour == 0 ? HOURS_IN_HALF_DAY : hour;
}
}
}
/**
* 设置当前选择的24小时制小时值
* @param hourOfDay 24小时制小时值
*/
public void setCurrentHour(int hourOfDay) {
// 如果不是初始化状态且小时值没有改变,直接返回
if (!mInitialising && hourOfDay == getCurrentHourOfDay()) {
return;
}
// 设置日历对象的小时值
mDate.set(Calendar.HOUR_OF_DAY, hourOfDay);
// 如果不是24小时制
if (!mIs24HourView) {
// 根据小时值判断是否为上午
if (hourOfDay >= HOURS_IN_HALF_DAY) {
mIsAm = false;
if (hourOfDay > HOURS_IN_HALF_DAY) {
hourOfDay -= HOURS_IN_HALF_DAY;
}
} else {
mIsAm = true;
if (hourOfDay == 0) {
hourOfDay = HOURS_IN_HALF_DAY;
}
}
// 更新上午/下午选择器的显示
updateAmPmControl();
}
// 设置小时选择器的值
mHourSpinner.setValue(hourOfDay);
// 触发日期时间改变事件
onDateTimeChanged();
}
/**
* 获取当前选择的分钟值
* @return 分钟值
*/
public int getCurrentMinute() {
return mDate.get(Calendar.MINUTE);
}
/**
* 设置当前选择的分钟值
* @param minute 分钟值
*/
public void setCurrentMinute(int minute) {
// 如果不是初始化状态且分钟值没有改变,直接返回
if (!mInitialising && minute == getCurrentMinute()) {
return;
}
// 设置分钟选择器的值
mMinuteSpinner.setValue(minute);
// 设置日历对象的分钟值
mDate.set(Calendar.MINUTE, minute);
// 触发日期时间改变事件
onDateTimeChanged();
}
/**
* 判断是否为24小时制显示
* @return 是否为24小时制
*/
public boolean is24HourView () {
return mIs24HourView;
}
/**
* 设置是否为24小时制显示
* @param is24HourView 是否为24小时制
*/
public void set24HourView(boolean is24HourView) {
// 如果当前状态和要设置的状态相同,直接返回
if (mIs24HourView == is24HourView) {
return;
}
// 更新是否为24小时制的状态
mIs24HourView = is24HourView;
// 根据是否为24小时制设置上午/下午选择器的可见性
mAmPmSpinner.setVisibility(is24HourView ? View.GONE : View.VISIBLE);
// 获取当前的小时值
int hour = getCurrentHourOfDay();
// 更新小时选择器的显示
updateHourControl();
// 设置当前的小时值
setCurrentHour(hour);
// 更新上午/下午选择器的显示
updateAmPmControl();
}
/**
* 更新日期选择器的显示
*/
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();
}
/**
* 更新上午/下午选择器的显示
*/
private void updateAmPmControl() {
// 如果是24小时制隐藏上午/下午选择器
if (mIs24HourView) {
mAmPmSpinner.setVisibility(View.GONE);
}
// 如果不是24小时制
else {
// 根据是否为上午设置选择器的值
int index = mIsAm ? Calendar.AM : Calendar.PM;
mAmPmSpinner.setValue(index);
// 显示上午/下午选择器
mAmPmSpinner.setVisibility(View.VISIBLE);
}
}
/**
* 更新小时选择器的显示
*/
private void updateHourControl() {
// 如果是24小时制
if (mIs24HourView) {
// 设置小时选择器的最小值和最大值为24小时制的范围
mHourSpinner.setMinValue(HOUR_SPINNER_MIN_VAL_24_HOUR_VIEW);
mHourSpinner.setMaxValue(HOUR_SPINNER_MAX_VAL_24_HOUR_VIEW);
}
// 如果不是24小时制
else {
// 设置小时选择器的最小值和最大值为12小时制的范围
mHourSpinner.setMinValue(HOUR_SPINNER_MIN_VAL_12_HOUR_VIEW);
mHourSpinner.setMaxValue(HOUR_SPINNER_MAX_VAL_12_HOUR_VIEW);
}
}
/**
* 设置日期时间改变监听器
* @param callback 监听器回调
*/
public void setOnDateTimeChangedListener(OnDateTimeChangedListener callback) {
mOnDateTimeChangedListener = callback;
}
/**
* 触发日期时间改变事件
*/
private void onDateTimeChanged() {
// 如果监听器不为空
if (mOnDateTimeChangedListener != null) {
// 调用监听器的回调方法
mOnDateTimeChangedListener.onDateTimeChanged(this, getCurrentYear(),
getCurrentMonth(), getCurrentDay(), getCurrentHourOfDay(), getCurrentMinute());
}
}
}