前言
因工作需要,又找不到合适的,学习成本又低的日历组件,自己封装一套,也提供给小伙伴们一同使用,如有任何可优化的地方,也烦请各位大佬友情提出,感恩的♥♥♥
效果展示:
安装方法:
1、【https://gitee.com/jacobkay/unity-calendar】中的下载链接,免费拉取代码或者直接下载zip
2、将ZCalendar文件夹直接拖入工程内
3、将Prefabs/Zcalendar拖入到canvas下,即可使用
可配置显示项:
1、可自定义初始化时间
2、可控制显示前后月的日期显示
3、可控制显示农历日期
4、可做静态日历显示
5、尺寸自适应
6、可控制超出当前日期是否可以点击
7、如果做为弹窗使用,点击周围可隐藏控件
8、可选择时间范围
配置说明:
- Awake2Init:true => 以当前时间初始化,false => 不初始化,可通过ZCalendar中的Init方法初始化;
- AutoFillDate:true => 自动补充前后月日期,false => 不显示前后月日期;
- IsUnexpiredTimeCanClick:true => 超出当前时间可点击,false => 超出当前时间不可点击;
- Lunar:true => 显示农历日期,false => 不显示农历日期;
- IsPopupCalendar:true => 运行时,点击周边或者点击日期可隐藏日历,如果默认为隐藏状态,可关闭子对象bak,本身有管理脚本,不可隐藏,false => 为长显示日历
- IsStaticCalendar:true => 为静态日期显示日历,不可操作,false => 可操作日历
- AutoSetItemSize:true => 日期显示部分可根据尺寸自适应显示,false => 不会自适应显示
- RangeCalendar:true => 可选择时间范围,false => 可选择单天日期
预制体说明:
Zcalendar:日历预制体,将此预制体拖到canvas下,即可使用
ZCalendarDayItem:日历中天的预制体,可对其进行扩展
RangeImageBak:区间时间选择时,背景图
IsOnImage:选中时,背景图
Text:当前时间显示
Lunar:农历
注意:如果大佬们只是使用,没有优化想法,挂载的组件及布局不可修改,但可扩展添加需用组件
脚本说明:
1、ZCalendar.cs:程序入口,API接口
2、ZCalendarController:逻辑控制器
3、ZCalendarModel:日历模型
4、ZCalendarDayItem:日历中每一天的时间对象
API(ZCalendar中):
1、在切换月份或者年份时,可获取到每一个日期对象
public event Action<ZCalendarDayItem> UpdateDateEvent;
2、切换日期时,可获取到选择的某一天
public event Action<ZCalendarDayItem> ChoiceDayEvent;
3、监听区间时间选择,获取选择的时间对象
public event Action<ZCalendarDayItem, ZCalendarDayItem> RangeTimeEvent;
4、日历加载结束时
public event Action CompleteEvent;
5、手动获取当前选中的时间对象
public ZCalendarDayItem CrtTime { get; set; }
6、按照当前时间初始化
public void Init()
7、按照DateTime格式初始化
public void Init(DateTime dateTime)
8、按照string,YYYY-MM-DD格式初始化日历
public void Init(string dateTime)
9、显示日历
public void Show()
10、隐藏日历
public void Hide()
主要逻辑实现说明:
1、上个月
获取当月第一天是周几,通过枚举获取索引值
int GetLastMonthDays()
{
string firstWeek = new DateTime(Year, Month, 1).DayOfWeek.ToString();
return (int)Enum.Parse(typeof(DayOfWeek), firstWeek);
}
2、当月
切换日期时,从当月第一天开始,循环当月天数
3、下个月
六行七列,总共42天,减去上个月的天数和这个月的天数,剩下的就是下个月天数
void FillNextMonth()
{
int nextMonthDays = 42 - (lastMonthEmptyDays + days);
if (nextMonthDays != 0)
{
if (zCalendarModel.autoFillDate)
{
DateTime lastDay = monthFirstDay.AddDays(days);
for (int i = 0; i < nextMonthDays; i++)
{
AddDayItem(lastDay.AddDays(i));
}
}
}
}
4、获取时间
通过DateTime中一系列接口获取修改后时间
public DateTime Add(TimeSpan value);
public DateTime AddDays(double value);
public DateTime AddHours(double value);
public DateTime AddMilliseconds(double value);
public DateTime AddMinutes(double value);
public DateTime AddMonths(int months);
public DateTime AddSeconds(double value);
public DateTime AddTicks(long value);
public DateTime AddYears(int value);
农历实现
将当前时间DateTime传入,使用ChineseLunisolarCalendar获取当前农历时间,具体脚本如下:
public System.Globalization.ChineseLunisolarCalendar cncld = new System.Globalization.ChineseLunisolarCalendar();
/// <summary>
/// 农历月
/// </summary>
public string[] lunarMonths = { "正", "二", "三", "四", "五", "六", "七", "八", "九", "十", "十一", "腊" };
public string[] lunarDaysT = { "初", "十", "廿", "三" };
/// <summary>
/// 农历日
/// </summary>
public string[] lunarDays = { "一", "二", "三", "四", "五", "六", "七", "八", "九", "十" };
/// <summary>
/// 显示农历日期
/// </summary>
/// <param name="time"></param>
void SolarToLunar(DateTime dt)
{
int year = cncld.GetYear(dt);
int flag = cncld.GetLeapMonth(year);
int month = cncld.GetMonth(dt);
if (flag > 0)
{
if (flag == month)
{
//闰月
month--;
}
else if (month > flag)
{
month--;
}
}
int day = cncld.GetDayOfMonth(dt);
lunarTxt.text = (day == 1) ? GetLunarMonth(month) : GetLunarDay(day);
//Debug.Log($"{year}-{(month.ToString().Length == 1 ? "0" + month : month + "")}-{(day.ToString().Length == 1 ? "0" + day : day + "")}");
}
/// <summary>
/// 获取农历月
/// </summary>
/// <param name="month"></param>
/// <returns></returns>
string GetLunarMonth(int month)
{
if (month < 13 && month > 0)
{
return $"{lunarMonths[month - 1]}月";
}
}
/// <summary>
/// 获取农历年
/// </summary>
/// <param name="day"></param>
/// <returns></returns>
string GetLunarDay(int day)
{
if (day > 0 && day < 32)
{
if (day != 20 && day != 30)
{
return string.Concat(lunarDaysT[(day - 1) / 10], lunarDays[(day - 1) % 10]);
}
else
{
return string.Concat(lunarDays[(day - 1) / 10], lunarDaysT[1]);
}
}
}
最后附上控制器代码
/*
* Created by JacobKay - 2022.08.24
*/
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace ZCalendar
{
public class ZCalendarController
{
public int Year { set; get; }
public int Month { set; get; }
public int Day { set; get; }
/// <summary>
/// 当前是否在区间选择状态
/// </summary>
private bool isInRange = false;
public bool IsInRange { get { return isInRange; } }
private string week;
private DateTime now;
private int days;
/// <summary>
/// 当前选中的位置
/// </summary>
public Vector3 pos;
private int lastMonthDays;
private int nextMonthDays;
public ZCalendar zCalendar;
public ZCalendarModel zCalendarModel;
public DateTime nowTime = DateTime.Now;
private int lastMonthEmptyDays;
bool isShow = true;
public bool isInit = false;
/// <summary>
/// 保存文字颜色
/// </summary>
public Color greyColor;
public System.Globalization.ChineseLunisolarCalendar cncld = new System.Globalization.ChineseLunisolarCalendar();
/// <summary>
/// 农历月
/// </summary>
public string[] lunarMonths = { "正", "二", "三", "四", "五", "六", "七", "八", "九", "十", "十一", "腊" };
public string[] lunarDaysT = { "初", "十", "廿", "三" };
/// <summary>
/// 农历日
/// </summary>
public string[] lunarDays = { "一", "二", "三", "四", "五", "六", "七", "八", "九", "十" };
DateTime monthFirstDay;
/// <summary>
/// 初始化
/// </summary>
/// <param name="date"></param>
public void Init()
{
zCalendarModel.zCalendarController = this;
zCalendarModel.Init();
if (zCalendarModel.isStaticCalendar) return;
// 动态日历,可关闭
if (zCalendarModel.isPopupCalendar)
{
zCalendarModel.btnClose.onClick.AddListener(() =>
{
Hide();
});
}
zCalendarModel.btnLastYear.onClick.AddListener(LastYear);
zCalendarModel.btnNextYear.onClick.AddListener(NextYear);
zCalendarModel.btnLastMonth.onClick.AddListener(LastMonth);
zCalendarModel.btnNextMonth.onClick.AddListener(NextMonth);
}
/// <summary>
/// 按照规定时间初始化日历
/// </summary>
public void InitDate(DateTime date)
{
now = date;
DestroyAllChildren();
UpdateYear();
UpdateMonth();
UpdateDays();
UpdateData();
if (!isInit)
{
isInit = true;
zCalendar.DateComplete();
}
}
void LastYear()
{
now = now.AddYears(-1);
DestroyAllChildren();
UpdateYear();
UpdateMonth();
UpdateDays();
UpdateData();
}
void NextYear()
{
now = now.AddYears(1);
DestroyAllChildren();
UpdateYear();
UpdateMonth();
UpdateDays();
UpdateData();
}
void LastMonth()
{
now = now.AddMonths(-1);
DestroyAllChildren();
UpdateYear();
UpdateMonth();
UpdateDays();
UpdateData();
}
void NextMonth()
{
now = now.AddMonths(1);
DestroyAllChildren();
UpdateYear();
UpdateMonth();
UpdateDays();
UpdateData();
}
List<ZCalendarDayItem> dayItemList = new List<ZCalendarDayItem>();
/// <summary>
/// 如果是区间日历,选择时间时,需要判断当前日期选择状态
/// </summary>
/// <returns></returns>
public void ChangeRangeType(ZCalendarDayItem dayItem)
{
isInRange = !isInRange;
if (dayItemList.Count >= 2)
{
dayItemList.Clear();
}
if (dayItemList.Count == 0)
{
dayItemList.Add(dayItem);
}
else
{
int compare = DateTime.Compare(dayItemList[0].dateTime, dayItem.dateTime);
if (compare <= 0)
{
dayItemList.Add(dayItem);
}
else
{
dayItemList.Insert(0, dayItem);
}
}
if (!isInRange)
{
zCalendar.RangeCalendar(dayItemList[0], dayItemList[1]);
}
}
/// <summary>
/// 显示日历
/// </summary>
public void Show()
{
if (pos != null && !isShow)
{
isShow = true;
zCalendar.transform.localPosition = pos;
}
}
/// <summary>
/// 隐藏日历
/// </summary>
public void Hide()
{
if (isShow && !isInRange)
{
isShow = false;
Debug.Log("hide");
zCalendar.transform.localPosition = new Vector3(pos.x, 5000, pos.z);
}
}
/// <summary>
/// 查询年数据
/// </summary>
void UpdateYear()
{
Year = now.Year;
}
/// <summary>
/// 查询月数据
/// </summary>
void UpdateMonth()
{
Month = int.Parse(now.Month.ToString("00"));
}
/// <summary>
/// 返回要查询那天
/// </summary>
/// <returns></returns>
void UpdateDays()
{
days = DateTime.DaysInMonth(Year, Month);
if (Day == 0)
{
Day = now.Day;
}
else if (Day > days)
{
Day = days;
}
}
/// <summary>
/// 更新显示月份
/// </summary>
void UpdateData()
{
zCalendarModel.SetTimeTxt(Year, Month);
FillLastMonth();
for (int i = 0; i < days; i++)
{
AddDayItem(monthFirstDay.AddDays(i));
}
FillNextMonth();
}
/// <summary>
/// 自动填充上个月内容
/// </summary>
void FillLastMonth()
{
monthFirstDay = new DateTime(Year, Month, 1);
lastMonthEmptyDays = GetLastMonthDays();
if (zCalendarModel.autoFillDate)
{
for (int i = lastMonthEmptyDays; i > 0; i--)
{
AddDayItem(monthFirstDay.AddDays(-i));
}
}
else
{
for (int i = 0; i < lastMonthEmptyDays; i++)
{
ZCalendarDayItem dayItem = zCalendarModel.Instantiate();
dayItem.zCalendarController = this;
dayItem.CloseClickAble();
}
}
}
/// <summary>
/// 添加下个月的时间
/// </summary>
void FillNextMonth()
{
int nextMonthDays = 42 - (lastMonthEmptyDays + days);
if (nextMonthDays != 0)
{
if (zCalendarModel.autoFillDate)
{
DateTime lastDay = monthFirstDay.AddDays(days);
for (int i = 0; i < nextMonthDays; i++)
{
AddDayItem(lastDay.AddDays(i));
}
}
}
}
/// <summary>
/// 添加日期对象
/// </summary>
void AddDayItem(DateTime dateTime)
{
ZCalendarDayItem dayItem = zCalendarModel.Instantiate();
dayItem.zCalendarController = this;
dayItem.Init(dateTime, Day);
zCalendar.UpdateDate(dayItem);
if (!isInRange && dayItemList.Count > 0)
{
dayItem.IsRangeDayItem(dayItemList[0], dayItemList[1]);
}
}
/// <summary>
/// 判断上一个月有几天
/// </summary>
/// <returns></returns>
int GetLastMonthDays()
{
string firstWeek = new DateTime(Year, Month, 1).DayOfWeek.ToString();
return (int)Enum.Parse(typeof(DayOfWeek), firstWeek);
}
/// <summary>
/// 删除所有内容
/// </summary>
void DestroyAllChildren()
{
List<Transform> lst = new List<Transform>();
int count = zCalendarModel.dayContent.childCount;
for (int i = 0; i < count; i++)
{
Transform child = zCalendarModel.dayContent.GetChild(i);
lst.Add(child);
}
for (int i = 0; i < lst.Count; i++)
{
MonoBehaviour.Destroy(lst[i].gameObject);
}
}
}
}
如需完整插件 和 更多好用插件,烦请大佬到【JacobKay的博客_CSDN博客-领域博主】中查看
如有插件优化,或者更多好推荐,烦请大佬友情指出