博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
CalendarListView解析与重新实现
阅读量:5882 次
发布时间:2019-06-19

本文共 4309 字,大约阅读时间需要 14 分钟。

项目地址:。分析的版本:063952b

功能介绍

CalendarListview是一个日历控件,效果如图

图片描述

总体设计

CalendarListview日历控件本质上是一个基于RecyclerView的列表控件。

DayPickerView类直接继承RecyclerView,是控件的主类,表示日历。SimpleMonthView类继承View,是列表的单元控件,表示单个月份。

总体的类结构图为

zongtisheji.png

详细设计

SimpleMonthView

SimpleMonthView类的核心方法onDraw将月份的绘制划分为三个互相独立的部分,见下图。

size.png

每一部分的绘制都涉及两个问题位置选择文本内容。除此之外,还要处理该控件的点击事件。

绘制位置

关于绘制位置的选择问题,在图中都用颜色块标出了,此外应该注意两个问题。

1.第三部分第一行中每月的第一天不一定是周日,所有要留出一定的偏移量,偏移量等于当月第一天与周日的差。采用findDayOffset()方法计算出该偏移量。
2.要求出每月的天数,才能准确结尾。在工具类CalendarUtils中解决,重点是处理闰年二月的天数。

此外对整体控件的尺寸要设置其高度与宽度。

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {    setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), mRowHeight * mNumRows + MONTH_HEADER_SIZE);}protected void onSizeChanged(int w, int h, int oldw, int oldh) {        mWidth = w;    }

文本内容

关于文本的内容涉及一些类。

1.标题绘制采用DateUtils工具类。

String DateUtils.formatDateRange(getContext(), millis, millis, flags);

简单的说,该类第二三两个参数表示起始和结束机器时间,第四个参数设置所显示的人类时间格式。

2.星期绘制采用DateFormatSymbols

//获取星期标签,如“周日”,“周一”等String[] getShortWeekdays();

点击事件

在触摸回调方法中,首先使用getDayFromLocation方法根据点击区域的得到某日(CalendarDay)。

public boolean onTouchEvent(MotionEvent event) {    if (event.getAction() == MotionEvent.ACTION_UP) {        SimpleMonthAdapter.CalendarDay calendarDay = getDayFromLocation(event.getX(), event.getY());        if (calendarDay != null) {            onDayClick(calendarDay);        }    }    return true;}

而后设计接口OnDayClickListener将点击事件委托出去。

public static abstract interface OnDayClickListener {    public abstract void onDayClick(SimpleMonthView simpleMonthView, SimpleMonthAdapter.CalendarDay calendarDay);}

SimpleMonthAdapter

SimpleMonthAdapter是处理日期数据集的数据适配器,继承于RecyclerView.Adapter。首先要认识两个内部类。

  • CalendarDay类表示单独的某一天,有三个参数表示年/月/日。

public static class CalendarDay {    int day;    int month;    int year;}
  • SelectedDays<K> 表示一个时间段的首尾两天,用来处理用户的点击行为。

public static class SelectedDays
{ private CalendarDay first; private CalendarDay last; }

1.首先在getItemCount()方法中设置要显示的月份数目。

2.处理日期相关逻辑都放在onBindViewHolder方法中进行,完成数据和视图的绑定。在这个方法中要告诉视图SimpleMonthView该显示哪年哪月,使用下列方法为视图传递具体月份,并重绘。

