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.
git-xiaomibianqian/xkw2/DateTimePicker.java

503 lines
23 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;
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; // 是否为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();// 获取当前时间的日历对象
if (!mIs24HourView) { // 如果不是24小时制
// 如果从上午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);// 将日期加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);// 将日期减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);// 将日期加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);// 将日期减1天
isDateChanged = true;// 标记日期已改变
}
}
int newHour = mHourSpinner.getValue() % HOURS_IN_HALF_DAY + (mIsAm ? 0 : HOURS_IN_HALF_DAY);
// 获取小时数,对半天的小时数取模,如果是下午,加上半天的小时数
// 这一行代码的作用是将 12 小时制转换为 24 小时制
// mHourSpinner 是一个 Spinner 控件,用于选择小时数
// HOURS_IN_HALF_DAY 是常量,表示半天的小时数
// mIsAm 是一个布尔值,表示当前是否是上午
mDate.set(Calendar.HOUR_OF_DAY, newHour);
// 将新的小时数设置到 Calendar 对象中
// Calendar 是一个日期时间类,用于处理日期时间相关的操作
onDateTimeChanged();// 调用 onDateTimeChanged() 方法,通知界面更新日期时间显示
if (isDateChanged) {
setCurrentYear(cal.get(Calendar.YEAR));
setCurrentMonth(cal.get(Calendar.MONTH));
setCurrentDay(cal.get(Calendar.DAY_OF_MONTH));
}// 如果日期有变化,更新当前年、月、日的显示
// cal 是一个 Calendar 对象,表示当前的日期时间
}
};
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;
} // 如果偏移量不为0那么更新日期和小时选择器的值
if (offset != 0) {
mDate.add(Calendar.HOUR_OF_DAY, offset);
mHourSpinner.setValue(getCurrentHour());
updateDateControl();// 获取当前的小时数
int newHour = getCurrentHourOfDay(); // 如果小时数大于等于12那么设置上午/下午为下午
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() {// 重写监听器的onValueChange方法
@Override
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
mIsAm = !mIsAm; // 反转mIsAm的布尔值
if (mIsAm) {// 如果mIsAm为true则将mDate时间减去12小时
mDate.add(Calendar.HOUR_OF_DAY, -HOURS_IN_HALF_DAY);
} else {// 如果mIsAm为false则将mDate时间加上12小时
mDate.add(Calendar.HOUR_OF_DAY, HOURS_IN_HALF_DAY);
}// 更新上午/下午控件的状态
updateAmPmControl();
onDateTimeChanged();// 调用onDateTimeChanged方法
}
};
public interface OnDateTimeChangedListener {// 定义一个OnDateTimeChangedListener接口
void onDateTimeChanged(DateTimePicker view, int year, int month,
int dayOfMonth, int hourOfDay, int minute);
}// 定义onDateTimeChanged方法传入日期时间选择器的年、月、日、时、分等参数
public DateTimePicker(Context context)// 定义一个公共的构造函数,传入上下文参数
{
this(context, System.currentTimeMillis());// 调用另一个构造函数,传入上下文和当前时间的毫秒数
}// 调用父类的构造函数传入上下文、时间毫秒数和是否为24小时制参数。这里用到了Java中的this关键字表示当前对象。
public DateTimePicker(Context context, long date) {// 定义一个公共的构造函数,传入上下文和时间毫秒数参数
this(context, date, DateFormat.is24HourFormat(context));// 调用另一个构造函数传入上下文、时间毫秒数和是否为24小时制参数
}
public DateTimePicker(Context context, long date, boolean is24HourView) {// 定义一个公共的日期选择器类继承自View类
super(context);// 调用父类的构造函数
mDate = Calendar.getInstance(); // 获取当前时间
mInitialising = true; // 设置初始化状态为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);// 设置长按分钟选择器按钮的更新间隔为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);// 设置上午/下午选择器的值改变监听器
// update controls to initial state
updateDateControl();// 更新日期选择器的控件状态
updateHourControl();// 更新小时选择器的控件状态
updateAmPmControl();// 更新上午/下午选择器的控件状态
set24HourView(is24HourView);//设置是否为24小时制
// set to current time
setCurrentDate(date);// 设置当前时间
setEnabled(isEnabled());// 设置是否可用
// set the content descriptions
mInitialising = false;// 设置内容描述
}
@Override
public void setEnabled(boolean enabled) {
if (mIsEnabled == enabled) {// 设置控件是否可用的方法
return; // 如果当前可用状态与要设置的状态一致,则直接返回,不进行任何操作
}
super.setEnabled(enabled);// 调用父类的setEnabled方法设置控件是否可用
mDateSpinner.setEnabled(enabled);// 设置日期选择器是否可用
mMinuteSpinner.setEnabled(enabled);// 设置分钟选择器是否可用
mHourSpinner.setEnabled(enabled);// 设置小时选择器是否可用
mAmPmSpinner.setEnabled(enabled);// 设置上午/下午选择器是否可用
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);
}// 获取当前小时数24小时制
private int getCurrentHour() {
if (mIs24HourView){ // 如果是24小时制
return getCurrentHourOfDay();// 直接返回当前小时数
} else {// 如果是12小时制
int hour = getCurrentHourOfDay();// 获取当前小时数
if (hour > HOURS_IN_HALF_DAY) {// 如果当前小时数大于12
return hour - HOURS_IN_HALF_DAY;// 返回减去12的小时数
} else {// 如果当前小时数小于等于12
return hour == 0 ? HOURS_IN_HALF_DAY : hour;// 如果当前小时数为0则返回12否则返回当前小时数
}
}
}
/**
* 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);// 设置日期的小时数为设置的小时数
if (!mIs24HourView) {// 如果是12小时制
if (hourOfDay >= HOURS_IN_HALF_DAY) {// 如果设置的小时数大于等于12
mIsAm = false;// 设置为下午
if (hourOfDay > HOURS_IN_HALF_DAY) {// 如果设置的小时数大于12
hourOfDay -= HOURS_IN_HALF_DAY;// 小时数减去12
}
} else {// 如果设置的小时数小于12
mIsAm = true;// 设置为上午
if (hourOfDay == 0) {// 如果设置的小时数为0
hourOfDay = HOURS_IN_HALF_DAY;// 小时数设置为12
}
}
updateAmPmControl();// 更新上午/下午控件
}
mHourSpinner.setValue(hourOfDay);// 设置小时数的Spinner的值为设置的小时数
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小时制
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() {// 更新上午/下午控件的显示状态
if (mIs24HourView) {// 如果是24小时制
mAmPmSpinner.setVisibility(View.GONE);// 隐藏上午/下午控件
} else {// 如果是12小时制
int index = mIsAm ? Calendar.AM : Calendar.PM;// 获取当前时间是上午还是下午
mAmPmSpinner.setValue(index); // 设置上午/下午控件的值
mAmPmSpinner.setVisibility(View.VISIBLE);// 显示上午/下午控件
}
}
private void updateHourControl() {// 更新小时控件的显示范围
if (mIs24HourView) { // 如果是24小时制
mHourSpinner.setMinValue(HOUR_SPINNER_MIN_VAL_24_HOUR_VIEW); // 设置小时控件的最小值为0
mHourSpinner.setMaxValue(HOUR_SPINNER_MAX_VAL_24_HOUR_VIEW);// 设置小时控件的最大值为23
} else {
mHourSpinner.setMinValue(HOUR_SPINNER_MIN_VAL_12_HOUR_VIEW);// 设置小时控件的最小值为1
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());
}// 调用日期时间变化监听器的onDateTimeChanged方法传递当前日期时间的各个参数
}
}