單一控件同時選擇日期(月、日)和時間(24制時),不顯示年份、分、秒。
public class MonthDayHourPicker extends LinearLayout {
private NumberPicker npMonth, npDay, npHour;
private Calendar calendar = Calendar.getInstance();
public MonthDayHourPicker(Context context) {
super(context);
init(context);
}
private void init(Context context) {
setOrientation(HORIZONTAL);
setGravity(Gravity.CENTER);
npMonth = createPicker(1, 12, calendar.get(Calendar.MONTH) + 1, "%02d月");
npDay = createPicker(1, 31, calendar.get(Calendar.DAY_OF_MONTH), "%02d日");
npHour = createPicker(0, 23, calendar.get(Calendar.HOUR_OF_DAY), "%02d時");
// 動態調整日數(根據月份)
npMonth.setOnValueChangedListener((picker, oldVal, newVal) -> updateDayRange(newVal));
addView(npMonth, new LayoutParams(0, WRAP_CONTENT, 1));
addView(npDay, new LayoutParams(0, WRAP_CONTENT, 1));
addView(npHour, new LayoutParams(0, WRAP_CONTENT, 1));
}
private NumberPicker createPicker(int min, int max, int current, String format) {
NumberPicker np = new NumberPicker(getContext());
np.setMinValue(min);
np.setMaxValue(max);
np.setValue(current);
np.setFormatter(v -> String.format(format, v));
return np;
}
private void updateDayRange(int month) {
int maxDay = getMaxDayOfMonth(month);
npDay.setMaxValue(maxDay);
if (npDay.getValue() > maxDay) npDay.setValue(maxDay);
}
private int getMaxDayOfMonth(int month) {
switch (month) {
case 2: return 28; // 簡化,不考慮閏年
case 4: case 6: case 9: case 11: return 30;
default: return 31;
}
}
public Calendar getSelectedDateTime() {
Calendar cal = Calendar.getInstance();
cal.set(Calendar.MONTH, npMonth.getValue() - 1);
cal.set(Calendar.DAY_OF_MONTH, npDay.getValue());
cal.set(Calendar.HOUR_OF_DAY, npHour.getValue());
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
return cal;
}
}
// DatePicker 隱藏年份
int yearPickerId = Resources.getSystem().getIdentifier("year", "id", "android");
View yearPicker = datePicker.findViewById(yearPickerId);
if (yearPicker != null) yearPicker.setVisibility(View.GONE);
// TimePicker 隱藏分秒(API 24+)
timePicker.setIs24HourView(true);
// 隱藏分鐘選擇器(需反射或自定義布局)
// 參考:https://www.iteye.com/topic/1131942
// build.gradle
implementation 'com.github.florent37:singledateandtimepicker:2.2.0'
// 使用
new SingleDateAndTimePicker.Builder(context)
.displayHours(true)
.displayMinutes(false)
.displayDays(true)
.displayMonths(true)
.displayYears(false)
.title("選擇月日時")
.build()
.show();
| 方案 | 優點 | 缺點 | 推薦場景 |
|---|---|---|---|
| NumberPicker 組合 | 輕量、完全可控、無反射風險 | 需自行處理閏年、日數聯動 | 簡單表單、不想引入庫 |
| 原生組件隱藏 | 系統風格一致、無依賴 | 反射脆弱、API 變動風險、定制受限 | 需系統原生外觀 |
| 第三方庫 | 功能豐富、Material 風格、維護活躍 | 增大包體積、版本兼容性 | 複雜交互、項目已用相關庫 |
推薦 NumberPicker 組合:代碼可控、邏輯清晰、輕量無依賴,滿足「月日時」精確定制需求。