目錄
前言
由于只想單純的實現(xiàn)一個日歷藤巢,因此整體比較簡單竟纳,沒有過多的代碼撵溃,更方便捋清楚實現(xiàn)日歷的邏輯
效果展示
實現(xiàn)思路
首先日歷控件可以左右滑動因此我們選擇ViewPager來作為最外層容器,然后在頂上加上周日到周六的文字標題
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_weight="1"
android:textSize="15sp"
android:textColor="@color/black"
android:padding="5dp"
android:gravity="center"
android:text="日"
android:layout_width="0dp"
android:layout_height="wrap_content"/>
<TextView
android:layout_weight="1"
android:textSize="15sp"
android:gravity="center"
android:textColor="@color/black"
android:padding="5dp"
android:text="一"
android:layout_width="0dp"
android:layout_height="wrap_content"/>
<TextView
android:layout_weight="1"
android:textSize="15sp"
android:gravity="center"
android:textColor="@color/black"
android:padding="5dp"
android:text="二"
android:layout_width="0dp"
android:layout_height="wrap_content"/>
<TextView
android:layout_weight="1"
android:textSize="15sp"
android:gravity="center"
android:textColor="@color/black"
android:padding="5dp"
android:text="三"
android:layout_width="0dp"
android:layout_height="wrap_content"/>
<TextView
android:layout_weight="1"
android:textSize="15sp"
android:gravity="center"
android:textColor="@color/black"
android:padding="5dp"
android:text="四"
android:layout_width="0dp"
android:layout_height="wrap_content"/>
<TextView
android:layout_weight="1"
android:textSize="15sp"
android:gravity="center"
android:textColor="@color/black"
android:padding="5dp"
android:text="五"
android:layout_width="0dp"
android:layout_height="wrap_content"/>
<TextView
android:layout_weight="1"
android:textSize="15sp"
android:gravity="center"
android:textColor="@color/black"
android:padding="5dp"
android:text="六"
android:layout_width="0dp"
android:layout_height="wrap_content"/>
</LinearLayout>
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/vpContainer"
android:layout_width="match_parent"
android:layout_height="330dp"/>
</LinearLayout>
然后展示具體日期的可以看做是一個七列多行的網(wǎng)格
因此我們可以使用GridView來展示日期锥累,這里我用的是自定義的自適應高度的GridView
class WrapGridView : GridView {
constructor(context: Context?) : this(context, null)
constructor(context: Context?, attrs: AttributeSet?) : this(context, attrs, 0)
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
val heightSpec = if (layoutParams.height == LayoutParams.WRAP_CONTENT) {
MeasureSpec.makeMeasureSpec(Int.MAX_VALUE shr 2, MeasureSpec.AT_MOST)
} else {
heightMeasureSpec
}
// 這幾行代碼比較重要
super.onMeasure(widthMeasureSpec, heightSpec)
}
}
然后就是展示日期部分了缘挑,展示日期的時候我們只需要知道展示的月份有多少天和第一天是星期幾即可,每月前幾天的空白可以用空數(shù)據(jù)頂替
/**
* 獲取第一天為星期幾
*/
private fun getMonthOneDayWeek(): Int {
val a: Calendar = Calendar.getInstance()
a.set(Calendar.YEAR, year)
a.set(Calendar.MONTH, month)
a.set(Calendar.DATE, 1) //把日期設置為當月第一天
return a.get(Calendar.DAY_OF_WEEK)
}
/**
* 獲取當月有幾天
*/
private fun getMonthMaxDay(): Int {
val a: Calendar = Calendar.getInstance()
a.set(Calendar.YEAR, year)
a.set(Calendar.MONTH, month)
return a.getActualMaximum(Calendar.DAY_OF_MONTH)
}
val data = ArrayList<DateBean>()
//獲取第一天是星期幾然后計算出需要填充的空白數(shù)據(jù)桶略,這里使用0作為空白數(shù)據(jù)语淘,展示的時候判斷并顯示空
repeat(getMonthOneDayWeek() - 1){
//填充空白的
data.add(DateBean(0,0,0))
}
//填充日期數(shù)據(jù)
repeat(getMonthMaxDay()){
data.add(DateBean(year,month,it + 1))
}
這是日期實體類
data class DateBean(var year:Int,var month:Int,var day:Int)
最后的話就是關(guān)于添加多少頁展示數(shù)據(jù)的處理了,這里我設置的當前月之前會固定有200頁(也就是200個月)际歼,當前月之后默認有5頁(5個月)惶翻,當往后滑動頁面的時候會動態(tài)的添加新的頁面
private fun initListener(){
vpContainer.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
override fun onPageSelected(position: Int) {
currentPos = position
fragments[currentPos].arguments?.let {
val currentPageYear = it.getInt(CalendarFragment.YEAR) //當前頁的年
val currentPageMonth = it.getInt(CalendarFragment.MONTH) //當前頁的月
//展示的月需要加1(因為系統(tǒng)中的月是從0開始的)
onPageChangedCallBack?.onPageChanged(currentPageYear,currentPageMonth + 1)
}
//刷新下當前的數(shù)據(jù)
fragments[currentPos].refreshData()
if(currentPos + 3 > fragments.size){
addNextFragment()
}
}
})
}
/**
* 動態(tài)添加后面的日歷
*/
private fun addNextFragment() {
fragments[fragments.size-1].arguments?.let {
//獲取展示的最小的年和月
val minYear = it.getInt(CalendarFragment.YEAR)
val minMonth = it.getInt(CalendarFragment.MONTH)
//動態(tài)加兩個
for(i in minMonth + 1 until minMonth + 2){
var month = i
var year = minYear
if(i > 11){
month = i - 12
year = minYear + 1
}
val fragment = CalendarFragment(object :OnDateSelectCallBack{
override fun onDateSelect(year: Int, month: Int, day: Int) {
onDateSelectCallBack?.onDateSelect(year,month,day)
}
})
val arguments = Bundle()
arguments.putInt(CalendarFragment.YEAR,year)
arguments.putInt(CalendarFragment.MONTH,month)
fragment.arguments = arguments
fragments.add(fragment)
}
fragmentAdapter?.notifyDataSetChanged()
}
}
更加詳細的代碼請參考源碼