基于Jetpack Compose实现Android谷歌日历日视图拖拽拉伸功能遇阻求助
Android谷歌日历日视图拖拽与拉伸实现方案
数据模型设计(核心避坑点)
不要通过槽位索引绑定事件,改用独立事件实体存储起止槽位:
data class CalendarEvent( val id: String, // 唯一标识,避免操作混淆 var startSlot: Int, // 起始槽位索引(0=00:00,1=00:30...47=23:30) var endSlot: Int, // 结束槽位索引 val title: String, val color: Int )
用MutableList<CalendarEvent>维护所有事件,每个事件独立管理自身的时间范围,彻底避免槽位索引导致的事件合并问题。
整体拖拽逻辑(上下移动时间段)
- 触摸事件触发时,通过坐标找到当前操作的
CalendarEvent,记录其初始startSlot和endSlot。 - 根据触摸移动的距离计算偏移的槽位数
deltaSlots(比如每移动一个槽位高度,deltaSlots±1)。 - 更新事件的起止槽位:
val newStart = event.startSlot + deltaSlots val newEnd = event.endSlot + deltaSlots // 边界校验:不能超出当天的时间范围 if (newStart >= 0 && newEnd <= 47) { event.startSlot = newStart event.endSlot = newEnd } - 刷新视图(用DiffUtil优化RecyclerView刷新,避免全量刷新)。
拉伸时长逻辑(解决合并问题的关键)
- 区分拉伸区域:触摸点在事件顶部1/4区域时拉伸起始时间,底部1/4区域时拉伸结束时间。
- 拉伸时仅修改对应槽位,保持另一槽位不变:
- 拉伸结束时间(延长/缩短时长):
val newEndSlot = calculateNewSlotFromTouchY(touchY) // 确保时长至少为1个槽位(30分钟) if (newEndSlot > event.startSlot && newEndSlot <= 47) { event.endSlot = newEndSlot } - 拉伸起始时间:
val newStartSlot = calculateNewSlotFromTouchY(touchY) if (newStartSlot < event.endSlot && newStartSlot >= 0) { event.startSlot = newStartSlot }
- 拉伸结束时间(延长/缩短时长):
- 核心注意:拉伸过程中只修改当前操作事件的属性,绝不修改其他事件的起止槽位,从根源避免两个事件被错误合并。
视图渲染逻辑
绘制事件时,根据事件的startSlot和endSlot计算位置和高度:
- 每个槽位的高度固定(比如80dp),事件顶部坐标 =
startSlot * slotHeight - 事件高度 =
(endSlot - startSlot) * slotHeight
触摸事件处理的细节
- ACTION_DOWN时,遍历事件列表找到触摸点命中的事件,保存其
id作为当前操作目标,后续ACTION_MOVE仅针对该事件。 - 处理触摸时加入防抖逻辑,避免微小移动触发不必要的槽位更新。
内容的提问来源于stack exchange,提问作者Jahangir Jadi
相关产品推荐
相关产品推荐

