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

595 lines
21 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 {
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;
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;
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;
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) ||
(mIsAm && oldVal == HOURS_IN_HALF_DAY && newVal == HOURS_IN_HALF_DAY - 1)) {
cal.setTimeInMillis(mDate.getTimeInMillis());
cal.add(Calendar.DAY_OF_YEAR, mIsAm ? -1 : 1);
isDateChanged = true;
}
// 更新上午/下午标志
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点到0点或反之的跨日变化
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;
}
}
// 根据AM/PM和选择的小时数更新日期的小时部分
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;
} 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;
} 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;
// 根据AM/PM调整日期的小时部分
if (mIsAm) {
mDate.add(Calendar.HOUR_OF_DAY, -HOURS_IN_HALF_DAY);
} else {
mDate.add(Calendar.HOUR_OF_DAY, HOURS_IN_HALF_DAY);
}
// 更新AM/PM控件的显示
updateAmPmControl();
// 通知日期时间已更改
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) {
this(context, date, DateFormat.is24HourFormat(context));
}
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;
}
/**
* 重写setEnabled方法用于启用或禁用该组件及其子组件。
*
* @param enabled true表示启用false表示禁用
*/
@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 = enabled;
}
/**
* 重写isEnabled方法用于获取当前组件的启用状态。
*
* @return true表示启用false表示禁用
*/
@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 月从0开始
* @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;
}
// 更新日期对象中的年份
mDate.set(Calendar.YEAR, year);
// 更新日期控件的显示
updateDateControl();
// 触发日期时间改变事件
onDateTimeChanged();
}
/**
* 获取当前月份从0开始
*
* @return 当前月份从0开始
*/
public int getCurrentMonth() {
return mDate.get(Calendar.MONTH);
}
/**
* 设置当前月份从0开始
*
* @param month 当前月份从0开始
*/
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 当前小时根据是否为24小时制调整
*/
private int getCurrentHour() {
if (mIs24HourView){
// 如果是24小时制直接返回小时
return getCurrentHourOfDay();
} else {
// 如果不是24小时制则进行转换
int hour = getCurrentHourOfDay();
if (hour > 12) { // HOURS_IN_HALF_DAY应为12
return hour - 12;
} else {
return hour == 0 ? 12 : hour; // 凌晨12点表示为12点
}
}
}
/**
* 设置当前小时24小时制
*
* @param hourOfDay 当前小时24小时制
*/
public void setCurrentHour(int hourOfDay) {
// 如果不是在初始化阶段且小时未改变,则直接返回
if (!mInitialising && hourOfDay == getCurrentHourOfDay()) {
return;
}
// 更新日期对象中的小时
mDate.set(Calendar.HOUR_OF_DAY, hourOfDay);
if (!mIs24HourView) {
// 如果不是24小时制则进行AM/PM转换
if (hourOfDay >= 12) { // HOURS_IN_HALF_DAY应为12
mIsAm = false;
if (hourOfDay > 12) {
hourOfDay -= 12;
}
} else {
mIsAm = true;
if (hourOfDay == 0) {
hourOfDay = 12;
}
}
// 更新AM/PM控件的显示
updateAmPmControl();
}
// 更新小时选择器的值
mHourSpinner.setValue(hourOfDay);
// 触发日期时间改变事件
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中的分钟
mDate.set(Calendar.MINUTE, minute);
// 触发日期时间变化事件
onDateTimeChanged();
}
/**
* 判断当前是否处于24小时视图模式
*
* @return 如果是24小时视图模式返回true否则返回false
*/
public boolean is24HourView() {
return mIs24HourView; // 返回mIs24HourView的值
}
/**
* 设置是否处于24小时或AM/PM模式
*
* @param is24HourView 如果是24小时模式则为true如果是AM/PM模式则为false
*/
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);
// 更新AM/PM控件
updateAmPmControl();
}
/**
* 更新日期控件
*/
private void updateDateControl() {
// 获取当前日历实例
Calendar cal = Calendar.getInstance();
// 设置日历时间为mDate的时间
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);
mAmPmSpinner.setVisibility(View.VISIBLE);
}
}
/**
* 更新小时控件
*/
private void updateHourControl() {
// 根据是否是24小时视图模式设置小时选择器的最小值和最大值
if (mIs24HourView) {
mHourSpinner.setMinValue(HOUR_SPINNER_MIN_VAL_24_HOUR_VIEW);
mHourSpinner.setMaxValue(HOUR_SPINNER_MAX_VAL_24_HOUR_VIEW);
} else {
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() {
// 如果设置了日期时间变化监听器则调用其onDateTimeChanged方法
if (mOnDateTimeChangedListener != null) {
mOnDateTimeChangedListener.onDateTimeChanged(this, getCurrentYear(),
getCurrentMonth(), getCurrentDay(), getCurrentHourOfDay(), getCurrentMinute());
}
}