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

460 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.

/*
* 版权所有 (c) 2010-2011MiCode 开源社区 (www.micode.net)
* 根据 Apache 许可证 2.0 版本("许可证")授权;
* 除非符合许可证的规定,否则不得使用本文件。
* 您可以从以下网址获取许可证副本:
* http://www.apache.org/licenses/LICENSE-2.0
* 除非适用法律要求或书面同意,本软件按"原样"分发,
* 没有任何明示或暗示的保证或条件。
* 详见许可证中规定的权限和限制。
* 这是一份标准的Apache许可证2.0版本的开源声明)
*/
// 定义包路径
package net.micode.notes.ui;
// 导入Java工具类
import java.text.DateFormatSymbols; // 用于获取AM/PM本地化字符串
import java.util.Calendar; // 日期时间处理类
// 导入项目资源
import net.micode.notes.R; // 资源ID引用
// 导入Android相关类
import android.content.Context; // 上下文对象
import android.text.format.DateFormat; // 日期格式化工具
import android.view.View; // 基础视图类
import android.widget.FrameLayout; // 帧布局容器
import android.widget.NumberPicker;// 数字选择器控件
/**
* 自定义日期时间选择器组件
* 包含日期、小时、分钟和AM/PM选择器
*/
public class DateTimePicker extends FrameLayout {
// 默认启用状态
private static final boolean DEFAULT_ENABLE_STATE = true;
// 时间相关常量
private static final int HOURS_IN_HALF_DAY = 12; // 半天的12小时
private static final int HOURS_IN_ALL_DAY = 24; // 全天24小时
private static final int DAYS_IN_ALL_WEEK = 7; // 一周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; // AM/PM选择器
// 状态变量
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(); // 触发回调
}
};
// 小时变化监听器处理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();
// 处理12小时制下的日期变更
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();
}
}
// 处理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;
}
}
// 更新实际小时值考虑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));
}
}
};
// 分钟变化监听器处理59->00和00->59的边界情况
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;
// 处理分钟循环滚动情况
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();
} else {
mIsAm = true;
updateAmPmControl();
}
}
mDate.set(Calendar.MINUTE, newVal);
onDateTimeChanged();
}
};
// AM/PM变化监听器
private NumberPicker.OnValueChangeListener mOnAmPmChangedListener =
new NumberPicker.OnValueChangeListener() {
@Override
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
mIsAm = !mIsAm; // 切换AM/PM状态
// 调整12小时
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 {
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));
}
// 主构造函数
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[] ampmStrings = new DateFormatSymbols().getAmPmStrings();
mAmPmSpinner = (NumberPicker) findViewById(R.id.amPm);
mAmPmSpinner.setMinValue(AMPM_SPINNER_MIN_VAL);
mAmPmSpinner.setMaxValue(AMPM_SPINNER_MAX_VAL);
mAmPmSpinner.setDisplayedValues(ampmStrings);
mAmPmSpinner.setOnValueChangedListener(mOnAmPmChangedListener);
// 初始化控件状态
updateDateControl();
updateHourControl();
updateAmPmControl();
// 设置24小时制模式
set24HourView(is24HourView);
// 设置当前时间
setCurrentDate(date);
// 设置初始启用状态
setEnabled(DEFAULT_ENABLE_STATE);
// 初始化完成
mInitialising = 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);
mIsEnabled = enabled;
}
// 获取当前时间戳
public long getCurrentDateInTimeMillis() {
return mDate.getTimeInMillis();
}
// 设置当前时间(时间戳)
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)
);
}
// 设置当前时间(各部分)
public void setCurrentDate(int year, int month, int dayOfMonth,
int hourOfDay, int minute) {
setCurrentYear(year);
setCurrentMonth(month);
setCurrentDay(dayOfMonth);
setCurrentHour(hourOfDay);
setCurrentMinute(minute);
}
// 获取/设置年份
public int getCurrentYear() { return mDate.get(Calendar.YEAR); }
public void setCurrentYear(int year) {
if (!mInitialising && year == getCurrentYear()) return;
mDate.set(Calendar.YEAR, year);
updateDateControl();
onDateTimeChanged();
}
// 获取/设置月份
public int getCurrentMonth() { return mDate.get(Calendar.MONTH); }
public void setCurrentMonth(int month) {
if (!mInitialising && month == getCurrentMonth()) return;
mDate.set(Calendar.MONTH, month);
updateDateControl();
onDateTimeChanged();
}
// 获取/设置日
public int getCurrentDay() { return mDate.get(Calendar.DAY_OF_MONTH); }
public void setCurrentDay(int dayOfMonth) {
if (!mInitialising && dayOfMonth == getCurrentDay()) return;
mDate.set(Calendar.DAY_OF_MONTH, dayOfMonth);
updateDateControl();
onDateTimeChanged();
}
// 获取24小时制小时
public int getCurrentHourOfDay() {
return mDate.get(Calendar.HOUR_OF_DAY);
}
// 获取当前显示的小时考虑12/24小时制
private int getCurrentHour() {
if (mIs24HourView){
return getCurrentHourOfDay();
} 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;
}
}
}
// 设置小时
public void setCurrentHour(int hourOfDay) {
if (!mInitialising && hourOfDay == getCurrentHourOfDay()) return;
mDate.set(Calendar.HOUR_OF_DAY, hourOfDay);
if (!mIs24HourView) {
// 处理12小时制下的AM/PM状态
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();
}
// 获取/设置分钟
public int getCurrentMinute() { return mDate.get(Calendar.MINUTE); }
public void setCurrentMinute(int minute) {
if (!mInitialising && minute == getCurrentMinute()) return;
mMinuteSpinner.setValue(minute);
mDate.set(Calendar.MINUTE, minute);
onDateTimeChanged();
}
// 获取/设置24小时制模式
public boolean is24HourView() { return mIs24HourView; }
public void set24HourView(boolean is24HourView) {
if (mIs24HourView == is24HourView) return;
mIs24HourView = is24HourView;
// 更新AM/PM选择器可见性
mAmPmSpinner.setVisibility(is24HourView ? View.GONE : View.VISIBLE);
// 更新小时选择器范围
updateHourControl();
// 设置当前小时(保持实际时间不变)
setCurrentHour(getCurrentHourOfDay());
// 更新AM/PM显示
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);
// 格式化日期为"MM.dd EEEE"格式(如"07.15 星期一"
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);
} else {
int index = mIsAm ? Calendar.AM : Calendar.PM;
mAmPmSpinner.setValue(index);
mAmPmSpinner.setVisibility(View.VISIBLE);
}
}
// 更新小时控件范围12/24小时制
private void updateHourControl() {
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);
}
}
// 设置日期时间变化监听器
public void setOnDateTimeChangedListener(OnDateTimeChangedListener callback) {
mOnDateTimeChangedListener = callback;
}
// 触发日期时间变化回调
private void onDateTimeChanged() {
if (mOnDateTimeChangedListener != null) {
mOnDateTimeChangedListener.onDateTimeChanged(this,
getCurrentYear(), getCurrentMonth(), getCurrentDay(),
getCurrentHourOfDay(), getCurrentMinute());
}
}
}