@Overridepublic void onBindViewHolder(ViewHolder viewHolder, int position){    SimpleMonthView v = viewHolder.simpleMonthView;    HashMap
drawingParams = new HashMap
(); //传递年月参数给子视图 v.setMonthParams(drawingParams); v.invalidate();}

3.SimpleMonthAdapter类实现了子视图SimpleMonthViewOnDayClickListener接口,处理某日被选中后的事件。并给用户留下一个处理白点。

public void onDayClick(SimpleMonthView simpleMonthView, CalendarDay calendarDay) {    mController.onDayOfMonthSelected(calendarDay.year, calendarDay.month, calendarDay.day);    setSelectedDay(calendarDay);    }

其中选中日期方法的处理流程如下

setSelectedDay.png

DayPickerView

在主类中,要完成数据适配器的设置之外,还要处理属性和用户接口。

  • TypedArray 在构造适配器时将该参数传递过去。

new SimpleMonthAdapter(getContext(), mController, typedArray);
  • DatePickerController接口虽然为用户能直接接触和感知,但该接口仅仅是控件点击事件中留给用户的处理白点,不再多说。

一些工具类

DateFormatSymbols

java.textDate.FormatSymbols类作用是封装一个局部时间,例如当月,年,星期。常见的DateFormatSimpleDateFormat格式化类都基于该类。但不宜直接使用该类。

首先实例化DateFormatSymbols类,同时使用Locale类来本地化。

DateFormatSymbols dfs = DateFormatSymbols.getInstance(Locale.ENGLISH);

而后获取各类时间日期字符串

String[] ampm = dfs.getAmPmStrings();String[] eras = dfs.getEras();String[] months = dfs.getMonths();String[] shortMonths = dfs.getShortMonths();String[] weekdays = dfs.getWeekdays();String[] shortWeekdays = dfs.getShortWeekdays();

DateFormat与SimpleDateFormat

专门负责日期格式,实现DateString之间的转化。

DateUtils

android.text.format.DateUtils类是一个日期相关工具,依赖创建时间和日期跨度有关的文本。基本使用方法如下

DateUtils.formatDateRange(Context context, long startMillis,long endMillis, int flags);

最关键的参数是第四个,可以控制时间与日期格式,形式为FORMAT_*,例如

FORMAT_SHOW_TIME //显示完整时间FORMAT_SHOW_DATE //显示完整日期FORMAT_SHOW_WEEKDAY //显示星期FORMAT_SHOW_YEAR    //显示年FORMAT_NO_MONTH_DAY //不显示星期  FORMAT_NO_YEAR        //不显示年

重新实现

首先看下初步效果

demo.jpg

原控件使用Calender来处理日期,Calender的弱点很多,在处理日期时也不简洁。改用使用实现日历列表。

首先考虑几个问题:

1.获取当月一共有多少天

LocalDate localDate = new LocalDate();LocalDate firstDay = localDate.dayOfMonth().withMinimumValue();LocalDate lastDay = localDate.dayOfMonth().withMaximumValue();Period period = new Period(firstDay, lastDay);int dayCount = period.toStandardDays().getDays()+1;//注意使用toStandardDays()方法

2.获取当月第一天对上一个周末的偏移量

LocalDate lastWeekday = firstDay.minusWeeks(1).dayOfWeek().withMaximumValue();Period p = new Period(lastWeekday, firstDay);int offset = p.getDays();

参考文献

转载地址:http://ofvix.baihongyu.com/

你可能感兴趣的文章
SCCM 2016 配置管理系列(Part8)
查看>>
struts中的xwork源码下载地址
查看>>
ABP理论学习之仓储
查看>>
我的友情链接
查看>>
Tengine新增nginx upstream模块的使用
查看>>
CentOS图形界面和命令行切换
查看>>
HTML5通信机制与html5地理信息定位(gps)
查看>>
汽车常识全面介绍 - 悬挂系统
查看>>
加快ALTER TABLE 操作速度
查看>>
学习笔记之软考数据库系统工程师教程(第一版)
查看>>
PHP 程序员的技术成长规划
查看>>
memcached 分布式聚类算法
查看>>
jquery css3问卷答题卡翻页动画效果
查看>>
$digest already in progress 解决办法——续
查看>>
虚拟机 centos设置代理上网
查看>>
Struts2中Date日期转换的问题
查看>>
mysql 数据类型
查看>>
Ubuntu 设置当前用户sudo免密码
查看>>
设置tomcat远程debug
查看>>
android 电池(一):锂电池基本原理篇【转】
查看>>