Skip to content

Commit 2308e3b

Browse files
committed
Add support for additional drawers
Additional drawers allow users to customize the appearance of the calendar by drawing additional items on any layer (background, on top of the grid, on top of everything, etc). This allows supporting customization use-cases that are too uncommon to implement and support in the base library.
1 parent 88638dc commit 2308e3b

File tree

5 files changed

+94
-27
lines changed

5 files changed

+94
-27
lines changed

core/src/main/java/com/alamkanak/weekview/CalendarRenderer.kt

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@ import android.graphics.Paint
55
import android.graphics.RectF
66
import android.text.StaticLayout
77
import androidx.collection.ArrayMap
8+
import com.alamkanak.weekview.WeekView.DrawBase
89
import java.util.Calendar
910
import kotlin.math.max
1011
import kotlin.math.min
1112

1213
internal class CalendarRenderer(
13-
viewState: ViewState,
14+
private val viewState: ViewState,
1415
eventChipsCacheProvider: EventChipsCacheProvider
1516
) : Renderer {
1617

@@ -29,9 +30,21 @@ internal class CalendarRenderer(
2930
override fun render(canvas: Canvas) {
3031
eventsUpdater.update()
3132

33+
drawAdditional(canvas, DrawBase.CANVAS)
3234
for (drawer in drawers) {
33-
drawer.draw(canvas)
35+
drawer.draw(canvas, viewState)
36+
drawAdditional(canvas, drawer.base)
3437
}
38+
drawAdditional(canvas, DrawBase.TOP)
39+
}
40+
41+
private fun drawAdditional(canvas: Canvas, base: DrawBase) {
42+
val drawers = viewState.additionalDrawers[base]
43+
if (drawers.isNullOrEmpty()) return
44+
45+
val saveCount = canvas.save()
46+
drawers.forEach { it.draw(canvas, viewState) }
47+
canvas.restoreToCount(saveCount)
3548
}
3649
}
3750

@@ -107,9 +120,10 @@ private class SingleEventsUpdater(
107120

108121
private class DayBackgroundDrawer(
109122
private val viewState: ViewState
110-
) : Drawer {
123+
) : WeekView.Drawer {
124+
override val base = DrawBase.BACKGROUND
111125

112-
override fun draw(canvas: Canvas) {
126+
override fun draw(canvas: Canvas, bounds: WeekView.DrawBounds) {
113127
canvas.drawInBounds(viewState.calendarGridBounds) {
114128
viewState.dateRangeWithStartPixels.forEach { (date, startPixel) ->
115129
drawDayBackground(date, startPixel, canvas)
@@ -167,9 +181,10 @@ private class DayBackgroundDrawer(
167181

168182
private class BackgroundGridDrawer(
169183
private val viewState: ViewState
170-
) : Drawer {
184+
) : WeekView.Drawer {
185+
override val base = DrawBase.GRID
171186

172-
override fun draw(canvas: Canvas) {
187+
override fun draw(canvas: Canvas, bounds: WeekView.DrawBounds) {
173188
canvas.drawInBounds(viewState.calendarGridBounds) {
174189
if (viewState.showHourSeparators) {
175190
drawHourLines()
@@ -201,7 +216,7 @@ private class BackgroundGridDrawer(
201216
private fun Canvas.drawHourLine(hour: Int) {
202217
val heightOfHour = (viewState.hourHeight * (hour - viewState.minHour))
203218
val verticalOffset = viewState.headerHeight + viewState.currentOrigin.y + heightOfHour
204-
val horizontalOffset = if (viewState.isLtr) viewState.timeColumnWidth else 0f
219+
val horizontalOffset = viewState.calendarGridBounds.left
205220

206221
drawHorizontalLine(
207222
verticalOffset = verticalOffset,
@@ -216,11 +231,12 @@ private class SingleEventsDrawer(
216231
private val viewState: ViewState,
217232
private val chipsCacheProvider: EventChipsCacheProvider,
218233
private val eventLabels: ArrayMap<String, StaticLayout>
219-
) : Drawer {
234+
) : WeekView.Drawer {
235+
override val base = DrawBase.EVENTS
220236

221237
private val eventChipDrawer = EventChipDrawer(viewState)
222238

223-
override fun draw(canvas: Canvas) {
239+
override fun draw(canvas: Canvas, bounds: WeekView.DrawBounds) {
224240
canvas.drawInBounds(viewState.calendarGridBounds) {
225241
for (date in viewState.dateRange) {
226242
drawEventsForDate(date)
@@ -241,9 +257,10 @@ private class SingleEventsDrawer(
241257

242258
private class NowLineDrawer(
243259
private val viewState: ViewState
244-
) : Drawer {
260+
) : WeekView.Drawer {
261+
override val base = DrawBase.NOWLINE
245262

246-
override fun draw(canvas: Canvas) {
263+
override fun draw(canvas: Canvas, bounds: WeekView.DrawBounds) {
247264
if (viewState.showNowLine.not()) {
248265
return
249266
}

core/src/main/java/com/alamkanak/weekview/HeaderRenderer.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ private class HeaderUpdater(
136136
private class DateLabelsDrawer(
137137
private val viewState: ViewState,
138138
private val dateLabelLayouts: SparseArray<StaticLayout>
139-
) : Drawer {
139+
) : DrawerInternal {
140140

141141
override fun draw(canvas: Canvas) {
142142
canvas.drawInBounds(viewState.headerBounds) {
@@ -240,7 +240,7 @@ private class AllDayEventsUpdater(
240240
internal class AllDayEventsDrawer(
241241
private val viewState: ViewState,
242242
private val allDayEventLayouts: ArrayMap<EventChip, StaticLayout>
243-
) : Drawer {
243+
) : DrawerInternal {
244244

245245
private val eventChipDrawer = EventChipDrawer(viewState)
246246

@@ -319,7 +319,7 @@ internal class AllDayEventsDrawer(
319319
private class HeaderDrawer(
320320
context: Context,
321321
private val viewState: ViewState
322-
) : Drawer {
322+
) : DrawerInternal {
323323

324324
private val upArrow: Drawable by lazy {
325325
checkNotNull(ContextCompat.getDrawable(context, R.drawable.ic_arrow_up))

core/src/main/java/com/alamkanak/weekview/Renderers.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ internal interface Updater {
66
fun update()
77
}
88

9-
internal interface Drawer {
9+
internal interface DrawerInternal {
1010
fun draw(canvas: Canvas)
1111
}
1212

core/src/main/java/com/alamkanak/weekview/ViewState.kt

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,21 +8,21 @@ import android.graphics.Typeface
88
import android.os.Build
99
import android.text.TextPaint
1010
import android.view.View
11-
import java.util.Calendar
11+
import java.util.*
1212
import kotlin.math.ceil
1313
import kotlin.math.max
1414
import kotlin.math.min
1515

1616
typealias DateFormatter = (Calendar) -> String
1717
typealias TimeFormatter = (Int) -> String
1818

19-
internal class ViewState {
19+
internal class ViewState : WeekView.DrawBounds {
2020

2121
// View
22-
var viewWidth: Int = 0
23-
var viewHeight: Int = 0
22+
override var viewWidth: Int = 0
23+
override var viewHeight: Int = 0
2424

25-
var isLtr: Boolean = true
25+
override var isLtr: Boolean = true
2626

2727
// Calendar state
2828
var firstVisibleDate: Calendar = today()
@@ -38,7 +38,7 @@ internal class ViewState {
3838
val dateRangeWithStartPixels: MutableList<Pair<Calendar, Float>> = mutableListOf()
3939

4040
// Calendar configuration
41-
var numberOfVisibleDays: Int = 3
41+
override var numberOfVisibleDays: Int = 3
4242
var restoreNumberOfVisibleDays: Boolean = true
4343
var showFirstDayOfWeekFirst: Boolean = false
4444
var showCurrentTimeFirst: Boolean = false
@@ -64,7 +64,7 @@ internal class ViewState {
6464
var eventMarginVertical: Int = 0
6565
var singleDayHorizontalPadding: Int = 0
6666

67-
var hourHeight: Float = 0f
67+
override var hourHeight: Float = 0f
6868
var minHourHeight: Float = 0f
6969
var maxHourHeight: Float = 0f
7070
var effectiveMinHourHeight: Float = 0f
@@ -90,11 +90,13 @@ internal class ViewState {
9090
@Deprecated("No longer used")
9191
var scrollDuration: Int = 0
9292

93-
var minHour: Int = 0
94-
var maxHour: Int = 24
93+
override var minHour: Int = 0
94+
override var maxHour: Int = 24
9595

9696
var typeface: Typeface = Typeface.DEFAULT
9797

98+
var additionalDrawers: Map<WeekView.DrawBase, List<WeekView.Drawer>> = emptyMap()
99+
98100
var timeColumnWidth: Float = 0f
99101
var timeColumnTextHeight: Float = 0f
100102

@@ -134,13 +136,13 @@ internal class ViewState {
134136

135137
// In LTR: Dates in the past have origin.x > 0, dates in the future have origin.x < 0
136138
// In RTL: Dates in the past have origin.x < 0, dates in the future have origin.x > 0
137-
var currentOrigin = PointF(0f, 0f)
139+
override var currentOrigin = PointF(0f, 0f)
138140

139141
val headerBackgroundPaint = Paint()
140142

141143
val headerBackgroundWithShadowPaint = Paint()
142144

143-
val dayWidth: Float
145+
override val dayWidth: Float
144146
get() = (viewWidth - timeColumnWidth) / numberOfVisibleDays
145147

146148
val drawableDayWidth: Float
@@ -237,7 +239,7 @@ internal class ViewState {
237239

238240
private val _calendarGridBounds: RectF = RectF()
239241

240-
val calendarGridBounds: RectF
242+
override val calendarGridBounds: RectF
241243
get() = _calendarGridBounds.apply {
242244
left = if (isLtr) timeColumnWidth else 0f
243245
top = headerHeight

core/src/main/java/com/alamkanak/weekview/WeekView.kt

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import android.annotation.SuppressLint
44
import android.content.Context
55
import android.content.res.Configuration
66
import android.graphics.Canvas
7+
import android.graphics.PointF
78
import android.graphics.RectF
89
import android.graphics.Typeface
910
import android.os.Parcelable
@@ -1339,6 +1340,53 @@ class WeekView @JvmOverloads constructor(
13391340
invalidate()
13401341
}
13411342

1343+
/*
1344+
***********************************************************************************************
1345+
*
1346+
* Custom drawers
1347+
*
1348+
***********************************************************************************************
1349+
*/
1350+
1351+
/**
1352+
* Bounds available to [Drawer] to draw on the [Canvas] during rendering.
1353+
*/
1354+
@PublicApi
1355+
interface DrawBounds {
1356+
val calendarGridBounds: RectF
1357+
val currentOrigin: PointF
1358+
val viewWidth: Int
1359+
val viewHeight: Int
1360+
val dayWidth: Float
1361+
val hourHeight: Float
1362+
val isLtr: Boolean
1363+
}
1364+
1365+
@PublicApi
1366+
enum class DrawBase {
1367+
CANVAS, BACKGROUND, GRID, EVENTS, NOWLINE, TOP;
1368+
}
1369+
1370+
@PublicApi
1371+
interface Drawer {
1372+
fun draw(canvas: Canvas, bounds: DrawBounds)
1373+
val base: DrawBase
1374+
}
1375+
1376+
/**
1377+
* Additional drawers that will be draw on the view during rendering.
1378+
*
1379+
* Each drawer will draw on top of the layer specified by [Drawer.base]. Drawers with the same
1380+
* base will draw in the order specified in the list.
1381+
*/
1382+
@PublicApi
1383+
var additionalDrawers: List<Drawer>
1384+
get() = viewState.additionalDrawers.values.flatten()
1385+
set(value) {
1386+
viewState.additionalDrawers = value.groupBy { it.base }
1387+
invalidate()
1388+
}
1389+
13421390
/*
13431391
***********************************************************************************************
13441392
*

0 commit comments

Comments
 (0)