Skip to content

Commit

Permalink
Add support for additional drawers
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
RemiNV committed Nov 28, 2020
1 parent 88638dc commit 2308e3b
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 27 deletions.
39 changes: 28 additions & 11 deletions core/src/main/java/com/alamkanak/weekview/CalendarRenderer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@ import android.graphics.Paint
import android.graphics.RectF
import android.text.StaticLayout
import androidx.collection.ArrayMap
import com.alamkanak.weekview.WeekView.DrawBase
import java.util.Calendar
import kotlin.math.max
import kotlin.math.min

internal class CalendarRenderer(
viewState: ViewState,
private val viewState: ViewState,
eventChipsCacheProvider: EventChipsCacheProvider
) : Renderer {

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

drawAdditional(canvas, DrawBase.CANVAS)
for (drawer in drawers) {
drawer.draw(canvas)
drawer.draw(canvas, viewState)
drawAdditional(canvas, drawer.base)
}
drawAdditional(canvas, DrawBase.TOP)
}

private fun drawAdditional(canvas: Canvas, base: DrawBase) {
val drawers = viewState.additionalDrawers[base]
if (drawers.isNullOrEmpty()) return

val saveCount = canvas.save()
drawers.forEach { it.draw(canvas, viewState) }
canvas.restoreToCount(saveCount)
}
}

