如何在MaterialDatePicker中禁用随机指定日期?
问题分析与解决方案
你的代码核心问题有两个:一是isValid方法的循环逻辑错误,导致仅最后一个禁用日期生效;二是Date构造函数的使用错误,会生成错误的年份日期。以下是修正后的完整实现:
问题根源
- 循环逻辑错误:原
isValid方法中,遍历禁用日期时每次都覆盖s1变量,最终仅保留最后一个日期的字符串,因此只会和最后一个日期做比较。 - 日期初始化错误:
new Date(2023,Calendar.NOVEMBER,10)是过时API,且Date的年份参数是从1900开始偏移的,这会生成3923年11月10日而非预期的2023年11月10日。 - 日期比较方式不严谨:使用
SimpleDateFormat格式化字符串比较日期,容易出现时区、格式匹配等问题。
修正后的代码实现
1. 自定义日期验证器类
public class DaysOutOfValidate implements CalendarConstraints.DateValidator { private List<Long> disabledDatesMillis; // 存储禁用日期的UTC毫秒数(仅保留年月日) // 接收禁用日期列表,转换为UTC毫秒数(统一时区,避免偏差) public DaysOutOfValidate(List<Date> dates) { disabledDatesMillis = new ArrayList<>(); Calendar utcCalendar = Calendar.getInstance(TimeZone.getTimeZone("UTC")); for (Date date : dates) { utcCalendar.setTime(date); // 清空时分秒,仅保留年月日部分 utcCalendar.set(Calendar.HOUR_OF_DAY, 0); utcCalendar.set(Calendar.MINUTE, 0); utcCalendar.set(Calendar.SECOND, 0); utcCalendar.set(Calendar.MILLISECOND, 0); disabledDatesMillis.add(utcCalendar.getTimeInMillis()); } } // Parcel构造函数,正确读取禁用日期列表 protected DaysOutOfValidate(Parcel in) { disabledDatesMillis = new ArrayList<>(); in.readList(disabledDatesMillis, Long.class.getClassLoader()); } @Override public boolean isValid(long date) { // 将传入的日期转换为UTC时区的年月日毫秒数 Calendar utcCalendar = Calendar.getInstance(TimeZone.getTimeZone("UTC")); utcCalendar.setTimeInMillis(date); utcCalendar.set(Calendar.HOUR_OF_DAY, 0); utcCalendar.set(Calendar.MINUTE, 0); utcCalendar.set(Calendar.SECOND, 0); utcCalendar.set(Calendar.MILLISECOND, 0); long targetMillis = utcCalendar.getTimeInMillis(); // 遍历禁用列表,匹配则返回false(禁用该日期) for (long disabledMillis : disabledDatesMillis) { if (disabledMillis == targetMillis) { return false; } } return true; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeList(disabledDatesMillis); } public static final Creator<DaysOutOfValidate> CREATOR = new Creator<DaysOutOfValidate>() { @Override public DaysOutOfValidate createFromParcel(Parcel in) { return new DaysOutOfValidate(in); } @Override public DaysOutOfValidate[] newArray(int size) { return new DaysOutOfValidate[size]; } }; }
2. 主页面日期初始化与Picker创建
// 主页面创建Picker的代码 MaterialDatePicker materialDatePicker = MaterialDatePicker.Builder.dateRangePicker() .setCalendarConstraints(DaysOutOfValidate().build()) .setSelection(Pair.create(MaterialDatePicker.thisMonthInUtcMilliseconds(), MaterialDatePicker.todayInUtcMilliseconds())) .build(); materialDatePicker.show(getSupportFragmentManager(), "S"); // 生成禁用日期列表的方法 private CalendarConstraints.Builder DaysOutOfValidate() { List<Date> listDates = new ArrayList<>(); Calendar calendar = Calendar.getInstance(); // 正确创建2023年11月10日 calendar.set(2023, Calendar.NOVEMBER, 10, 0, 0, 0); listDates.add(calendar.getTime()); // 正确创建2023年11月16日 calendar.set(2023, Calendar.NOVEMBER, 16, 0, 0, 0); listDates.add(calendar.getTime()); CalendarConstraints.Builder constraintsBuilderRange = new CalendarConstraints.Builder(); constraintsBuilderRange.setValidator(new DaysOutOfValidate(listDates)); return constraintsBuilderRange; }
关键优化点
- 统一时区处理:所有日期转换为UTC时区的毫秒数,避免不同时区导致的日期偏差。
- 精确日期比较:直接比较毫秒数而非格式化字符串,避免格式匹配错误。
- 修复Parcelable实现:正确读写禁用日期列表,保证验证器状态在配置变更时不丢失。
- 正确初始化日期:使用
Calendar生成目标日期,避免过时API的年份偏移问题。
内容的提问来源于stack exchange,提问作者Osin94
相关产品推荐
相关产品推荐

