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

512 lines
18 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.
*/
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;
// 自定义的日期时间选择器类继承自FrameLayout
public class DateTimePicker extends FrameLayout {
// 默认启用状态
private static final boolean DEFAULT_ENABLE_STATE = true;
// 半天和整天的小时数一周的天数以及日期、小时、分钟和AM/PM选择器的最小和最大值
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;
private static final int HOUR_SPINNER_MAX_VAL_24_HOUR_VIEW = 23;
private static final int HOUR_SPINNER_MIN_VAL_12_HOUR_VIEW = 1;
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;
// NumberPicker组件用于选择日期、小时、分钟和在12小时模式下上午/下午
private final NumberPicker mDateSpinner;
private final NumberPicker mHourSpinner;
private final NumberPicker mMinuteSpinner;
private final NumberPicker mAmPmSpinner;
// 用于存储和计算日期的Calendar对象
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();
}
};
// AM/PM选择器值改变时的监听器
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); // 如果是上午减去12小时
} else {
mDate.add(Calendar.HOUR_OF_DAY, HOURS_IN_HALF_DAY); // 如果是下午加上12小时
}
// 更新AM/PM控件的显示
updateAmPmControl();
// 通知日期时间已改变
onDateTimeChanged();
}
};
// 日期时间改变时的回调接口
public interface OnDateTimeChangedListener {
void onDateTimeChanged(DateTimePicker view, int year, int month,
int dayOfMonth, int hourOfDay, int minute); // 当日期时间改变时调用此方法
}
// 构造方法:仅接收上下文
public DateTimePicker(Context context) {
this(context, System.currentTimeMillis()); // 调用下一个构造方法,使用当前时间作为初始时间
}
// 构造方法:接收上下文和初始日期(以毫秒为单位的时间戳)
public DateTimePicker(Context context, long date) {
this(context, date, DateFormat.is24HourFormat(context)); // 调用下一个构造方法根据系统设置决定是否使用24小时制
}
// 构造方法接收上下文、初始日期和是否使用24小时制的标志
public DateTimePicker(Context context, long date, boolean is24HourView) {
super(context); // 调用父类FrameLayout的构造方法
mDate = Calendar.getInstance(); // 初始化Calendar对象
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);
// ...(省略了小时选择器的监听器设置,可能在后续代码中)
// 初始化分钟选择器并设置监听器
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(); // 获取系统定义的上午/下午字符串
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(); // 方法在后续代码中定义用于更新AM/PM控件
// 设置是否使用24小时制
set24HourView(is24HourView);
// 设置到当前时间
setCurrentDate(date); // 方法在后续代码中定义,用于设置当前日期时间
// 设置启用状态
setEnabled(isEnabled()); // 这里的isEnabled()可能是指mIsEnabled成员变量但更可能是调用父类方法
// 设置内容描述(用于无障碍功能)
// ...(可能省略了设置内容描述的代码)
mInitialising = false; // 标记初始化完成
}
// 重写setEnabled方法用于设置组件的启用状态
@Override
public void setEnabled(boolean enabled) {
// 如果当前状态与要设置的状态相同,则直接返回,不做任何操作
if (mIsEnabled == enabled) {
return;
}
// 调用父类的setEnabled方法设置当前组件的启用状态
super.setEnabled(enabled);
// 设置日期、分钟、小时和AM/PM选择器的启用状态
mDateSpinner.setEnabled(enabled);
mMinuteSpinner.setEnabled(enabled);
mHourSpinner.setEnabled(enabled);
mAmPmSpinner.setEnabled(enabled);
// 更新成员变量mIsEnabled记录当前组件的启用状态
mIsEnabled = enabled;
}
// 重写isEnabled方法用于获取组件的启用状态
@Override
public boolean isEnabled() {
return mIsEnabled;
}
/**
* 获取当前日期的时间戳(毫秒)
*
* @return 当前日期的时间戳(毫秒)
*/
public long getCurrentDateInTimeMillis() {
return mDate.getTimeInMillis();
}
/**
* 设置当前日期
*
* @param date 当前日期的时间戳(毫秒)
*/
public void setCurrentDate(long date) {
// 创建一个Calendar实例并根据传入的时间戳设置其时间
Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(date);
// 调用另一个setCurrentDate方法传入年、月、日、小时和分钟来设置日期
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 当前月份从0开始0代表1月
* @param dayOfMonth 当前月份中的天数
* @param hourOfDay 当前小时24小时制
* @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;
}
// 使用Calendar的set方法设置年份
mDate.set(Calendar.YEAR, year);
// 更新日期控件的显示
updateDateControl();
// 通知日期时间已更改
onDateTimeChanged();
}
/**
* 获取当前月份在一年中的月份从0开始0代表1月
*
* @return 当前月份
*/
public int getCurrentMonth() {
return mDate.get(Calendar.MONTH);
}
/**
* Set current month in the year
*
* @param month The month in the year
*/
/**
* 设置当前月份
*
* @param month 要设置的月份从0开始0代表1月
*/
public void setCurrentMonth(int month) {
// 如果不是在初始化阶段且月份未改变,则直接返回
if (!mInitialising && month == getCurrentMonth()) {
return;
}
// 使用Calendar的set方法设置月份
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;
}
// 使用Calendar的set方法设置天数
mDate.set(Calendar.DAY_OF_MONTH, dayOfMonth);
// 更新日期控件的显示
updateDateControl();
// 通知日期时间已更改
onDateTimeChanged();
}
/**
* 获取当前小时24小时制范围在0到23之间
*
* @return 当前小时24小时制
*/
public int getCurrentHourOfDay() {
return mDate.get(Calendar.HOUR_OF_DAY);
}
/**
* 获取当前小时根据是否为24小时制返回不同的小时值
*
* @return 当前小时在24小时制下为0~23在12小时制下为1~12并处理午夜和中午的特殊情况
*/
private int getCurrentHour() {
// 如果为24小时制则直接返回当前小时24小时制
if (mIs24HourView){
return getCurrentHourOfDay();
} else {
// 如果为12小时制则进行转换
int hour = getCurrentHourOfDay();
if (hour > HOURS_IN_HALF_DAY) { // HOURS_IN_HALF_DAY应为12表示半天的小时数
// 如果小时数大于12则转换为PM并减去12
return hour - HOURS_IN_HALF_DAY;
} else {
// 处理午夜和中午的特殊情况
return hour == 0 ? HOURS_IN_HALF_DAY : hour; // 午夜12点转换为12点AM其他情况保持不变
}
}
}
/**
* 设置当前小时24小时制范围在0到23之间
*
* @param hourOfDay 要设置的小时24小时制
*/
public void setCurrentHour(int hourOfDay) {
// 如果不是在初始化阶段且小时未改变,则直接返回
if (!mInitialising && hourOfDay == getCurrentHourOfDay()) {
return;
}
// 使用Calendar的set方法设置小时24小时制
mDate.set(Calendar.HOUR_OF_DAY, hourOfDay);
// 如果不是24小时制则进行AM/PM转换
if (!mIs24HourView) {
if (hourOfDay >= HOURS_IN_HALF_DAY) { // 如果小时数大于等于12则设置为PM
mIsAm = false;
if (hourOfDay > HOURS_IN_HALF_DAY) { // 如果小时数大于12则减去12
hourOfDay -= HOURS_IN_HALF_DAY;
}
} else { // 如果小时数小于12则设置为AM
mIsAm = true;
if (hourOfDay == 0) { // 处理午夜12点的特殊情况转换为12点AM
hourOfDay = HOURS_IN_HALF_DAY;
}
}
// 更新AM/PM控件的显示
updateAmPmControl();
}
// 更新小时选择器的值注意这里可能需要额外的逻辑来确保小时选择器在12小时制下正确显示
mHourSpinner.setValue(hourOfDay); // 注意这里可能需要根据mIs24HourView来决定是否转换小时值
// 通知日期时间已更改
onDateTimeChanged();
}
/**
* 获取当前分钟
*
* @return 当前分钟
*/
public int getCurrentMinute() {
return mDate.get(Calendar.MINUTE); // 从mDate日历对象中获取当前分钟
}
/**
* 设置当前分钟
*
* @param minute 要设置的分钟
*/
public void setCurrentMinute(int minute) {
// 如果不在初始化阶段且分钟未改变,则直接返回
if (!mInitialising && minute == getCurrentMinute()) {
return;
}
mMinuteSpinner.setValue(minute); // 在分钟选择器中设置值
mDate.set(Calendar.MINUTE, minute); // 在mDate日历对象中设置分钟
onDateTimeChanged(); // 触发日期时间变化事件
}
/**
* 判断是否处于24小时视图模式
*
* @return 如果是24小时视图返回true否则返回false
*/
public boolean is24HourView() {
return mIs24HourView; // 返回mIs24HourView的值
}
/**
* 设置是否处于24小时或AM/PM模式
*
* @param is24HourView true表示24小时模式false表示AM/PM模式
*/
public void set24HourView(boolean is24HourView) {
// 如果当前模式与要设置的模式相同,则直接返回
if (mIs24HourView == is24HourView) {
return;
}
mIs24HourView = is24HourView; // 设置新的模式
// 根据模式设置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()); // 设置日历时间为mDate的时间
// 调整日历以显示一周内的日期
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() {
if (mIs24HourView) {
mAmPmSpinner.setVisibility(View.GONE); // 在24小时模式下隐藏AM/PM选择器
} else {
int index = mIsAm ? Calendar.AM : Calendar.PM; // 根据当前是上午还是下午设置索引
mAmPmSpinner.setValue(index); // 设置AM/PM选择器的值
mAmPmSpinner.setVisibility(View.VISIBLE); // 显示AM/PM选择器
}
}
/**
* 更新小时选择器控制
*/
private void updateHourControl() {
if (mIs24HourView) {
// 在24小时模式下设置小时选择器的最小值和最大值
mHourSpinner.setMinValue(HOUR_SPINNER_MIN_VAL_24_HOUR_VIEW);
mHourSpinner.setMaxValue(HOUR_SPINNER_MAX_VAL_24_HOUR_VIEW);
} else {
// 在12小时模式下设置小时选择器的最小值和最大值
mHourSpinner.setMinValue(HOUR_SPINNER_MIN_VAL_12_HOUR_VIEW);
mHourSpinner.setMaxValue(HOUR_SPINNER_MAX_VAL_12_HOUR_VIEW);
}
}
/**
* 设置日期时间变化监听器
*
* @param callback 日期时间变化时的回调如果为null则不执行任何操作
*/
public void setOnDateTimeChangedListener(OnDateTimeChangedListener callback) {
mOnDateTimeChangedListener = callback; // 设置监听器
}
/**
* 当日期时间变化时触发
*/
private void onDateTimeChanged() {
if (mOnDateTimeChangedListener != null) {
// 如果设置了监听器则调用其onDateTimeChanged方法
mOnDateTimeChangedListener.onDateTimeChanged(this, getCurrentYear(),
getCurrentMonth(), getCurrentDay(), getCurrentHourOfDay(), getCurrentMinute());
}
}