Expand Down Expand Up @@ -107,9 +120,10 @@ private class SingleEventsUpdater(

private class DayBackgroundDrawer(
private val viewState: ViewState
) : Drawer {
) : WeekView.Drawer {
override val base = DrawBase.BACKGROUND

override fun draw(canvas: Canvas) {
override fun draw(canvas: Canvas, bounds: WeekView.DrawBounds) {
canvas.drawInBounds(viewState.calendarGridBounds) {
viewState.dateRangeWithStartPixels.forEach { (date, startPixel) ->
drawDayBackground(date, startPixel, canvas)
Expand Down Expand Up @@ -167,9 +181,10 @@ private class DayBackgroundDrawer(

private class BackgroundGridDrawer(
private val viewState: ViewState
) : Drawer {
) : WeekView.Drawer {
override val base = DrawBase.GRID

override fun draw(canvas: Canvas) {
override fun draw(canvas: Canvas, bounds: WeekView.DrawBounds) {
canvas.drawInBounds(viewState.calendarGridBounds) {
if (viewState.showHourSeparators) {
drawHourLines()
Expand Down Expand Up @@ -201,7 +216,7 @@ private class BackgroundGridDrawer(
private fun Canvas.drawHourLine(hour: Int) {
val heightOfHour = (viewState.hourHeight * (hour - viewState.minHour))
val verticalOffset = viewState.headerHeight + viewState.currentOrigin.y + heightOfHour
val horizontalOffset = if (viewState.isLtr) viewState.timeColumnWidth else 0f
val horizontalOffset = viewState.calendarGridBounds.left

drawHorizontalLine(
verticalOffset = verticalOffset,
Expand All @@ -216,11 +231,12 @@ private class SingleEventsDrawer(
private val viewState: ViewState,
private val chipsCacheProvider: EventChipsCacheProvider,
private val eventLabels: ArrayMap<String, StaticLayout>
) : Drawer {
) : WeekView.Drawer {
override val base = DrawBase.EVENTS

private val eventChipDrawer = EventChipDrawer(viewState)

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

private class NowLineDrawer(
private val viewState: ViewState
) : Drawer {
) : WeekView.Drawer {
override val base = DrawBase.NOWLINE

override fun draw(canvas: Canvas) {
override fun draw(canvas: Canvas, bounds: WeekView.DrawBounds) {
if (viewState.showNowLine.not()) {
return
}
Expand Down
6 changes: 3 additions & 3 deletions core/src/main/java/com/alamkanak/weekview/HeaderRenderer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ private class HeaderUpdater(
private class DateLabelsDrawer(
private val viewState: ViewState,
private val dateLabelLayouts: SparseArray<StaticLayout>
) : Drawer {
) : DrawerInternal {

override fun draw(canvas: Canvas) {
canvas.drawInBounds(viewState.headerBounds) {
Expand Down Expand Up @@ -240,7 +240,7 @@ private class AllDayEventsUpdater(
internal class AllDayEventsDrawer(
private val viewState: ViewState,
private val allDayEventLayouts: ArrayMap<EventChip, StaticLayout>
) : Drawer {
) : DrawerInternal {

private val eventChipDrawer = EventChipDrawer(viewState)

Expand Down Expand Up @@ -319,7 +319,7 @@ internal class AllDayEventsDrawer(
private class HeaderDrawer(
context: Context,
private val viewState: ViewState
) : Drawer {
) : DrawerInternal {

private val upArrow: Drawable by lazy {
checkNotNull(ContextCompat.getDrawable(context, R.drawable.ic_arrow_up))
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/java/com/alamkanak/weekview/Renderers.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ internal interface Updater {
fun update()
}

internal interface Drawer {
internal interface DrawerInternal {
fun draw(canvas: Canvas)
}

Expand Down
26 changes: 14 additions & 12 deletions core/src/main/java/com/alamkanak/weekview/ViewState.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,21 @@ import android.graphics.Typeface
import android.os.Build
import android.text.TextPaint
import android.view.View
import java.util.Calendar
import java.util.*
import kotlin.math.ceil
import kotlin.math.max
import kotlin.math.min

typealias DateFormatter = (Calendar) -> String
typealias TimeFormatter = (Int) -> String

internal class ViewState {
internal class ViewState : WeekView.DrawBounds {

// View
var viewWidth: Int = 0
var viewHeight: Int = 0
override var viewWidth: Int = 0
override var viewHeight: Int = 0

var isLtr: Boolean = true
override var isLtr: Boolean = true

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

// Calendar configuration
var numberOfVisibleDays: Int = 3
override var numberOfVisibleDays: Int = 3
var restoreNumberOfVisibleDays: Boolean = true
var showFirstDayOfWeekFirst: Boolean = false
var showCurrentTimeFirst: Boolean = false
Expand All @@ -64,7 +64,7 @@ internal class ViewState {
var eventMarginVertical: Int = 0
var singleDayHorizontalPadding: Int = 0

var hourHeight: Float = 0f
override var hourHeight: Float = 0f
var minHourHeight: Float = 0f
var maxHourHeight: Float = 0f
var effectiveMinHourHeight: Float = 0f
Expand All @@ -90,11 +90,13 @@ internal class ViewState {
@Deprecated("No longer used")
var scrollDuration: Int = 0

var minHour: Int = 0
var maxHour: Int = 24
override var minHour: Int = 0
override var maxHour: Int = 24

var typeface: Typeface = Typeface.DEFAULT

var additionalDrawers: Map<WeekView.DrawBase, List<WeekView.Drawer>> = emptyMap()

var timeColumnWidth: Float = 0f
var timeColumnTextHeight: Float = 0f

Expand Down Expand Up @@ -134,13 +136,13 @@ internal class ViewState {

// In LTR: Dates in the past have origin.x > 0, dates in the future have origin.x < 0
// In RTL: Dates in the past have origin.x < 0, dates in the future have origin.x > 0
var currentOrigin = PointF(0f, 0f)
override var currentOrigin = PointF(0f, 0f)

val headerBackgroundPaint = Paint()

val headerBackgroundWithShadowPaint = Paint()

val dayWidth: Float
override val dayWidth: Float
get() = (viewWidth - timeColumnWidth) / numberOfVisibleDays

val drawableDayWidth: Float
Expand Down Expand Up @@ -237,7 +239,7 @@ internal class ViewState {

private val _calendarGridBounds: RectF = RectF()

val calendarGridBounds: RectF
override val calendarGridBounds: RectF
get() = _calendarGridBounds.apply {
left = if (isLtr) timeColumnWidth else 0f
top = headerHeight
Expand Down
48 changes: 48 additions & 0 deletions core/src/main/java/com/alamkanak/weekview/WeekView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import android.annotation.SuppressLint
import android.content.Context
import android.content.res.Configuration
import android.graphics.Canvas
import android.graphics.PointF
import android.graphics.RectF
import android.graphics.Typeface
import android.os.Parcelable
Expand Down Expand Up @@ -1339,6 +1340,53 @@ class WeekView @JvmOverloads constructor(
invalidate()
}

/*
***********************************************************************************************
*
* Custom drawers
*
***********************************************************************************************
*/

/**
* Bounds available to [Drawer] to draw on the [Canvas] during rendering.
*/
@PublicApi
interface DrawBounds {
val calendarGridBounds: RectF
val currentOrigin: PointF
val viewWidth: Int
val viewHeight: Int
val dayWidth: Float
val hourHeight: Float
val isLtr: Boolean
}

@PublicApi
enum class DrawBase {
CANVAS, BACKGROUND, GRID, EVENTS, NOWLINE, TOP;
}

@PublicApi
interface Drawer {
fun draw(canvas: Canvas, bounds: DrawBounds)
val base: DrawBase
}

/**
* Additional drawers that will be draw on the view during rendering.
*
* Each drawer will draw on top of the layer specified by [Drawer.base]. Drawers with the same
* base will draw in the order specified in the list.
*/
@PublicApi
var additionalDrawers: List<Drawer>
get() = viewState.additionalDrawers.values.flatten()
set(value) {
viewState.additionalDrawers = value.groupBy { it.base }
invalidate()
}

/*
***********************************************************************************************
*
Expand Down

0 comments on commit 2308e3b

Please sign in to comment.