You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在MaterialDatePicker中禁用随机指定日期?

问题分析与解决方案

你的代码核心问题有两个:一是isValid方法的循环逻辑错误,导致仅最后一个禁用日期生效;二是Date构造函数的使用错误,会生成错误的年份日期。以下是修正后的完整实现:

问题根源

  1. 循环逻辑错误:原isValid方法中,遍历禁用日期时每次都覆盖s1变量,最终仅保留最后一个日期的字符串,因此只会和最后一个日期做比较。
  2. 日期初始化错误:new Date(2023,Calendar.NOVEMBER,10)是过时API,且Date的年份参数是从1900开始偏移的,这会生成3923年11月10日而非预期的2023年11月10日。
  3. 日期比较方式不严谨:使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 02:13:16