初学Compose肯定要做些东西来熟悉,接下来我来分享我做的一个简易日历。
(最新更新:Android Compose 日期选择器)
首先推荐一个网站,Accompanist是一组库,旨在用开发人员通常需要但尚不可用的功能来补充[Jetpack Compose。]。https://google.github.io/accompanist/pager/
协奏曲pager implementation(“com.google.accompanist:accompanist-pager:0.21.3-beta”)
然后是一个库,帮助我们在compose里创建和管理viewmodel
implementation(‘androidx.lifecycle:lifecycle-viewmodel-compose:2.4.0’)
首先日历有年月日,但是我做的这个日历没有年份,你当然也可以自己添加。
建一个函数,day就是第几天,selectDay用来判断是否是被点击的那天,然后抛出去一个lambda,处理点击事件,lambda传入的day就是用来说明是第几天。
@Composable
fun Day(day: Int, selectDay: Int, dayClick: (Int) -> Unit) {
Text(text = day.toString(),
textAlign = TextAlign.Center,
color = if (day == selectDay) Color.Red else Color.White,
modifier = Modifier
.clickable {
dayClick(day)
})
}
然后写月份,首先我们写一个枚举类来定义月份,虽然java.time里有自带的,但是由于版本需要安卓8及以上,所以就自己写了,直接复制里面的内容修改就行。
enum class MonthEnum {
JANUARY,
FEBRUARY,
MARCH,
APRIL,
MAY,
JUNE,
JULY,
AUGUST,
SEPTEMBER,
OCTOBER,
NOVEMBER,
DECEMBER;
companion object {
private val enums = values()
fun of(month: Int): MonthEnum {
if (month < 1 || month > 12) {
throw RuntimeException("Invalid value for MonthOfYear: $month")
}
return enums[month - 1]
}
}
fun maxLength(): Int {
return when (this) {
FEBRUARY -> 29
APRIL, JUNE, SEPTEMBER, NOVEMBER -> 30
else -> 31
}
}
fun minLength(): Int {
return when (this) {
FEBRUARY -> 28
APRIL, JUNE, SEPTEMBER, NOVEMBER -> 30
else -> 31
}
}
fun length(leapYear: Boolean): Int {
return when (this) {
FEBRUARY -> if (leapYear) 29 else 28
APRIL, JUNE, SEPTEMBER, NOVEMBER -> 30
else -> 31
}
}
}
/**
*@param selectDay 动态更新
*@param dayClick 点击高亮
*LazyVerticalGrid是用来写网格列表,GridCells.Fixed(6)就是每行6个
*month.length(true) 返回月份的天数,true则代表闰年
*/
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun Month(
month: MonthEnum,
selectDay: Int,
dayClick: (Int) -> Unit
) {
Column(
modifier = Modifier
.fillMaxHeight()
.padding(top = 60.dp)
) {
Text(
text = month.name,
modifier = Modifier
// .background(Color.Red)
.fillMaxWidth(),
color = Color.White,
textAlign = TextAlign.Center
)
Log.i(TAG, "Month: ${month.name} ")
LazyVerticalGrid(cells = GridCells.Fixed(6)) {
items(month.length(true)) { index ->
Day(day = index + 1, selectDay, dayClick)
}
}
}
}
接下来是最外层的viewpager,move用于页面切换时需要做的事情
@OptIn(ExperimentalPagerApi::class)
@Composable
fun Calendar(selectDay: Int, move: (Int) -> Unit, dayClick: (Int) -> Unit) {
//对页面更改做出反应¶
//每当所选页面发生更改时,都会更新PagerState.currentPage属性。
val pagerState = rememberPagerState()
LaunchedEffect(pagerState) {
// 从读取 currentPage 的快照流中收集
snapshotFlow { pagerState.currentPage }
.collect {
move(it)
}
}
HorizontalPager(
count = 12, state = pagerState, modifier = Modifier
.paint(
painter = painterResource(
id = R.drawable.background
), contentScale = ContentScale.Crop
)
.height(300.dp), verticalAlignment = Alignment.Top
) { index ->
Month(
month = MonthEnum.of(index + 1),
selectDay = selectDay,
dayClick = dayClick
)
}
}
最后是用法,selectDay是当前选中的号数,move:因为选择的号数可能是大于切换的下个月份的,导致不高亮显示,所以每次切换时默认高亮显示第一天,最外层lambda是处理day的点击事件。
setContent {
WeChat_ComposeTheme {
val viewModel: MainViewModel = viewModel()
Calendar(selectDay = viewModel.selectDay, move = {
viewModel.selectDay = 1
}) {
viewModel.selectDay = it
}
}
}
viewModel类里的selectDay用作动态更新,MutableState 类是一个单一的值持有者,其读取和写入由 Compose 观察,当值发生变化会更新ui。
class MainViewModel : ViewModel() {
var selectDay by mutableStateOf(1)
}