有个日历的需求, 自己实现一下简单的
![](https://i-blog.csdnimg.cn/blog_migrate/86eb846e74e9942f0020110f5aa30579.png)
- 生成数据
private fun initData() {
val listOfCalendar = mutableListOf<CalendarData>()
val calendar = Calendar.getInstance()
val todayYear = calendar.get(Calendar.YEAR)
val todayMonth = calendar.get(Calendar.MONTH)
val todayDay = calendar.get(Calendar.DAY_OF_MONTH)
calendar.firstDayOfWeek = Calendar.MONDAY // 设置一周的第一天为周一
(0..100).forEach { monthIndex ->
if (monthIndex > 0) {
calendar.add(Calendar.MONTH, 1)
} else {
calendar.add(Calendar.MONTH, 0)
}
val year = calendar[Calendar.YEAR]
val month = calendar.get(Calendar.MONTH)
val calendarData = CalendarData(
year = year,
month = month + 1
)
calendar[year, month] = 1 // 设置日期为月份的第一天
val list = mutableListOf<MonthData>()
for (dayOfMonth in 1..calendar.getActualMaximum(Calendar.DAY_OF_MONTH)) {
Log.d("TAG,", "dayOfMonth:::$dayOfMonth")
calendar[year, month] = dayOfMonth
val dayOfWeek = calendar[Calendar.DAY_OF_WEEK]
XLogger.d("${month + 1} 月 第" + dayOfMonth + "天是星期" + (dayOfWeek - 1))
list.add(
MonthData(
year = year,
month = month + 1,
day = dayOfMonth,
week = if ((dayOfWeek - 1) == 0) 7 else (dayOfWeek - 1),
weekOfYear = calendar.get(Calendar.WEEK_OF_YEAR),
isCurrentDay = year == todayYear && month == todayMonth && dayOfMonth == todayDay
)
)
}
listOfCalendar.add(calendarData.copy(list = list))
}
}
- 界面的绘制
入口
@OptIn(ExperimentalFoundationApi::class, ExperimentalTextApi::class)
@Composable
fun Calendar(homeViewModel: HomeViewModel = viewModel()) {
val homeUiState = homeViewModel.homeUiState.collectAsState().value
val weekTitleList = homeUiState.weekTitleList
val pagerState = rememberPagerState()
val textMeasurerAndTextSize = getTextMeasurerAndTextSize()
Column(modifier = Modifier.fillMaxSize()) {
XLogger.d("==================>Calendar")
YearAndMonth(homeViewModel, pagerState)
//星期
WeekRow(weekTitleList)
//日历信息
CalendarPager(homeViewModel, pagerState, textMeasurerAndTextSize)
}
}
星期信息
/**
* 星期信息
*/
@Composable
fun WeekRow(weekTitleList: List<String>) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 4.dp)
) {
weekTitleList.forEachIndexed { _, s ->
Text(
text = s,
modifier = Modifier.weight(1f),
textAlign = TextAlign.Center,
fontSize = 14.sp,
color = Color.Black,
fontWeight = FontWeight.Medium
)
}
}
}
/**
* 年和月
*/
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun YearAndMonth(homeViewModel: HomeViewModel, pagerState: PagerState) {
val homeUiState = homeViewModel.homeUiState.collectAsState().value
val calendarList = homeUiState.calendarList
Row(
modifier = Modifier
.fillMaxWidth()
.padding(start = 12.dp),
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = "${calendarList[pagerState.currentPage].year}年",
fontSize = 30.sp,
fontWeight = FontWeight.SemiBold,
color = Color.Black
)
Text(
text = "${calendarList[pagerState.currentPage].month}月",
fontSize = 30.sp,
fontWeight = FontWeight.SemiBold,
color = Color.Black
)
}
}
获取 TextMeasurer 测量文字的高度
/**
* 获取 TextMeasurer 测量文字的高度
*/
@OptIn(ExperimentalTextApi::class)
@Composable
fun getTextMeasurerAndTextSize(): Pair<TextMeasurer, IntSize> {
val textMeasurer = rememberTextMeasurer()
val textLayoutResult: TextLayoutResult =
textMeasurer.measure(
text = "9",
style = TextStyle(
textAlign = TextAlign.Center,
color = Color.Black,
fontSize = 14.sp,
fontWeight = FontWeight.Medium,
)
)
val textSize = textLayoutResult.size
return Pair(textMeasurer, textSize)
}
日历的滑动页面
/**
* 日历的滑动页面
*/
@OptIn(ExperimentalFoundationApi::class, ExperimentalTextApi::class)
@Composable
fun CalendarPager(
homeViewModel: HomeViewModel,
pagerState: PagerState,
textMeasurerAndTextSize: Pair<TextMeasurer, IntSize>
) {
val homeUiState = homeViewModel.homeUiState.collectAsState().value
val calendarList = homeUiState.calendarList
//size 最大的列 找出最长的那个决定高度
var maxColumn by remember {
mutableStateOf(1)
}
val screenWidthDp = LocalConfiguration.current.screenWidthDp
val paddingPx = 2
val textMeasurer = textMeasurerAndTextSize.first
val textSize = textMeasurerAndTextSize.second
LaunchedEffect(key1 = pagerState.currentPage, block = {
XLogger.d("=======>${pagerState.currentPage}")
//TODO:监听 滑动到<=2 或 size-2 的时候追加 日期数据
})
XLogger.d("==================>CalendarPager")
HorizontalPager(
pageCount = calendarList.size,
state = pagerState,
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight(),
) {
val calendarData = calendarList[it]
//绘制整个月的数据
Canvas(modifier = Modifier
.fillMaxWidth()
.height((maxColumn * screenWidthDp / 7f).dp)
// .pointerInput(Unit) {
// detectTapGestures(onTap = {
// //TODO:点击 定位哪一天
// })
// detectDragGestures { change, dragAmount ->
// //TODO:竖直方向滑动 进入周历模式
// }
// }
// .background(color = Color.Blue)
, onDraw = {
val perWidthWithPadding = this.size.width / 7f
//第一条数据是周几
val firstWeek = calendarData.list[0].week
//计算最大的列 跨度 一年的几周就是最大的列
maxColumn = calendarData.list.groupBy { monthData ->
monthData.weekOfYear
}.size
//根据星期 进行分组
val groupedData = calendarData.list.groupBy { monthData ->
monthData.week
}
//竖着按照列进行 绘制
groupedData.forEach { (week, monthDataList) ->
monthDataList.forEachIndexed { index, monthData ->
//按照列写的数据
// drawRoundRect(
// color = Color.Magenta,
// topLeft = Offset(
// (week - 1) * perWidthWithPadding + paddingPx,
// (index + if (week >= firstWeek) 0 else 1) * perWidthWithPadding - paddingPx
// ),
// size = Size(
// perWidthWithPadding - 2 * paddingPx,
// perWidthWithPadding - 2 * paddingPx
// )
// )
drawText(
textMeasurer = textMeasurer,
text = "${monthData.day}",
size = Size(
perWidthWithPadding - 2 * paddingPx,
perWidthWithPadding - 2 * paddingPx
),
topLeft = Offset(
(week - 1) * perWidthWithPadding,
(index + if (week >= firstWeek) 0 else 1) * perWidthWithPadding
//定位到中间位置
+ perWidthWithPadding * 0.5f
//减去文字的高度
- textSize.height / 2f
),
style = TextStyle(
textAlign = TextAlign.Center,
color = if (monthData.isCurrentDay) Color.Red else Color.Black,
fontSize = 14.sp,
fontWeight = FontWeight.Medium,
)
)
//当天画圆背景
if (monthData.isCurrentDay) {
drawCircle(
color = Color.LightGray.copy(0.5f),
radius = (perWidthWithPadding - 2 * paddingPx) / 2f,
center = Offset(
(week - 1) * perWidthWithPadding + paddingPx + perWidthWithPadding / 2f,
(index + if (week >= firstWeek) 0 else 1) * perWidthWithPadding - paddingPx + perWidthWithPadding / 2f
),
)
}
}
}
})
}
}