From 366bcf719d053beeacd5b4469d189ec02886d165 Mon Sep 17 00:00:00 2001 From: msidolphin Date: Wed, 1 Apr 2020 12:51:29 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=E8=AE=BE=E7=BD=AE?= =?UTF-8?q?=E9=A6=96=E6=97=A5=E5=81=8F=E7=A7=BB=E9=87=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- API.md | 6 +- API_en.md | 4 +- example/lib/custom_offset_page.dart | 192 ++++++++++++++++++++++++++++ example/lib/main.dart | 9 ++ lib/calendar_provider.dart | 2 +- lib/configuration.dart | 7 +- lib/controller.dart | 8 +- lib/utils/date_util.dart | 34 +++-- lib/widget/calendar_view.dart | 2 +- lib/widget/month_view.dart | 11 +- lib/widget/week_view.dart | 3 +- 11 files changed, 250 insertions(+), 28 deletions(-) create mode 100644 example/lib/custom_offset_page.dart diff --git a/API.md b/API.md index e5e0185..4363744 100644 --- a/API.md +++ b/API.md @@ -74,7 +74,8 @@ Set selectedDateTimeList = EMPTY_SET, DateModel selectDateModel, int maxMultiSelectCount = 9999, - Map extraDataMap = EMPTY_MAP}) + Map extraDataMap = EMPTY_MAP, + int offset = 0}) ``` #### 通用参数说明 @@ -94,11 +95,12 @@ | minSelectDay | 可以选择的最小月份的日子 | 1 | | maxSelectYear | 可以选择的最大年份 | 2055 | | maxSelectMonth | 可以选择的最大年份的月份 | 12 | -| maxSelectDay | 可以选择的最大月份的日子 | 30,注意:不能超过对应月份的总天数 | +| maxSelectDay | 可以选择的最大月份的日子 | 30,注意:不能超过对应月份的总天数 | | selectedDateList | 被选中的日期,用于多选 | 默认为空Set, Set selectedDateList = new Set() | | selectDateModel | 当前选择项,用于单选 | 默认为空 | | maxMultiSelectCount | 多选,最多选多少个 | hhh | | extraDataMap | 自定义额外的数据 | 默认为空Map,Map extraDataMap = new Map() | +| offset | 首日偏移量 | 0 | #### 给controller添加事件监听 diff --git a/API_en.md b/API_en.md index 889ede0..cd53593 100644 --- a/API_en.md +++ b/API_en.md @@ -73,7 +73,8 @@ One is to use the controller to add event monitoring and use the controller to o Set selectedDateTimeList = EMPTY_SET, DateModel selectDateModel, int maxMultiSelectCount = 9999, - Map extraDataMap = EMPTY_MAP}) + Map extraDataMap = EMPTY_MAP, + int offset}) ``` #### General parameter description @@ -98,6 +99,7 @@ One is to use the controller to add event monitoring and use the controller to o | selectDateModel | Current selection, for radio selection | 默认为空 | | maxMultiSelectCount | Multiple choice, how many at most | hhh | | extraDataMap | Customize additional data | 默认为空Map,Map extraDataMap = new Map() | +| offset | The offset of first day | 0 | #### Add event listening to controller diff --git a/example/lib/custom_offset_page.dart b/example/lib/custom_offset_page.dart new file mode 100644 index 0000000..830998b --- /dev/null +++ b/example/lib/custom_offset_page.dart @@ -0,0 +1,192 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_custom_calendar/flutter_custom_calendar.dart'; + +/** + * 自定义风格+单选 + */ +class CustomOffsetPage extends StatefulWidget { + CustomOffsetPage({Key key, this.title}) : super(key: key); + + final String title; + + @override + _CustomOffsetPageState createState() => _CustomOffsetPageState(); +} + +class _CustomOffsetPageState extends State { + ValueNotifier text; + ValueNotifier selectText; + + CalendarController controller; + + @override + void initState() { + super.initState(); + controller = new CalendarController( + offset: 5 + ); + + controller.addMonthChangeListener( + (year, month) { + text.value = "$year年$month月"; + }, + ); + + controller.addOnCalendarSelectListener((dateModel) { + //刷新选择的时间 + selectText.value = + "单选模式\n选中的时间:\n${controller.getSingleSelectCalendar()}"; + }); + + text = new ValueNotifier("${DateTime.now().year}年${DateTime.now().month}月"); + + selectText = new ValueNotifier( + "单选模式\n选中的时间:\n${controller.getSingleSelectCalendar()}"); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text(widget.title), + ), + body: new Container( + child: new Column( + children: [ + new Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + new IconButton( + icon: Icon(Icons.navigate_before), + onPressed: () { + controller.moveToPreviousMonth(); + }), + ValueListenableBuilder( + valueListenable: text, + builder: (context, value, child) { + return new Text(text.value); + }), + new IconButton( + icon: Icon(Icons.navigate_next), + onPressed: () { + controller.moveToNextMonth(); + }), + ], + ), + CalendarViewWidget( + calendarController: controller, + weekBarItemWidgetBuilder: () { + return CustomStyleWeekBarItem(); + }, + dayWidgetBuilder: (dateModel) { + return CustomStyleDayWidget(dateModel); + } + ), + ValueListenableBuilder( + valueListenable: selectText, + builder: (context, value, child) { + return new Text(selectText.value); + }), + ], + ), + ), + floatingActionButton: FloatingActionButton( + onPressed: () {}, + tooltip: 'Increment', + child: Icon(Icons.add), + ), // This trailing comma makes auto-formatting nicer for build methods. + ); + } +} + +class CustomStyleWeekBarItem extends BaseWeekBar { + final List weekList = ["三", "四", "五", "六", "日", "一", "二"]; + + @override + Widget getWeekBarItem(int index) { + return new Container( + child: new Center( + child: new Text(weekList[index]), + ), + ); + } +} + +class CustomStyleDayWidget extends BaseCustomDayWidget { + CustomStyleDayWidget(DateModel dateModel) : super(dateModel); + + @override + void drawNormal(DateModel dateModel, Canvas canvas, Size size) { +// if (!dateModel.isCurrentMonth) { +// return; +// } + bool isWeekend = dateModel.isWeekend; + bool isInRange = dateModel.isInRange; + + //顶部的文字 + TextPainter dayTextPainter = new TextPainter() + ..text = TextSpan( + text: dateModel.day.toString(), + style: new TextStyle( + color: !isInRange + ? Colors.grey + : isWeekend ? Colors.blue : Colors.black, + fontSize: 16)) + ..textDirection = TextDirection.ltr + ..textAlign = TextAlign.center; + + dayTextPainter.layout(minWidth: size.width, maxWidth: size.width); + dayTextPainter.paint(canvas, Offset(0, 10)); + + //下面的文字 + TextPainter lunarTextPainter = new TextPainter() + ..text = new TextSpan( + text: dateModel.lunarString, + style: new TextStyle( + color: !isInRange + ? Colors.grey + : isWeekend ? Colors.blue : Colors.grey, + fontSize: 12)) + ..textDirection = TextDirection.ltr + ..textAlign = TextAlign.center; + + lunarTextPainter.layout(minWidth: size.width, maxWidth: size.width); + lunarTextPainter.paint(canvas, Offset(0, size.height / 2)); + } + + @override + void drawSelected(DateModel dateModel, Canvas canvas, Size size) { +// if (!dateModel.isCurrentMonth) { +// return; +// } + //绘制背景 + Paint backGroundPaint = new Paint() + ..color = Colors.blue + ..strokeWidth = 2; + double padding = 8; + canvas.drawCircle(Offset(size.width / 2, size.height / 2), + (size.width - padding) / 2, backGroundPaint); + + //顶部的文字 + TextPainter dayTextPainter = new TextPainter() + ..text = TextSpan( + text: dateModel.day.toString(), + style: new TextStyle(color: Colors.white, fontSize: 16)) + ..textDirection = TextDirection.ltr + ..textAlign = TextAlign.center; + + dayTextPainter.layout(minWidth: size.width, maxWidth: size.width); + dayTextPainter.paint(canvas, Offset(0, 10)); + + //下面的文字 + TextPainter lunarTextPainter = new TextPainter() + ..text = new TextSpan( + text: dateModel.lunarString, + style: new TextStyle(color: Colors.white, fontSize: 12)) + ..textDirection = TextDirection.ltr + ..textAlign = TextAlign.center; + + lunarTextPainter.layout(minWidth: size.width, maxWidth: size.width); + lunarTextPainter.paint(canvas, Offset(0, size.height / 2)); + } +} diff --git a/example/lib/main.dart b/example/lib/main.dart index 565959e..0d2af75 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -1,3 +1,5 @@ +import 'package:example1/custom_offset_page.dart'; + import 'only_week_page.dart'; import 'red_style_page.dart'; import 'package:flutter/material.dart'; @@ -42,6 +44,7 @@ class MyApp extends StatelessWidget { ), "/blue_style_page": (context) => BlueStylePage(title: "蓝色背景Demo"), "/red_style_page": (context) => RedStylePage(title: "蓝色背景Demo"), + "/custom_offset_page": (context) => CustomOffsetPage(title: "自定义偏移量"), }, title: 'Flutter Demo', theme: ThemeData( @@ -105,6 +108,12 @@ class HomePage extends StatelessWidget { Navigator.pushNamed(context, "/red_style_page"); }, child: new Text("红色Demo"), + ), + new RaisedButton( + onPressed: () { + Navigator.pushNamed(context, "/custom_offset_page"); + }, + child: new Text("自定义偏移量"), ) ], ), diff --git a/lib/calendar_provider.dart b/lib/calendar_provider.dart index 90b426c..0dd700a 100644 --- a/lib/calendar_provider.dart +++ b/lib/calendar_provider.dart @@ -173,7 +173,7 @@ class CalendarProvider extends ChangeNotifier { calendarConfiguration.showMode == CalendarConstants.MODE_SHOW_MONTH_AND_WEEK) { int lineCount = DateUtil.getMonthViewLineCount( - calendarConfiguration.nowYear, calendarConfiguration.nowMonth); + calendarConfiguration.nowYear, calendarConfiguration.nowMonth, calendarConfiguration.offset); totalHeight = calendarConfiguration.itemSize * (lineCount) + calendarConfiguration.verticalSpacing * (lineCount - 1); } else { diff --git a/lib/configuration.dart b/lib/configuration.dart index 5786dd0..ab3d0a7 100644 --- a/lib/configuration.dart +++ b/lib/configuration.dart @@ -78,6 +78,10 @@ class CalendarConfiguration { DateModel minSelectDate; DateModel maxSelectDate; + /// 首日偏移量 first day offset + /// first day = (first day of month or week) + offset + final int offset; + CalendarConfiguration( {this.selectMode, this.minYear, @@ -104,7 +108,8 @@ class CalendarConfiguration { this.itemSize, this.showMode, this.padding, - this.margin}); + this.margin, + this.offset = 0}); @override String toString() { diff --git a/lib/controller.dart b/lib/controller.dart index 910ce1a..f284fbe 100644 --- a/lib/controller.dart +++ b/lib/controller.dart @@ -50,7 +50,10 @@ class CalendarController { Set selectedDateTimeList = EMPTY_SET, //多选模式下,默认选中的item列表 DateModel selectDateModel, //单选模式下,默认选中的item int maxMultiSelectCount = 9999, - Map extraDataMap = EMPTY_MAP}) { + Map extraDataMap = EMPTY_MAP, + int offset = 0 // 首日偏移量 + }) { + assert(offset >= 0 && offset <= 6); LogUtil.log(TAG: this.runtimeType, message: "init CalendarConfiguration"); //如果没有指定当前月份和年份,默认是当年时间 if (nowYear == null) { @@ -76,7 +79,8 @@ class CalendarController { extraDataMap: extraDataMap, maxSelectDay: maxSelectDay, maxMultiSelectCount: maxMultiSelectCount, - selectDateModel: selectDateModel); + selectDateModel: selectDateModel, + offset: offset); calendarConfiguration.defaultSelectedDateList = new HashSet(); calendarConfiguration.defaultSelectedDateList diff --git a/lib/utils/date_util.dart b/lib/utils/date_util.dart index 898200f..554a8eb 100644 --- a/lib/utils/date_util.dart +++ b/lib/utils/date_util.dart @@ -1,3 +1,4 @@ +import 'package:flutter/cupertino.dart'; import 'package:flutter_custom_calendar/model/date_model.dart'; import 'package:flutter_custom_calendar/utils/LogUtil.dart'; @@ -99,10 +100,10 @@ class DateUtil { * 本月第一天,是那一周的第几天,从1开始 * @return 获取日期所在月视图对应的起始偏移量 the start diff with MonthView */ - static int getIndexOfFirstDayInMonth(DateTime dateTime) { + static int getIndexOfFirstDayInMonth(DateTime dateTime, {int offset = 0}) { DateTime firstDayOfMonth = new DateTime(dateTime.year, dateTime.month, 1); - int week = firstDayOfMonth.weekday; + int week = firstDayOfMonth.weekday + offset; return week; } @@ -111,11 +112,12 @@ class DateUtil { int year, int month, DateTime currentDate, int weekStart, {DateModel minSelectDate, DateModel maxSelectDate, - Map extraDataMap}) { + Map extraDataMap, + int offset = 0}) { print('initCalendarForMonthView start'); weekStart = DateTime.monday; - //获取月视图其实偏移量 - int mPreDiff = getIndexOfFirstDayInMonth(new DateTime(year, month)); + //获取月视图真实偏移量 + int mPreDiff = getIndexOfFirstDayInMonth(new DateTime(year, month), offset: offset); //获取该月的天数 int monthDayCount = getMonthDaysCount(year, month); @@ -136,6 +138,10 @@ class DateUtil { DateTime temp; DateModel dateModel; if (i < mPreDiff - 1) { + if (i < ((mPreDiff / 7).ceil() - 1) * 7) { + size++; + continue; + } //这个上一月的几天 temp = firstDayOfMonth.subtract(Duration(days: mPreDiff - i - 1)); @@ -183,19 +189,18 @@ class DateUtil { /** * 月的行数 */ - static int getMonthViewLineCount(int year, int month) { + static int getMonthViewLineCount(int year, int month, int offset) { DateTime firstDayOfMonth = new DateTime(year, month, 1); int monthDayCount = getMonthDaysCount(year, month); -// DateTime lastDayOfMonth = new DateTime(year, month, monthDayCount); - - int preIndex = firstDayOfMonth.weekday - 1; -// int lastIndex = lastDayOfMonth.weekday; + int preIndex = (firstDayOfMonth.weekday - 1 + offset) % 7; + int lineCount = ((preIndex + monthDayCount) / 7).ceil(); LogUtil.log( TAG: "DateUtil", message: - "getMonthViewLineCount:$year年$month月:有${((preIndex + monthDayCount) / 7).toInt() + 1}行"); - return ((preIndex + monthDayCount) / 7).toInt() + 1; + "getMonthViewLineCount:$year年$month月:有$lineCount行"); + + return lineCount; } /** @@ -205,10 +210,11 @@ class DateUtil { int year, int month, DateTime currentDate, int weekStart, {DateModel minSelectDate, DateModel maxSelectDate, - Map extraDataMap}) { + Map extraDataMap, + int offset = 0}) { List items = List(); - int weekDay = currentDate.weekday; + int weekDay = currentDate.weekday + offset; //计算本周的第一天 DateTime firstDayOfWeek = currentDate.add(Duration(days: -weekDay)); diff --git a/lib/widget/calendar_view.dart b/lib/widget/calendar_view.dart index bb016a9..4dbd34c 100644 --- a/lib/widget/calendar_view.dart +++ b/lib/widget/calendar_view.dart @@ -167,7 +167,7 @@ class CalendarContainerState extends State .showMode != CalendarConstants.MODE_SHOW_ONLY_WEEK) { //月份切换的时候,如果高度发生变化的话,需要setState使高度整体自适应 - int lineCount = DateUtil.getMonthViewLineCount(year, month); + int lineCount = DateUtil.getMonthViewLineCount(year, month, widget.calendarController.calendarConfiguration.offset); double newHeight = itemHeight * (lineCount) + calendarProvider.calendarConfiguration.verticalSpacing * (lineCount - 1); diff --git a/lib/widget/month_view.dart b/lib/widget/month_view.dart index db6a134..0eb5643 100644 --- a/lib/widget/month_view.dart +++ b/lib/widget/month_view.dart @@ -42,7 +42,6 @@ class _MonthViewState extends State void initState() { super.initState(); extraDataMap = widget.configuration.extraDataMap; - DateModel firstDayOfMonth = DateModel.fromDateTime(DateTime(widget.year, widget.month, 1)); if (CacheData.getInstance().monthListCache[firstDayOfMonth]?.isNotEmpty == @@ -56,7 +55,7 @@ class _MonthViewState extends State }); } - lineCount = DateUtil.getMonthViewLineCount(widget.year, widget.month); + lineCount = DateUtil.getMonthViewLineCount(widget.year, widget.month, widget.configuration.offset); //第一帧后,添加监听,generation发生变化后,需要刷新整个日历 WidgetsBinding.instance.addPostFrameCallback((callback) { @@ -75,7 +74,8 @@ class _MonthViewState extends State 'month': widget.month, 'minSelectDate': widget.configuration.minSelectDate, 'maxSelectDate': widget.configuration.maxSelectDate, - 'extraDataMap': extraDataMap + 'extraDataMap': extraDataMap, + 'offset': widget.configuration.offset }); setState(() {}); } @@ -85,7 +85,8 @@ class _MonthViewState extends State map['year'], map['month'], DateTime.now(), DateTime.sunday, minSelectDate: map['minSelectDate'], maxSelectDate: map['maxSelectDate'], - extraDataMap: map['extraDataMap']); + extraDataMap: map['extraDataMap'], + offset: map['offset']); } @override @@ -104,7 +105,7 @@ class _MonthViewState extends State physics: const NeverScrollableScrollPhysics(), gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 7, mainAxisSpacing: configuration.verticalSpacing), - itemCount: items.isEmpty ? 0 : 7 * lineCount, + itemCount: items.isEmpty ? 0 : items.length, itemBuilder: (context, index) { DateModel dateModel = items[index]; //判断是否被选择 diff --git a/lib/widget/week_view.dart b/lib/widget/week_view.dart index 46f6944..b307c45 100644 --- a/lib/widget/week_view.dart +++ b/lib/widget/week_view.dart @@ -51,7 +51,8 @@ class _WeekViewState extends State { widget.year, widget.month, widget.firstDayOfWeek.getDateTime(), 0, minSelectDate: widget.configuration.minSelectDate, maxSelectDate: widget.configuration.maxSelectDate, - extraDataMap: extraDataMap); + extraDataMap: extraDataMap, + offset: widget.configuration.offset); setState(() {}); }); });