今天分享一个日历选择控件,可以定义日期可选、选择范围、按月分组展示。这个日历无非就是把每个日期的数据通过系统的日历查询出来,然后用recyclerview展示即可,数据模型里可以定义哪些可选以及选定状态等等。思路就是这样了,先看看效果:
日历效果
首先定义好数据来源,即从系统的calendar获取日期列表,这里因为是要按月分组,所以我选择用一个key为月份的时间戳value为对应月份的所有日期list的map来接收查询出来的日历,在展示的时候把map转为分组的list即可:
import android.annotation.SuppressLint;
import androidx.annotation.NonNull;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.LinkedHashMap;
/**
* 日历工具类
* @author ly
* date 2020/4/24 9:52
*/
public class CalendarUtils {
public final static String YEAR = "yyyy";
public final static String YEAR_MONTH = "yyyy年MM月";
public final static String MONTH_DAY = "MM-dd";
public final static String DATE = "yyyy-MM-dd";
public final static String TIME = "HH:mm";
public final static String MONTH_DAY_TIME = "MM月dd日 hh:mm";
public final static String DATE_TIME_SECOND = "yyyy-MM-dd HH:mm:ss";
public final static String DATE_TIME = "yyyy-MM-dd HH:mm";
public final static String DATE1_TIME = "yyyy/MM/dd HH:mm";
public static @NonNull
LinkedHashMap> getMonthMap(Calendar initialCalendar, int monthNum) {
return getMonthMap(initialCalendar, monthNum, Integer.MAX_VALUE);
}
public static @NonNull
LinkedHashMap> getMonthByLimit(Calendar initialCalendar, int validSelNumLimit) {
return getMonthMap(initialCalendar, Integer.MAX_VALUE, validSelNumLimit);
}
/**
* 以月为单位,获取每个月的日期列表
*
* @param initialCalendar 初始选中的日期
* @param validSelNumLimit 最大有效选中数量
* @return 返回一个key为当前loop月的时间戳,value为当前月的所有日期list的有序map
* @author ly on 2020/4/25 11:37
*/
public static @NonNull
LinkedHashMap> getMonthMap(Calendar initialCalendar, int monthNum, int validSelNumLimit) {
//key:当前loop月的时间戳 value:当前月的所有日期list
LinkedHashMap> dateMap = new LinkedHashMap<>();
ArrayList dateInMonthList = new ArrayList<>();
if (initialCalendar == null)
initialCalendar = Calendar.getInstance();
Calendar c = Calendar.getInstance();
//设置开始遍历的date
//也可以不使用下面的set,直接c.setTime(initialCalendar.getTime()),只是第一个月的数据可能不足那个月的最大天数(ui不美观)
//告诉calendar从指定月的第一天开始,而不是精确到某一天
c.set(initialCalendar.get(Calendar.YEAR), initialCalendar.get(Calendar.MONTH), 1);
int loopMonthCount = 0;
int validDateNum = 0;//有效的可选日期数量
while (loopMonthCount < monthNum) {
// 获取当前月最大天数
int maxDayNum = c.getActualMaximum(Calendar.DATE);
int month = c.get(Calendar.MONTH);
DateInfo dateInfo = new DateInfo();
dateInfo.year = c.get(Calendar.YEAR);
dateInfo.month = month + 1;
dateInfo.day = c.get(Calendar.DAY_OF_MONTH);
dateInfo.timestamp = c.getTimeInMillis();
dateInfo.week = c.get(Calendar.DAY_OF_WEEK);
dateInfo.isSelected = initialCalendar.get(Calendar.MONTH) == c.get(Calendar.MONTH) && initialCalendar.get(Calendar.DATE) == c.get(Calendar.DATE);
//是否为可选的日期
boolean isAvailableDate = (dateInfo.isSelected || c.after(initialCalendar)) && dateInfo.week != Calendar.SUNDAY && dateInfo.week != Calendar.SATURDAY;
if (isAvailableDate)
validDateNum++;
dateInfo.canSelect = isAvailableDate && validDateNum <= validSelNumLimit;
if (dateInMonthList.isEmpty()) {//在每一月的第一周前面补空data
for (int j = 0; j < dateInfo.week - 1; j++) {
DateInfo d = new DateInfo();
dateInMonthList.add(0, d);
}
}
dateInMonthList.add(dateInfo);
if (maxDayNum == dateInfo.day) {//每loop到月底put一次
dateMap.put(dateInfo.timestamp, new ArrayList<>(dateInMonthList));
dateInMonthList.clear();
if (validDateNum < validSelNumLimit) {
loopMonthCount++;
} else {
break;//达到最大可选数后自动跳出looper
}
}
//条件满足时一直add天数即可
c.add(Calendar.DATE, 1);
}
return dateMap;
}
public static @NonNull
LinkedHashMap> getMonthMap(String startDate, String endDate) {
//key:当前loop月的时间戳 value:当前月的所有日期list
LinkedHashMap> dateMap = new LinkedHashMap<