由於以前公司的產品需求,須要作一個符合日曆view作某些功能,當時想着說,日曆view仍是挺簡單的,官方也有封裝,要不了多久,所以提早調研的時候主要看了一下官方的CalendarView以及一些github上的開源日曆view。git
然而等到開始開發,拿到UI和UE的交互圖時,我傻了,和說好的不同啊,UE跟我說,你去看看飛豬的那個日曆,交互和那個差很少。行吧,只能從新擼代碼了。github
飛豬的那個日曆,是一個豎向日歷,和顯示連續幾個月的日期,而後不可選擇的日期標灰,而後還會顯示一些節假日,支持區間選擇,每月份有一個懸停的標題。這邊咱們的需求沒有區間選擇,可是有每一個日期的view有不一樣的顯示需求。bash
一個豎向滾動的日曆,最頂部是周幾,每月的日期數不同,要有留白,以及頂部的月份標識,我以爲你們看到這種需求,第一反應都是RecyclerView對吧,固然我也是這麼想的,每日都是一個item,而後留白就是空的佔位,大體的思路是能夠決定了的。app
由於頂部的星期是固定顯示的,並且是橫向鋪開的,因此一個LinearLayout便可實現ide
val week = LinearLayout(context).apply {
val headParams = LayoutParams(LayoutParams.MATCH_PARENT, dip2px(context, 30f))
layoutParams = headParams
orientation = HORIZONTAL
setBackgroundColor(ContextCompat.getColor(context,R.color.color_F1F5F8))
setPadding(dip2px(context, 15f), 0, dip2px(context, 15f), 0)
}
val list = listOf("日", "一", "二", "三", "四", "五", "六")
val itemParams = LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT)
for(i in list){
val tv = TextView(context).apply {
layoutParams = itemParams
gravity = Gravity.CENTER
setTextColor(ContextCompat.getColor(context,R.color.color_92a0aa))
setTextSize(TypedValue.COMPLEX_UNIT_SP, 13f)
text = i
}
week.addView(tv)
}
複製代碼
作的比較簡陋,頂部星期的顯示寫成了一個固定的值,若是配置成xml屬性,能夠經過屬性去控制每週的起始日是星期天仍是星期一函數
須要顯示日期的話,咱們得把須要構造一個List,將全部須要顯示的數據放進List,再將它放到RecyclerView的Adapter裏。由於日期分月和天天,所以咱們須要兩個dataBean,一個是月份的Bean,一個是天天日期的Bean。性能
每月的數據是一個MonthBean,裏面包含年份,月份,以及一個List,List內包含的是這個月的天天的DateBean。每日的DateBean,須要的數據有,年費,月份,日期,同時由於置灰以及點擊的判斷,咱們還須要一些布爾值去判斷。那麼這樣基本能夠肯定MonthBean和DateBean的類型,大體代碼以下:ui
MonthBeanspa
data class MonthBean(var year: Int, var month: Int, var dateList: MutableList<DateBean> = mutableListOf())
複製代碼
DateBean.net
data class DateBean(var year: Int = 2019, var month: Int, var day: Int, var type: Int,
var isToday: Boolean = false, //是不是當天
var isChooseDay: Boolean = false, // 是不是選擇日期
) {
// 分組
val groupName: String
get() {
val sMonth = if (month < 10) String.format("0%d", month) else String.format("%d", month)
return year.toString() + "年" + sMonth + "月"
}
// 當日的準確日期
val date: String
get() {
val sMonth = if (month < 10) String.format("0%d", month) else String.format("%d", month)
val sDate = if (day < 10) String.format("0%d", day) else String.format("%d", day)
return year.toString() + sMonth + sDate
}
}
複製代碼
接下來咱們就能夠構建Adapter所須要的list了
val calendar = Calendar.getInstance()
calendar.add(Calendar.MONTH, -MAX_MONTH_COUNT + 1)
for (i in 0 until MAX_MONTH_COUNT) {
val year = calendar.get(Calendar.YEAR)
val month = calendar.get(Calendar.MONTH) + 1
val bean = MonthInfoBean(year, month)
monthList.add(bean)
calendar.add(Calendar.MONTH, 1)
}
複製代碼
MAX_MONTH_COUNT指的是最多顯示的月份,而後把須要顯示的月份的數據add到Calendar的實例內,monthList是須要展現的月份的MonthBean的List,以後再循環給monthList內添加dateList,同時經過Calendar的計算,去添加月份初始以及月份結束的空白的佔位符,代碼較長,具體的代碼以後能夠看附上的github連接。 經過上面的循環,咱們能夠構造出一個含有空白佔位符的每日的List,以後咱們再給它添加title,做爲每月份的title。
完成了List的建立,咱們就能夠建立對應的Adapter以及Decoration。Adapter內咱們須要區分View的種類,月份的title,空白日期的佔位符,以及顯示每日的數據。
Adapter內咱們能夠定義三種ViewType,去繪製不一樣的item。分割線和懸浮的title只須要重寫RecyclerView.ItemDecoration內的onDrawOver函數,便可作到,下面是大體代碼
override fun onDrawOver(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
super.onDrawOver(c, parent, state)
val manager = parent.layoutManager as GridLayoutManager
val position = manager.findFirstVisibleItemPosition()
if (position == RecyclerView.NO_POSITION) {
return
}
val viewHolder = parent.findViewHolderForAdapterPosition(position)
val item = viewHolder?.itemView
var flag = false
// 判斷下一個title是否滑動上來了
if (isLast(position) && null != item) {
if (item.height + item.top < top) {
c.save()
flag = true
c.translate(0f, (item.height + item.top - top).toFloat())
}
}
// 繪製title
val rect = RectF(
0f,
parent.paddingTop.toFloat(),
parent.right.toFloat(),
(parent.paddingTop + top).toFloat())
c.drawRect(rect, mPaint)
c.drawText(mCallBack(position),
rect.centerX(),
rect.centerY() + mTopPadding,
mTextPaint)
if (flag) {
c.restore()
}
}
複製代碼
mCallBack是根據position取對應item的分組的回調函數,isLast是判斷當前的位置是否是處於最後一排。 以後將Adapter和Decoration加到對應的RecyclerView內就能夠完成了。
大體效果以下gif
這種方式實現的日曆,功能比較簡單,也沒有特別多的擴展功能,並且由於使用的是RecyclerView,若是須要顯示的數據多了,代碼性能是不太OK的,並且構造日曆的部分的代碼是循環套循環,時間複雜度上也是很是低效的。也就是摳腳寫法,代碼寫的也挺簡陋,具體細節能夠參考github連接,歡迎各位拿去修改使用,提出各類意見啥的。
最後放上項目的github地址 連接
參考博客和項目