android做一个类似钉钉的日历时间选择器
最近做一个日期选择器,仿照钉钉上的,它有三种模式:日期选择、日期间隔、日期+时间。日期是用一个月历选择的,时间是滚动的TimePicker。 大概的样子是这样的:
-
日期选择
-
日期间隔
- 日期+时间
布局
整个界面主要是一个头部(显示当前选中的日期时间),一个日历或TimePicker,一个底部(操作按钮)。用ConstraintLayout
把整个布局先调整好:
外框用的是Android现在默认的ConstraintLayout
来布局,这个约束布局功能很是强大,现在Android layout文件默认生成也是这个布局,强烈推荐,它的强大之处网上一搜一大把!
上图那个放在屏幕外面的方块是一个TimePicker,因为在日期+时间选择的时候才用到,并且会有个过渡动画,所以先把它放屏幕外面。
日历,因为我们项目中用了一个Github上的开源的MaterialCalendar 所以就直接拿来用了,然后把这个CalendarView的背景色设置成透明,下面放一个大的TextView,这个日历下面的月份就有了。应该给这个TextView设置一个特殊的字体就比较好看点。
功能
有了整体布局只要把这个布局用到一个DialogFragment
中,用的时候传入对应类型参数显示DialogFragment
就行了,日期选择器和日期间隔选择器,因为有了现成的CalendarView就变的很简单,选中日期,刷新头部的TextView,点击确定把值回调出去就行了。
这个CalendarView提供了选中事件、月份改变事件:
override fun onDateSelected(p0: MaterialCalendarView, p1: CalendarDay, p2: Boolean) {
if (isStartDateCalendarMode) {
tv_start_date.text = DateHelper.getDate(p1.date)
//如果是日期时间选择类型就切换到时间选择器
if (pickerType == DATE_TIME_PICKER_TYPE) {
animationForTimePickerVisible()
//如果是日期间隔类型就把calendarView设置成结束日期
} else if (pickerType == DATEINTERVAL_PICKER_TYPE) {
showEndDateCalendar()
}
}else {
tv_end_date.text = DateHelper.getDate(p1.date)
}
}
override fun onMonthChanged(p0: MaterialCalendarView?, p1: CalendarDay?) {
if (p1!= null) {//月份变化就动态修改底部月份的TextView的值
setCalendarBg(p1.date)
}
}
日期+时间选择器,就有个View的切换过程,在日历上选中一个日期后,马上就切换成时间选择器,还有个过渡动画。
然后这个时间选择器TimePicker,本来就是系统自带的View,但是sdk21之后变成了一个时钟的样子,不过它还是提供了一个属性timePickerMode
可以决定这个TimePicker的显示模式,是老的spinner模式就是滚动选择的模式,还是新的时钟模式。
动画
前面说了日历切换成TimePicker还有个过渡动画,我们用的是约束布局,这个约束布局有个关键帧动画使用起来很方便。
关键帧动画,就是要两个关键状态,一个开始状态,一个结束状态,中间的变化过程给系统自己完成。套用到这里就是把一个开始的布局,一个结束的布局提供给ConstraintSet
,Android的TransitionManager就能用这个ConstraintSet
执行动画了。
就比如开始的时候是layout1.xml
,就是上面图片的布局。结束的时候是layout2.xml
,布局如下:
跟layout1.xml
区别就是把calendarView移出到屏幕外面,把TimePicker放到屏幕中间了。
private val datePickerLayout: ConstraintSet by lazy { ConstraintSet() }
private val dateTimePickerLayout: ConstraintSet by lazy { ConstraintSet() }
...
//layout1的布局可以从当前的ConstraintLayout复制过来
datePickerLayout.clone(ConstraintLayoutView)
//没有加载的layout2 直接从布局文件复制过来
dateTimePickerLayout.clone(activity, R.layout.layout2)
...
//使用就很简单了,切换成TimePicker
TransitionManager.beginDelayedTransition(ConstraintLayoutView)
dateTimePickerLayout.applyTo(ConstraintLayoutView)
//切换回来
TransitionManager.beginDelayedTransition(ConstraintLayoutView)
datePickerLayout.applyTo(ConstraintLayoutView)
就这么简单,动画就完成了。
这个ConstraintSet
就是把两个布局文件的所有View的约束条件都收集起来,然后应用到ConstraintLayout上面去,所以它有个前提,你这个布局文件中所有的View都要有id,它要做一一对应的。
用这样两个xml的方式比较简单明了,ConstraintSet
它还提供了很多方法可以手动设置一些约束进去,然后应用到ConstraintLayout上去,但是不够直观设置起来比较麻烦。
最后:可能讲的不太清楚,想看源码的我放GitHub上了。