From ba60de41c75cfefe712f7e8fccd7f730ed8461cf Mon Sep 17 00:00:00 2001 From: Oleh Date: Thu, 22 Jun 2023 23:24:07 +0300 Subject: [PATCH 1/7] Move logic related to rendering and managing weekday row state out of RangeCalendarGridView. Move logic of extracting weekdays out of CalendarResources --- .../rangecalendar/WeekdayDataTests.kt | 29 +++++ .../rangecalendar/CalendarResources.kt | 60 +++-------- .../rangecalendar/RangeCalendarGridView.kt | 100 +++--------------- .../RangeCalendarPagerAdapter.kt | 2 - .../pelmenstar1/rangecalendar/WeekdayData.kt | 67 ++++++++++++ .../rangecalendar/WeekdayMeasureHelper.kt | 20 ++++ .../pelmenstar1/rangecalendar/WeekdayRow.kt | 79 ++++++++++++++ 7 files changed, 229 insertions(+), 128 deletions(-) create mode 100644 library/src/androidTest/java/com/github/pelmenstar1/rangecalendar/WeekdayDataTests.kt create mode 100644 library/src/main/java/com/github/pelmenstar1/rangecalendar/WeekdayData.kt create mode 100644 library/src/main/java/com/github/pelmenstar1/rangecalendar/WeekdayMeasureHelper.kt create mode 100644 library/src/main/java/com/github/pelmenstar1/rangecalendar/WeekdayRow.kt diff --git a/library/src/androidTest/java/com/github/pelmenstar1/rangecalendar/WeekdayDataTests.kt b/library/src/androidTest/java/com/github/pelmenstar1/rangecalendar/WeekdayDataTests.kt new file mode 100644 index 0000000..0f1f093 --- /dev/null +++ b/library/src/androidTest/java/com/github/pelmenstar1/rangecalendar/WeekdayDataTests.kt @@ -0,0 +1,29 @@ +package com.github.pelmenstar1.rangecalendar + +import android.os.Build +import androidx.test.ext.junit.runners.AndroidJUnit4 +import org.junit.Test +import org.junit.runner.RunWith +import java.util.Locale +import kotlin.test.assertContentEquals + +@RunWith(AndroidJUnit4::class) +class WeekdayDataTests { + @Suppress("UNCHECKED_CAST") + @Test + fun getTest() { + val locale = Locale.ENGLISH + val data = WeekdayData.get(locale) + + val expectedWeekdays: Array = if (Build.VERSION.SDK_INT >= 24) { + arrayOf( + "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun", + "M", "T", "W", "T", "F", "S", "S", + ) + } else { + arrayOf("Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun") + } + + assertContentEquals(expectedWeekdays, data.weekdays as Array) + } +} \ No newline at end of file diff --git a/library/src/main/java/com/github/pelmenstar1/rangecalendar/CalendarResources.kt b/library/src/main/java/com/github/pelmenstar1/rangecalendar/CalendarResources.kt index 47a43be..3873e41 100644 --- a/library/src/main/java/com/github/pelmenstar1/rangecalendar/CalendarResources.kt +++ b/library/src/main/java/com/github/pelmenstar1/rangecalendar/CalendarResources.kt @@ -3,7 +3,7 @@ package com.github.pelmenstar1.rangecalendar import android.annotation.SuppressLint import android.content.Context import android.content.res.ColorStateList -import android.icu.text.DateFormatSymbols +import android.graphics.Paint import android.os.Build import com.github.pelmenstar1.rangecalendar.utils.darkerColor import com.github.pelmenstar1.rangecalendar.utils.getColorFromAttribute @@ -23,7 +23,7 @@ internal class CalendarResources(context: Context) { val disabledTextColor: Int val weekdayTextSize: Float - val weekdays: Array + val defaultWeekdayData: WeekdayData val dayNumberTextSize: Float @@ -47,7 +47,8 @@ internal class CalendarResources(context: Context) { colorPrimary = context.getColorFromAttribute(androidx.appcompat.R.attr.colorPrimary) colorPrimaryDark = colorPrimary.darkerColor(0.4f) textColor = getTextColor(context) - colorControlNormal = context.getColorStateListFromAttribute(androidx.appcompat.R.attr.colorControlNormal) + colorControlNormal = + context.getColorStateListFromAttribute(androidx.appcompat.R.attr.colorControlNormal) outMonthTextColor = colorControlNormal.getColorForState(ENABLED_STATE, 0) disabledTextColor = colorControlNormal.getColorForState(EMPTY_STATE, 0) hoverColor = getHoverColor(context) @@ -64,52 +65,28 @@ internal class CalendarResources(context: Context) { // First element in getShortWeekDays() is empty and actual items start from 1 // It's better to copy them to another array where elements start from 0 val locale = context.getLocaleCompat() - val shortWeekdays: Array - var narrowWeekdays: Array? = null - if (Build.VERSION.SDK_INT >= 24) { - val symbols = DateFormatSymbols.getInstance(locale) - shortWeekdays = symbols.shortWeekdays + defaultWeekdayData = WeekdayData.get(locale) + val weekdays = defaultWeekdayData.weekdays - narrowWeekdays = symbols.getWeekdays( - DateFormatSymbols.FORMAT, - DateFormatSymbols.NARROW - ) - } else { - shortWeekdays = java.text.DateFormatSymbols.getInstance(locale).shortWeekdays - } - - val weekdaysLength = if (Build.VERSION.SDK_INT >= 24) 14 else 7 - - @Suppress("UNCHECKED_CAST") - weekdays = arrayOfNulls(weekdaysLength) as Array - System.arraycopy(shortWeekdays, 1, weekdays, 0, 7) + defaultWeekdayWidths = FloatArray(weekdays.size) - if (narrowWeekdays != null) { - System.arraycopy(narrowWeekdays, 1, weekdays, 7, 7) + val defaultWeekdayPaint = Paint().apply { + textSize = weekdayTextSize + typeface = null } - defaultWeekdayWidths = FloatArray(weekdaysLength) - defaultShortWeekdayRowHeight = computeWeekdayWidthAndMaxHeight(SHORT_WEEKDAYS_OFFSET) + defaultShortWeekdayRowHeight = WeekdayMeasureHelper.computeWeekdayWidthAndMaxHeight( + weekdays, WeekdayData.SHORT_WEEKDAYS_OFFSET, defaultWeekdayPaint, defaultWeekdayWidths + ) + defaultNarrowWeekdayRowHeight = if (Build.VERSION.SDK_INT >= 24) { - computeWeekdayWidthAndMaxHeight(NARROW_WEEKDAYS_OFFSET) + WeekdayMeasureHelper.computeWeekdayWidthAndMaxHeight( + weekdays, WeekdayData.NARROW_WEEKDAYS_OFFSET, defaultWeekdayPaint, defaultWeekdayWidths + ) } else Float.NaN } - private fun computeWeekdayWidthAndMaxHeight(offset: Int): Float { - var maxHeight = -1 - - getTextBoundsArray(weekdays, offset, offset + 7, weekdayTextSize, typeface = null) { i, width, height -> - if (height > maxHeight) { - maxHeight = height - } - - defaultWeekdayWidths[i + offset] = width.toFloat() - } - - return maxHeight.toFloat() - } - companion object { val DAYS = arrayOf( "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", @@ -123,9 +100,6 @@ internal class CalendarResources(context: Context) { private val ENABLED_STATE = intArrayOf(android.R.attr.state_enabled) private val EMPTY_STATE = IntArray(0) - const val SHORT_WEEKDAYS_OFFSET = 0 - const val NARROW_WEEKDAYS_OFFSET = 7 - fun getDayText(day: Int) = DAYS[day - 1] @SuppressLint("PrivateResource") diff --git a/library/src/main/java/com/github/pelmenstar1/rangecalendar/RangeCalendarGridView.kt b/library/src/main/java/com/github/pelmenstar1/rangecalendar/RangeCalendarGridView.kt index a0f6e76..94017ad 100644 --- a/library/src/main/java/com/github/pelmenstar1/rangecalendar/RangeCalendarGridView.kt +++ b/library/src/main/java/com/github/pelmenstar1/rangecalendar/RangeCalendarGridView.kt @@ -244,15 +244,9 @@ internal class RangeCalendarGridView( private var onAnimationEnd: (() -> Unit)? = null private var animationHandler: (() -> Unit)? = null - var isFirstDaySunday = false private val touchHelper: TouchHelper - private var weekdayType = WeekdayType.SHORT - - private var isWeekdayMeasurementsDirty = false - - private var weekdayWidths: FloatArray = cr.defaultWeekdayWidths - private var maxWeekdayHeight: Float = cr.defaultShortWeekdayRowHeight + private val weekdayRow: WeekdayRow private var isDayNumberMeasurementsDirty = false private var dayNumberSizes = cr.defaultDayNumberSizes @@ -332,6 +326,14 @@ internal class RangeCalendarGridView( cellHoverPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply { style = Paint.Style.FILL } + + weekdayRow = WeekdayRow( + cr.defaultShortWeekdayRowHeight, + cr.defaultNarrowWeekdayRowHeight, + cr.defaultWeekdayWidths, + cr.defaultWeekdayData, + weekdayPaint + ) } private inline fun updateUIState(block: () -> Unit) = @@ -437,7 +439,7 @@ internal class RangeCalendarGridView( weekdayPaint.textSize = size if (size != cr.weekdayTextSize) { - isWeekdayMeasurementsDirty = true + weekdayRow.onMeasurementsChanged() } onGridTopChanged() @@ -539,20 +541,8 @@ internal class RangeCalendarGridView( // There is no narrow weekdays before API < 24, so we need to resolve it val type = _type.resolved() - if (weekdayType != type) { - weekdayType = type - - // If weekdays measurements are from calendar resources then we can use precomputed values and don't make them "dirty" - if (weekdayWidths === cr.defaultWeekdayWidths) { - maxWeekdayHeight = if (type == WeekdayType.SHORT) { - cr.defaultShortWeekdayRowHeight - } else { - cr.defaultNarrowWeekdayRowHeight - } - } else { - // Widths and max height needs to be precomputed if they are not from calendar resources. - isWeekdayMeasurementsDirty = true - } + if (weekdayRow.type != type) { + weekdayRow.type = type onGridTopChanged() invalidate() @@ -1259,66 +1249,7 @@ internal class RangeCalendarGridView( } private fun drawWeekdayRow(c: Canvas) { - if (isWeekdayMeasurementsDirty) { - isWeekdayMeasurementsDirty = false - - measureWeekdays() - } - - var x = cr.hPadding + columnWidth * 0.5f - - val offset = if (weekdayType == WeekdayType.SHORT) - CalendarResources.SHORT_WEEKDAYS_OFFSET - else - CalendarResources.NARROW_WEEKDAYS_OFFSET - - val startIndex = if (isFirstDaySunday) 0 else 1 - - // If widths are from calendar resources, then we do not need any widths-offset to get the size, - // because it contains both short and narrow (if API level >= 24) weekday widths. - // But if the widths are recomputed, not from calendar resources, - // then it contains either short or narrow (if API level >= 24) weekday widths and - // we need to shift the index. - val widthsOffset = if (weekdayWidths === cr.defaultWeekdayWidths) 0 else offset - - for (i in offset + startIndex until offset + 7) { - drawWeekday(c, i, widthsOffset, x) - - x += columnWidth - } - - if (!isFirstDaySunday) { - drawWeekday(c, offset, widthsOffset, x) - } - } - - private fun drawWeekday(c: Canvas, index: Int, widthsOffset: Int, midX: Float) { - val textX = midX - weekdayWidths[index - widthsOffset] * 0.5f - val textY = maxWeekdayHeight - - c.drawText(cr.weekdays[index], textX, textY, weekdayPaint) - } - - private fun measureWeekdays() { - val offset = if (weekdayType == WeekdayType.SHORT) 0 else 7 - - var maxHeight = -1 - - // If weekdays are from calendar resources, - // then create new array to not overwrite the resources' one. - if (weekdayWidths === cr.defaultWeekdayWidths) { - weekdayWidths = FloatArray(7) - } - - weekdayPaint.getTextBoundsArray(cr.weekdays, offset, offset + 7) { i, width, height -> - if (height > maxHeight) { - maxHeight = height - } - - weekdayWidths[i] = width.toFloat() - } - - maxWeekdayHeight = maxHeight.toFloat() + weekdayRow.draw(c, cr.hPadding, columnWidth) } private fun drawHover(c: Canvas) { @@ -1483,12 +1414,15 @@ internal class RangeCalendarGridView( updateGradientBoundsIfNeeded() updateSelectionStateConfiguration() + // Height of the view depends on gridTop() + requestLayout() + // y-axis of entries depends on type of weekday, so we need to refresh accessibility info touchHelper.invalidateRoot() } private fun gridTop(): Float { - return maxWeekdayHeight + cr.weekdayRowMarginBottom + return weekdayRow.height + cr.weekdayRowMarginBottom } // It'd be better if cellRoundRadius() returns round radius that isn't greater than half of cell size. diff --git a/library/src/main/java/com/github/pelmenstar1/rangecalendar/RangeCalendarPagerAdapter.kt b/library/src/main/java/com/github/pelmenstar1/rangecalendar/RangeCalendarPagerAdapter.kt index 3b94d76..7eeed5f 100644 --- a/library/src/main/java/com/github/pelmenstar1/rangecalendar/RangeCalendarPagerAdapter.kt +++ b/library/src/main/java/com/github/pelmenstar1/rangecalendar/RangeCalendarPagerAdapter.kt @@ -1,6 +1,5 @@ package com.github.pelmenstar1.rangecalendar -import android.util.Log import android.util.SparseArray import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView @@ -876,7 +875,6 @@ internal class RangeCalendarPagerAdapter( val gridView = holder.calendar gridView.ym = ym - gridView.isFirstDaySunday = false gridView.onSelectionListener = createRedirectSelectionListener(ym) gridView.selectionGate = createRedirectSelectionGate(ym) diff --git a/library/src/main/java/com/github/pelmenstar1/rangecalendar/WeekdayData.kt b/library/src/main/java/com/github/pelmenstar1/rangecalendar/WeekdayData.kt new file mode 100644 index 0000000..f7d65f6 --- /dev/null +++ b/library/src/main/java/com/github/pelmenstar1/rangecalendar/WeekdayData.kt @@ -0,0 +1,67 @@ +package com.github.pelmenstar1.rangecalendar + +import android.icu.text.DateFormatSymbols +import android.os.Build +import java.util.Calendar +import java.util.Locale + +class WeekdayData(val weekdays: Array) { + companion object { + const val SHORT_WEEKDAYS_OFFSET = 0 + const val NARROW_WEEKDAYS_OFFSET = 7 + + fun get(locale: Locale): WeekdayData { + return WeekdayData(weekdays = getWeekdays(locale)) + } + + fun getOffsetByWeekdayType(type: WeekdayType): Int { + return if (type == WeekdayType.SHORT) { + SHORT_WEEKDAYS_OFFSET + } else { + NARROW_WEEKDAYS_OFFSET + } + } + + @Suppress("UNCHECKED_CAST") + private fun getWeekdays(locale: Locale): Array { + val resultArrayLength: Int + val shortWeekdays: Array + var narrowWeekdays: Array? = null + + if (Build.VERSION.SDK_INT >= 24) { + val symbols = DateFormatSymbols.getInstance(locale) + shortWeekdays = symbols.shortWeekdays + + narrowWeekdays = symbols.getWeekdays( + DateFormatSymbols.FORMAT, + DateFormatSymbols.NARROW + ) + resultArrayLength = 14 + } else { + shortWeekdays = java.text.DateFormatSymbols.getInstance(locale).shortWeekdays + resultArrayLength = 7 + } + + // Cast to array with non-null elements because in the end the array won't have any null elements. + val weekdays = arrayOfNulls(resultArrayLength) as Array + + copyAndFixWeekdaysOrder(shortWeekdays, weekdays, offset = SHORT_WEEKDAYS_OFFSET) + + if (narrowWeekdays != null) { + copyAndFixWeekdaysOrder(narrowWeekdays, weekdays, offset = NARROW_WEEKDAYS_OFFSET) + } + + return weekdays + } + + // In format that DateFormatSymbols returns, Sunday is the first day of the week and 0 element is null, then goes Sun, Mon, Tue, ... + // But it's better for logic when first day of week is Monday and the elements start from 0 element. + // The method copies elements from source to dest with some reordering. + private fun copyAndFixWeekdaysOrder(source: Array, dest: Array, offset: Int) { + System.arraycopy(source, 2, dest, offset, 6) + + // Sunday is the last day of week + dest[offset + 6] = source[Calendar.SUNDAY] + } + } +} \ No newline at end of file diff --git a/library/src/main/java/com/github/pelmenstar1/rangecalendar/WeekdayMeasureHelper.kt b/library/src/main/java/com/github/pelmenstar1/rangecalendar/WeekdayMeasureHelper.kt new file mode 100644 index 0000000..5f8501d --- /dev/null +++ b/library/src/main/java/com/github/pelmenstar1/rangecalendar/WeekdayMeasureHelper.kt @@ -0,0 +1,20 @@ +package com.github.pelmenstar1.rangecalendar + +import android.graphics.Paint +import com.github.pelmenstar1.rangecalendar.utils.getTextBoundsArray + +object WeekdayMeasureHelper { + fun computeWeekdayWidthAndMaxHeight(weekdays: Array, offset: Int, paint: Paint, outWidths: FloatArray): Float { + var maxHeight = -1 + + paint.getTextBoundsArray(weekdays, offset, offset + 7) { i, width, height -> + if (height > maxHeight) { + maxHeight = height + } + + outWidths[i + offset] = width.toFloat() + } + + return maxHeight.toFloat() + } +} \ No newline at end of file diff --git a/library/src/main/java/com/github/pelmenstar1/rangecalendar/WeekdayRow.kt b/library/src/main/java/com/github/pelmenstar1/rangecalendar/WeekdayRow.kt new file mode 100644 index 0000000..bd9b79f --- /dev/null +++ b/library/src/main/java/com/github/pelmenstar1/rangecalendar/WeekdayRow.kt @@ -0,0 +1,79 @@ +package com.github.pelmenstar1.rangecalendar + +import android.graphics.Canvas +import android.graphics.Paint + +internal class WeekdayRow( + private val defaultShortWeekdayRowHeight: Float, + private val defaultNarrowWeekdayRowHeight: Float, + private val defaultWeekdayWidths: FloatArray, + private val weekdayData: WeekdayData, + private val textPaint: Paint +) { + private var weekdayWidths: FloatArray = defaultWeekdayWidths + + /** + * Height of the row, in pixels. + */ + var height: Float = defaultShortWeekdayRowHeight + private set + + /** + * Gets or sets type of weekdays in the row. In setter the value should be "resolved" (via [WeekdayType.resolved]) + */ + var type = WeekdayType.SHORT + set(value) { + if (field != value) { + field = value + + // If we're still using defaultWeekdayWidths, it means that text size of weekday haven't been changed and + // we can use default measurements. + if (weekdayWidths === defaultWeekdayWidths) { + height = if (value == WeekdayType.SHORT) + defaultShortWeekdayRowHeight + else + defaultNarrowWeekdayRowHeight + } else { + onMeasurementsChanged() + } + } + } + + /** + * The method should be called when some measurements might be changed. + */ + fun onMeasurementsChanged() { + val offset = WeekdayData.getOffsetByWeekdayType(type) + + // If weekdayWidths is default one, create a new array not to overwrite default one. + if (weekdayWidths === defaultWeekdayWidths) { + // If we can't use precomputed widths, we re-compute them only for specified weekday type, so length is 7. + weekdayWidths = FloatArray(7) + } + + height = WeekdayMeasureHelper.computeWeekdayWidthAndMaxHeight( + weekdayData.weekdays, offset, textPaint, weekdayWidths + ) + } + + fun draw(c: Canvas, x: Float, columnWidth: Float) { + var midX = x + columnWidth * 0.5f + + val offset = WeekdayData.getOffsetByWeekdayType(type) + val widthsOffset = if (weekdayWidths === defaultWeekdayWidths) offset else 0 + + val weekdays = weekdayData.weekdays + val textY = height + + for (i in 0 until 7) { + val text = weekdays[i + offset] + val width = weekdayWidths[i + widthsOffset] + + val textX = midX - width * 0.5f + + c.drawText(text, textX, textY, textPaint) + + midX += columnWidth + } + } +} \ No newline at end of file From 76d4616fa15575b756f07707cedf882efad8ecbe Mon Sep 17 00:00:00 2001 From: Oleh Date: Thu, 22 Jun 2023 23:36:15 +0300 Subject: [PATCH 2/7] Allow customizing typeface of weekday text --- .../pelmenstar1/rangecalendar/RangeCalendarGridView.kt | 7 +++++++ .../rangecalendar/RangeCalendarPagerAdapter.kt | 10 ++++++++-- .../pelmenstar1/rangecalendar/RangeCalendarView.kt | 10 ++++++++++ 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/library/src/main/java/com/github/pelmenstar1/rangecalendar/RangeCalendarGridView.kt b/library/src/main/java/com/github/pelmenstar1/rangecalendar/RangeCalendarGridView.kt index 94017ad..8a67d1f 100644 --- a/library/src/main/java/com/github/pelmenstar1/rangecalendar/RangeCalendarGridView.kt +++ b/library/src/main/java/com/github/pelmenstar1/rangecalendar/RangeCalendarGridView.kt @@ -445,6 +445,13 @@ internal class RangeCalendarGridView( onGridTopChanged() } + fun setWeekdayTypeface(typeface: Typeface?) = updateUIState(weekdayPaint.typeface, typeface) { + weekdayPaint.typeface = typeface + weekdayRow.onMeasurementsChanged() + + onGridTopChanged() + } + fun setInMonthTextColor(color: Int) = updateUIState(inMonthTextColor, color) { inMonthTextColor = color } diff --git a/library/src/main/java/com/github/pelmenstar1/rangecalendar/RangeCalendarPagerAdapter.kt b/library/src/main/java/com/github/pelmenstar1/rangecalendar/RangeCalendarPagerAdapter.kt index 7eeed5f..9f18295 100644 --- a/library/src/main/java/com/github/pelmenstar1/rangecalendar/RangeCalendarPagerAdapter.kt +++ b/library/src/main/java/com/github/pelmenstar1/rangecalendar/RangeCalendarPagerAdapter.kt @@ -1,5 +1,6 @@ package com.github.pelmenstar1.rangecalendar +import android.graphics.Typeface import android.util.SparseArray import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView @@ -137,7 +138,7 @@ internal class RangeCalendarPagerAdapter( private val gridInfo = YearMonthGridInfo() private val styleData = IntArray(20) - private val styleObjData = arrayOfNulls(7) + private val styleObjData = arrayOfNulls(8) private var onSelectionListener: RangeCalendarView.OnSelectionListener? = null var selectionGate: RangeCalendarView.SelectionGate? = null @@ -164,6 +165,9 @@ internal class RangeCalendarPagerAdapter( initStyle(STYLE_CELL_HEIGHT, cr.cellSize) initStyle(STYLE_CLICK_ON_CELL_SELECTION_BEHAVIOR, ClickOnCellSelectionBehavior.NONE) + // typefaces + initStyle(STYLE_WEEKDAY_TYPEFACE, Typeface.DEFAULT_BOLD) + // animations initStyle( STYLE_COMMON_ANIMATION_DURATION, @@ -204,7 +208,7 @@ internal class RangeCalendarPagerAdapter( initStyle(type, data.ordinal) } - private fun initStyle(type: Int, data: Any) { + private fun initStyle(type: Int, data: Any?) { styleObjData[type - STYLE_OBJ_START] = data } @@ -325,6 +329,7 @@ internal class RangeCalendarPagerAdapter( STYLE_SELECTION_FILL -> setSelectionFill(data.value()) STYLE_SELECTION_MANAGER -> setSelectionManager(data.value()) STYLE_CELL_ACCESSIBILITY_INFO_PROVIDER -> setCellAccessibilityInfoProvider(data.value()) + STYLE_WEEKDAY_TYPEFACE -> setWeekdayTypeface(data.value()) } } } @@ -1033,6 +1038,7 @@ internal class RangeCalendarPagerAdapter( const val STYLE_SELECTION_FILL = 35 const val STYLE_SELECTION_MANAGER = 36 const val STYLE_CELL_ACCESSIBILITY_INFO_PROVIDER = 37 + const val STYLE_WEEKDAY_TYPEFACE = 38 // Precomputed value private const val PAGES_BETWEEN_ABS_MIN_MAX = 786432 diff --git a/library/src/main/java/com/github/pelmenstar1/rangecalendar/RangeCalendarView.kt b/library/src/main/java/com/github/pelmenstar1/rangecalendar/RangeCalendarView.kt index 41cc5b2..e2ade50 100644 --- a/library/src/main/java/com/github/pelmenstar1/rangecalendar/RangeCalendarView.kt +++ b/library/src/main/java/com/github/pelmenstar1/rangecalendar/RangeCalendarView.kt @@ -5,6 +5,7 @@ import android.content.Context import android.content.res.Configuration import android.content.res.TypedArray import android.graphics.Rect +import android.graphics.Typeface import android.os.Build import android.os.Parcelable import android.util.AttributeSet @@ -892,6 +893,15 @@ class RangeCalendarView @JvmOverloads constructor( adapter.setStyleFloat({ STYLE_WEEKDAY_TEXT_SIZE }, size) } + /** + * Gets or sets a typeface of weekday. Pass null to clear previous typeface. + */ + var weekdayTypeface: Typeface? + get() = adapter.getStyleObject { STYLE_WEEKDAY_TYPEFACE } + set(value) { + adapter.setStyleObject({ STYLE_WEEKDAY_TYPEFACE }, value) + } + /** * Gets or sets a background color of cell which appears when user hovers under a cell * (when a pointer is registered to be down). From 0932aff5f9e31ef446c2f789f917b2e751ea5c20 Mon Sep 17 00:00:00 2001 From: Oleh Date: Fri, 23 Jun 2023 00:04:00 +0300 Subject: [PATCH 3/7] Allow customizing weekdays --- .../rangecalendar/RangeCalendarGridView.kt | 11 +++-- .../RangeCalendarPagerAdapter.kt | 3 ++ .../rangecalendar/RangeCalendarView.kt | 27 ++++++++++++- .../pelmenstar1/rangecalendar/WeekdayRow.kt | 40 ++++++++++++++----- 4 files changed, 65 insertions(+), 16 deletions(-) diff --git a/library/src/main/java/com/github/pelmenstar1/rangecalendar/RangeCalendarGridView.kt b/library/src/main/java/com/github/pelmenstar1/rangecalendar/RangeCalendarGridView.kt index 8a67d1f..1c67926 100644 --- a/library/src/main/java/com/github/pelmenstar1/rangecalendar/RangeCalendarGridView.kt +++ b/library/src/main/java/com/github/pelmenstar1/rangecalendar/RangeCalendarGridView.kt @@ -437,10 +437,7 @@ internal class RangeCalendarGridView( fun setWeekdayTextSize(size: Float) = updateUIState(weekdayPaint.textSize, size) { weekdayPaint.textSize = size - - if (size != cr.weekdayTextSize) { - weekdayRow.onMeasurementsChanged() - } + weekdayRow.onMeasurementsChanged() onGridTopChanged() } @@ -452,6 +449,12 @@ internal class RangeCalendarGridView( onGridTopChanged() } + fun setCustomWeekdays(weekdays: Array?) = updateUIState(weekdayRow.weekdays, weekdays) { + weekdayRow.weekdays = weekdays + + onGridChanged() + } + fun setInMonthTextColor(color: Int) = updateUIState(inMonthTextColor, color) { inMonthTextColor = color } diff --git a/library/src/main/java/com/github/pelmenstar1/rangecalendar/RangeCalendarPagerAdapter.kt b/library/src/main/java/com/github/pelmenstar1/rangecalendar/RangeCalendarPagerAdapter.kt index 9f18295..19e2f25 100644 --- a/library/src/main/java/com/github/pelmenstar1/rangecalendar/RangeCalendarPagerAdapter.kt +++ b/library/src/main/java/com/github/pelmenstar1/rangecalendar/RangeCalendarPagerAdapter.kt @@ -190,6 +190,7 @@ internal class RangeCalendarPagerAdapter( // other stuff initStyle(STYLE_VIBRATE_ON_SELECTING_CUSTOM_RANGE, true) initStyle(STYLE_SHOW_ADJACENT_MONTHS, true) + initStyle(STYLE_WEEKDAYS, null) } private fun initStyle(type: Int, data: Boolean) { @@ -330,6 +331,7 @@ internal class RangeCalendarPagerAdapter( STYLE_SELECTION_MANAGER -> setSelectionManager(data.value()) STYLE_CELL_ACCESSIBILITY_INFO_PROVIDER -> setCellAccessibilityInfoProvider(data.value()) STYLE_WEEKDAY_TYPEFACE -> setWeekdayTypeface(data.value()) + STYLE_WEEKDAYS -> setCustomWeekdays(data.value()) } } } @@ -1039,6 +1041,7 @@ internal class RangeCalendarPagerAdapter( const val STYLE_SELECTION_MANAGER = 36 const val STYLE_CELL_ACCESSIBILITY_INFO_PROVIDER = 37 const val STYLE_WEEKDAY_TYPEFACE = 38 + const val STYLE_WEEKDAYS = 39 // Precomputed value private const val PAGES_BETWEEN_ABS_MIN_MAX = 786432 diff --git a/library/src/main/java/com/github/pelmenstar1/rangecalendar/RangeCalendarView.kt b/library/src/main/java/com/github/pelmenstar1/rangecalendar/RangeCalendarView.kt index e2ade50..5c80215 100644 --- a/library/src/main/java/com/github/pelmenstar1/rangecalendar/RangeCalendarView.kt +++ b/library/src/main/java/com/github/pelmenstar1/rangecalendar/RangeCalendarView.kt @@ -902,6 +902,25 @@ class RangeCalendarView @JvmOverloads constructor( adapter.setStyleObject({ STYLE_WEEKDAY_TYPEFACE }, value) } + /** + * Gets or sets array of weekdays. Length of the array should be exactly 7. First day of week is expected to be Monday. + * + * Note that the getter returns a reference to weekdays, not a copy, thus that array should not be modified in any way. + * If you want to update weekdays, make a copy with changes and use setter. + * + * Pass `null` to use localized weekdays. Note that if you do that, the getter will return localized weekdays, not `null`. + */ + var weekdays: Array? + get() = adapter.getStyleObject { STYLE_WEEKDAYS } + set(value) { + if (value != null && value.size != 7) { + throw IllegalArgumentException("Length of the array should be 7") + } + + // Copy the array because the caller might be it later. + adapter.setStyleObject({ STYLE_WEEKDAYS }, value?.copyOf()) + } + /** * Gets or sets a background color of cell which appears when user hovers under a cell * (when a pointer is registered to be down). @@ -969,13 +988,17 @@ class RangeCalendarView @JvmOverloads constructor( } /** - * Gets or sets weekday type. Should be [WeekdayType.SHORT] or [WeekdayType.NARROW]. + * Gets or sets weekday type. + * * If [WeekdayType.SHORT], then weekdays will be: Mo, Tue, Wed, Thu, Fri. * If [WeekdayType.NARROW], then weekdays will be: M, T, W, T, F. * Note: - * - Narrow weekdays is dependent on user locale and are not always one-letter. + * - Narrow weekdays depend on user locale and are not always one-letter. * - **If API level is less than 24, [WeekdayType.NARROW] won't work.** * + * Changing weekday type when custom weekdays array was set (via [weekdays]) won't have any effect, even though when + * `null` value is passed to the setter of [weekdays], the latest value of [weekdayType] will be used. + * * @throws IllegalArgumentException if type is not one of [WeekdayType] constants */ var weekdayType: WeekdayType diff --git a/library/src/main/java/com/github/pelmenstar1/rangecalendar/WeekdayRow.kt b/library/src/main/java/com/github/pelmenstar1/rangecalendar/WeekdayRow.kt index bd9b79f..ea064d4 100644 --- a/library/src/main/java/com/github/pelmenstar1/rangecalendar/WeekdayRow.kt +++ b/library/src/main/java/com/github/pelmenstar1/rangecalendar/WeekdayRow.kt @@ -7,11 +7,14 @@ internal class WeekdayRow( private val defaultShortWeekdayRowHeight: Float, private val defaultNarrowWeekdayRowHeight: Float, private val defaultWeekdayWidths: FloatArray, - private val weekdayData: WeekdayData, + private val localizedWeekdayData: WeekdayData, private val textPaint: Paint ) { private var weekdayWidths: FloatArray = defaultWeekdayWidths + private var isCustomWeekdays = false + private var weekdayData = localizedWeekdayData + /** * Height of the row, in pixels. */ @@ -26,19 +29,36 @@ internal class WeekdayRow( if (field != value) { field = value - // If we're still using defaultWeekdayWidths, it means that text size of weekday haven't been changed and - // we can use default measurements. - if (weekdayWidths === defaultWeekdayWidths) { - height = if (value == WeekdayType.SHORT) - defaultShortWeekdayRowHeight - else - defaultNarrowWeekdayRowHeight - } else { - onMeasurementsChanged() + // Changing the type when custom weekdays are used should have no effect + if (!isCustomWeekdays) { + // If we're still using defaultWeekdayWidths, it means that text size of weekday haven't been changed and + // we can use default measurements. + if (weekdayWidths === defaultWeekdayWidths) { + height = if (value == WeekdayType.SHORT) + defaultShortWeekdayRowHeight + else + defaultNarrowWeekdayRowHeight + } else { + onMeasurementsChanged() + } } } } + var weekdays: Array? + get() = weekdayData.weekdays + set(value) { + weekdayData = if (value == null) { + localizedWeekdayData + } else { + WeekdayData(value) + } + + isCustomWeekdays = value != null + + onMeasurementsChanged() + } + /** * The method should be called when some measurements might be changed. */ From 67e16029d01510a97891ce8479db74563e422173 Mon Sep 17 00:00:00 2001 From: Oleh Date: Fri, 23 Jun 2023 00:20:06 +0300 Subject: [PATCH 4/7] Add rangeCalendar_weekdays attribute --- README.md | 10 ++++++++-- .../rangecalendar/RangeCalendarView.kt | 18 +++++++++++++----- library/src/main/res/values/attrs.xml | 1 + 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 3617ba5..c53aa48 100644 --- a/README.md +++ b/README.md @@ -276,11 +276,17 @@ observer.register() // The observer should be registered manually. rangeCalendar_weekdayType Type of weekday.
- Format of weekdays is very dependent on locale. In example, English locale is used. + Format of weekdays depends on locale. In example, English locale is used.
  • shortName (WeekdayType.SHORT) - weekdays will look like Mob, Tue, Wed.
  • narrowName (WeekdayType.NARROW) - weekdays will look like M, T, W,.
  • -
+ + + + + rangeCalendar_weekdays + + Custom weekdays. The value should be a string array, whose length is 7. diff --git a/library/src/main/java/com/github/pelmenstar1/rangecalendar/RangeCalendarView.kt b/library/src/main/java/com/github/pelmenstar1/rangecalendar/RangeCalendarView.kt index 5c80215..a92d7a3 100644 --- a/library/src/main/java/com/github/pelmenstar1/rangecalendar/RangeCalendarView.kt +++ b/library/src/main/java/com/github/pelmenstar1/rangecalendar/RangeCalendarView.kt @@ -115,12 +115,14 @@ class RangeCalendarView @JvmOverloads constructor( fun onPageChanged(year: Int, month: Int) } + // Kotlin says that there's redundant qualitifer in RangeCalendarPagerAdapter.getStyle() but it's wrong. + @Suppress("RemoveRedundantQualifierName") private class ExtractAttributesScope( private val calendarView: RangeCalendarView, private val attrs: TypedArray ) { fun color(@StyleableRes index: Int, styleType: Int) { - extract( + extractPrimitive( index, styleType, ::PackedInt, extract = { getColor(index, 0) } @@ -128,7 +130,7 @@ class RangeCalendarView @JvmOverloads constructor( } fun dimension(@StyleableRes index: Int, styleType: Int) { - extract( + extractPrimitive( index, styleType, ::PackedInt, extract = { getDimension(index, 0f) } @@ -136,7 +138,7 @@ class RangeCalendarView @JvmOverloads constructor( } fun int(@StyleableRes index: Int, styleType: Int) { - extract( + extractPrimitive( index, styleType, ::PackedInt, extract = { getInteger(index, 0) } @@ -144,7 +146,7 @@ class RangeCalendarView @JvmOverloads constructor( } fun boolean(@StyleableRes index: Int, styleType: Int) { - extract( + extractPrimitive( index, styleType, ::PackedInt, extract = { getBoolean(index, false) } @@ -171,7 +173,7 @@ class RangeCalendarView @JvmOverloads constructor( getStyle: RangeCalendarPagerAdapter.Companion.() -> Int ) = boolean(index, RangeCalendarPagerAdapter.getStyle()) - private inline fun extract( + private inline fun extractPrimitive( @StyleableRes index: Int, styleType: Int, createPacked: (T) -> PackedInt, @@ -423,6 +425,12 @@ class RangeCalendarView @JvmOverloads constructor( } } + // weekdays should be set via setter because additional checks should be made. + if (a.hasValue(R.styleable.RangeCalendarView_rangeCalendar_weekdays)) { + val resId = a.getResourceId(R.styleable.RangeCalendarView_rangeCalendar_weekdays, 0) + + weekdays = resources.getStringArray(resId) + } } } finally { a.recycle() diff --git a/library/src/main/res/values/attrs.xml b/library/src/main/res/values/attrs.xml index 73f3f3e..226bca0 100644 --- a/library/src/main/res/values/attrs.xml +++ b/library/src/main/res/values/attrs.xml @@ -18,6 +18,7 @@ + From 244a036024913cc57a8690c158f6c46df2f47952 Mon Sep 17 00:00:00 2001 From: Oleh Date: Fri, 23 Jun 2023 15:40:24 +0300 Subject: [PATCH 5/7] Do not precompute text sizes of weekdays. Refactor the logic --- .../rangecalendar/WeekdayDataTests.kt | 38 ++++++++---- .../rangecalendar/CalendarResources.kt | 39 ++---------- .../rangecalendar/RangeCalendarGridView.kt | 13 +--- .../pelmenstar1/rangecalendar/WeekdayData.kt | 60 ++++++++----------- .../rangecalendar/WeekdayMeasureHelper.kt | 16 ++--- .../pelmenstar1/rangecalendar/WeekdayRow.kt | 60 +++++-------------- 6 files changed, 84 insertions(+), 142 deletions(-) diff --git a/library/src/androidTest/java/com/github/pelmenstar1/rangecalendar/WeekdayDataTests.kt b/library/src/androidTest/java/com/github/pelmenstar1/rangecalendar/WeekdayDataTests.kt index 0f1f093..6bf25d2 100644 --- a/library/src/androidTest/java/com/github/pelmenstar1/rangecalendar/WeekdayDataTests.kt +++ b/library/src/androidTest/java/com/github/pelmenstar1/rangecalendar/WeekdayDataTests.kt @@ -6,24 +6,42 @@ import org.junit.Test import org.junit.runner.RunWith import java.util.Locale import kotlin.test.assertContentEquals +import kotlin.test.assertSame @RunWith(AndroidJUnit4::class) class WeekdayDataTests { @Suppress("UNCHECKED_CAST") + private fun assertContentEquals(expected: Array?, actual: Array?) { + // Pass null message to make Kotlin use the right method. + assertContentEquals(expected as Array?, actual as Array?, message = null) + } + @Test fun getTest() { - val locale = Locale.ENGLISH - val data = WeekdayData.get(locale) - - val expectedWeekdays: Array = if (Build.VERSION.SDK_INT >= 24) { - arrayOf( - "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun", - "M", "T", "W", "T", "F", "S", "S", - ) + val data = WeekdayData.get(Locale.ENGLISH) + + val expectedShortWeekdays = arrayOf("Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun") + val expectedNarrowWeekdays = if (Build.VERSION.SDK_INT >= 24) { + arrayOf( "M", "T", "W", "T", "F", "S", "S") } else { - arrayOf("Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun") + null } - assertContentEquals(expectedWeekdays, data.weekdays as Array) + assertContentEquals(expectedShortWeekdays, data.shortWeekdays) + assertContentEquals(expectedNarrowWeekdays, data.narrowWeekdays) + } + + @Test + fun getWeekdaysTest() { + val data = WeekdayData.get(Locale.ENGLISH) + + val actualShortWeekdays = data.getWeekdays(WeekdayType.SHORT) + assertSame(data.shortWeekdays, actualShortWeekdays) + + if (Build.VERSION.SDK_INT >= 24) { + val actualNarrowWeekdays = data.getWeekdays(WeekdayType.NARROW) + + assertSame(data.narrowWeekdays, actualNarrowWeekdays) + } } } \ No newline at end of file diff --git a/library/src/main/java/com/github/pelmenstar1/rangecalendar/CalendarResources.kt b/library/src/main/java/com/github/pelmenstar1/rangecalendar/CalendarResources.kt index 3873e41..5fdfbeb 100644 --- a/library/src/main/java/com/github/pelmenstar1/rangecalendar/CalendarResources.kt +++ b/library/src/main/java/com/github/pelmenstar1/rangecalendar/CalendarResources.kt @@ -3,8 +3,6 @@ package com.github.pelmenstar1.rangecalendar import android.annotation.SuppressLint import android.content.Context import android.content.res.ColorStateList -import android.graphics.Paint -import android.os.Build import com.github.pelmenstar1.rangecalendar.utils.darkerColor import com.github.pelmenstar1.rangecalendar.utils.getColorFromAttribute import com.github.pelmenstar1.rangecalendar.utils.getColorStateListFromAttribute @@ -27,17 +25,9 @@ internal class CalendarResources(context: Context) { val dayNumberTextSize: Float - // Can only be used when text size is default one (dayNumberTextSize) + // It's a precomputed text sizes for day numbers using default system font and dayNumberTextSize val defaultDayNumberSizes: PackedSizeArray - /* - * These are precomputed values for default weekdayTextSize and cannot be used for another text size. - */ - val defaultWeekdayWidths: FloatArray - val defaultShortWeekdayRowHeight: Float - val defaultNarrowWeekdayRowHeight: Float - /* ----- */ - val weekdayRowMarginBottom: Float val colorControlNormal: ColorStateList @@ -62,29 +52,7 @@ internal class CalendarResources(context: Context) { // Compute text size of numbers in [0; 31] defaultDayNumberSizes = getTextBoundsArray(DAYS, dayNumberTextSize) - // First element in getShortWeekDays() is empty and actual items start from 1 - // It's better to copy them to another array where elements start from 0 - val locale = context.getLocaleCompat() - - defaultWeekdayData = WeekdayData.get(locale) - val weekdays = defaultWeekdayData.weekdays - - defaultWeekdayWidths = FloatArray(weekdays.size) - - val defaultWeekdayPaint = Paint().apply { - textSize = weekdayTextSize - typeface = null - } - - defaultShortWeekdayRowHeight = WeekdayMeasureHelper.computeWeekdayWidthAndMaxHeight( - weekdays, WeekdayData.SHORT_WEEKDAYS_OFFSET, defaultWeekdayPaint, defaultWeekdayWidths - ) - - defaultNarrowWeekdayRowHeight = if (Build.VERSION.SDK_INT >= 24) { - WeekdayMeasureHelper.computeWeekdayWidthAndMaxHeight( - weekdays, WeekdayData.NARROW_WEEKDAYS_OFFSET, defaultWeekdayPaint, defaultWeekdayWidths - ) - } else Float.NaN + defaultWeekdayData = WeekdayData.get(context.getLocaleCompat()) } companion object { @@ -96,7 +64,8 @@ internal class CalendarResources(context: Context) { ) private val SINGLE_INT_ARRAY = IntArray(1) - private val HOVER_STATE = intArrayOf(android.R.attr.state_hovered, android.R.attr.state_enabled) + private val HOVER_STATE = + intArrayOf(android.R.attr.state_hovered, android.R.attr.state_enabled) private val ENABLED_STATE = intArrayOf(android.R.attr.state_enabled) private val EMPTY_STATE = IntArray(0) diff --git a/library/src/main/java/com/github/pelmenstar1/rangecalendar/RangeCalendarGridView.kt b/library/src/main/java/com/github/pelmenstar1/rangecalendar/RangeCalendarGridView.kt index 1c67926..8dda2c0 100644 --- a/library/src/main/java/com/github/pelmenstar1/rangecalendar/RangeCalendarGridView.kt +++ b/library/src/main/java/com/github/pelmenstar1/rangecalendar/RangeCalendarGridView.kt @@ -33,10 +33,7 @@ import kotlin.math.min // It will never be XML layout, so there's no need to match conventions @SuppressLint("ViewConstructor") -internal class RangeCalendarGridView( - context: Context, - val cr: CalendarResources -) : View(context) { +internal class RangeCalendarGridView(context: Context, val cr: CalendarResources) : View(context) { interface OnSelectionListener { fun onSelectionCleared() fun onSelection(range: CellRange) @@ -327,13 +324,7 @@ internal class RangeCalendarGridView( style = Paint.Style.FILL } - weekdayRow = WeekdayRow( - cr.defaultShortWeekdayRowHeight, - cr.defaultNarrowWeekdayRowHeight, - cr.defaultWeekdayWidths, - cr.defaultWeekdayData, - weekdayPaint - ) + weekdayRow = WeekdayRow(cr.defaultWeekdayData, weekdayPaint) } private inline fun updateUIState(block: () -> Unit) = diff --git a/library/src/main/java/com/github/pelmenstar1/rangecalendar/WeekdayData.kt b/library/src/main/java/com/github/pelmenstar1/rangecalendar/WeekdayData.kt index f7d65f6..785d49d 100644 --- a/library/src/main/java/com/github/pelmenstar1/rangecalendar/WeekdayData.kt +++ b/library/src/main/java/com/github/pelmenstar1/rangecalendar/WeekdayData.kt @@ -5,27 +5,17 @@ import android.os.Build import java.util.Calendar import java.util.Locale -class WeekdayData(val weekdays: Array) { - companion object { - const val SHORT_WEEKDAYS_OFFSET = 0 - const val NARROW_WEEKDAYS_OFFSET = 7 +/** + * Saves weekdays in short format and in narrow format (if possible). [narrowWeekdays] is always non-null when API level >= 24 + */ +class WeekdayData(val shortWeekdays: Array, val narrowWeekdays: Array?) { + fun getWeekdays(type: WeekdayType): Array { + return if (type == WeekdayType.SHORT) shortWeekdays else narrowWeekdays!! + } + companion object { fun get(locale: Locale): WeekdayData { - return WeekdayData(weekdays = getWeekdays(locale)) - } - - fun getOffsetByWeekdayType(type: WeekdayType): Int { - return if (type == WeekdayType.SHORT) { - SHORT_WEEKDAYS_OFFSET - } else { - NARROW_WEEKDAYS_OFFSET - } - } - - @Suppress("UNCHECKED_CAST") - private fun getWeekdays(locale: Locale): Array { - val resultArrayLength: Int - val shortWeekdays: Array + var shortWeekdays: Array var narrowWeekdays: Array? = null if (Build.VERSION.SDK_INT >= 24) { @@ -36,32 +26,32 @@ class WeekdayData(val weekdays: Array) { DateFormatSymbols.FORMAT, DateFormatSymbols.NARROW ) - resultArrayLength = 14 + + shortWeekdays = fixWeekdaysOrder(shortWeekdays) + narrowWeekdays = fixWeekdaysOrder(narrowWeekdays) } else { shortWeekdays = java.text.DateFormatSymbols.getInstance(locale).shortWeekdays - resultArrayLength = 7 + shortWeekdays = fixWeekdaysOrder(shortWeekdays) } - // Cast to array with non-null elements because in the end the array won't have any null elements. - val weekdays = arrayOfNulls(resultArrayLength) as Array - - copyAndFixWeekdaysOrder(shortWeekdays, weekdays, offset = SHORT_WEEKDAYS_OFFSET) - - if (narrowWeekdays != null) { - copyAndFixWeekdaysOrder(narrowWeekdays, weekdays, offset = NARROW_WEEKDAYS_OFFSET) - } - - return weekdays + return WeekdayData(shortWeekdays, narrowWeekdays) } + // In format that DateFormatSymbols returns, Sunday is the first day of the week and 0 element is null, then goes Sun, Mon, Tue, ... // But it's better for logic when first day of week is Monday and the elements start from 0 element. - // The method copies elements from source to dest with some reordering. - private fun copyAndFixWeekdaysOrder(source: Array, dest: Array, offset: Int) { - System.arraycopy(source, 2, dest, offset, 6) + // The method creates a new array with fixed order. + @Suppress("UNCHECKED_CAST") + private fun fixWeekdaysOrder(source: Array): Array { + // In the end, weekdays will contain only non-null elements + val weekdays = arrayOfNulls(7) as Array + + System.arraycopy(source, 2, weekdays, 0, 6) // Sunday is the last day of week - dest[offset + 6] = source[Calendar.SUNDAY] + weekdays[6] = source[Calendar.SUNDAY] + + return weekdays } } } \ No newline at end of file diff --git a/library/src/main/java/com/github/pelmenstar1/rangecalendar/WeekdayMeasureHelper.kt b/library/src/main/java/com/github/pelmenstar1/rangecalendar/WeekdayMeasureHelper.kt index 5f8501d..b19f94b 100644 --- a/library/src/main/java/com/github/pelmenstar1/rangecalendar/WeekdayMeasureHelper.kt +++ b/library/src/main/java/com/github/pelmenstar1/rangecalendar/WeekdayMeasureHelper.kt @@ -2,17 +2,19 @@ package com.github.pelmenstar1.rangecalendar import android.graphics.Paint import com.github.pelmenstar1.rangecalendar.utils.getTextBoundsArray +import kotlin.math.max object WeekdayMeasureHelper { - fun computeWeekdayWidthAndMaxHeight(weekdays: Array, offset: Int, paint: Paint, outWidths: FloatArray): Float { + fun computeWidthsAndMaxHeight( + weekdays: Array, + paint: Paint, + outWidths: FloatArray + ): Float { var maxHeight = -1 - paint.getTextBoundsArray(weekdays, offset, offset + 7) { i, width, height -> - if (height > maxHeight) { - maxHeight = height - } - - outWidths[i + offset] = width.toFloat() + paint.getTextBoundsArray(weekdays, start = 0, end = 7) { i, width, height -> + maxHeight = max(maxHeight, height) + outWidths[i] = width.toFloat() } return maxHeight.toFloat() diff --git a/library/src/main/java/com/github/pelmenstar1/rangecalendar/WeekdayRow.kt b/library/src/main/java/com/github/pelmenstar1/rangecalendar/WeekdayRow.kt index ea064d4..2067623 100644 --- a/library/src/main/java/com/github/pelmenstar1/rangecalendar/WeekdayRow.kt +++ b/library/src/main/java/com/github/pelmenstar1/rangecalendar/WeekdayRow.kt @@ -4,21 +4,16 @@ import android.graphics.Canvas import android.graphics.Paint internal class WeekdayRow( - private val defaultShortWeekdayRowHeight: Float, - private val defaultNarrowWeekdayRowHeight: Float, - private val defaultWeekdayWidths: FloatArray, private val localizedWeekdayData: WeekdayData, private val textPaint: Paint ) { - private var weekdayWidths: FloatArray = defaultWeekdayWidths - private var isCustomWeekdays = false - private var weekdayData = localizedWeekdayData + private val currentWidths = FloatArray(7) /** * Height of the row, in pixels. */ - var height: Float = defaultShortWeekdayRowHeight + var height: Float = Float.NaN private set /** @@ -31,63 +26,40 @@ internal class WeekdayRow( // Changing the type when custom weekdays are used should have no effect if (!isCustomWeekdays) { - // If we're still using defaultWeekdayWidths, it means that text size of weekday haven't been changed and - // we can use default measurements. - if (weekdayWidths === defaultWeekdayWidths) { - height = if (value == WeekdayType.SHORT) - defaultShortWeekdayRowHeight - else - defaultNarrowWeekdayRowHeight - } else { - onMeasurementsChanged() - } + _weekdays = localizedWeekdayData.getWeekdays(value) + + onMeasurementsChanged() } } } + private var _weekdays: Array = localizedWeekdayData.shortWeekdays + var weekdays: Array? - get() = weekdayData.weekdays + get() = _weekdays set(value) { - weekdayData = if (value == null) { - localizedWeekdayData - } else { - WeekdayData(value) - } - + _weekdays = value ?: localizedWeekdayData.getWeekdays(type) isCustomWeekdays = value != null onMeasurementsChanged() } - /** - * The method should be called when some measurements might be changed. - */ - fun onMeasurementsChanged() { - val offset = WeekdayData.getOffsetByWeekdayType(type) - - // If weekdayWidths is default one, create a new array not to overwrite default one. - if (weekdayWidths === defaultWeekdayWidths) { - // If we can't use precomputed widths, we re-compute them only for specified weekday type, so length is 7. - weekdayWidths = FloatArray(7) - } + init { + onMeasurementsChanged() + } - height = WeekdayMeasureHelper.computeWeekdayWidthAndMaxHeight( - weekdayData.weekdays, offset, textPaint, weekdayWidths - ) + fun onMeasurementsChanged() { + height = WeekdayMeasureHelper.computeWidthsAndMaxHeight(_weekdays, textPaint, currentWidths) } fun draw(c: Canvas, x: Float, columnWidth: Float) { var midX = x + columnWidth * 0.5f - val offset = WeekdayData.getOffsetByWeekdayType(type) - val widthsOffset = if (weekdayWidths === defaultWeekdayWidths) offset else 0 - - val weekdays = weekdayData.weekdays val textY = height for (i in 0 until 7) { - val text = weekdays[i + offset] - val width = weekdayWidths[i + widthsOffset] + val text = _weekdays[i] + val width = currentWidths[i] val textX = midX - width * 0.5f From 8da5f8d6fb124904aa0f5e44071cf568af38f506 Mon Sep 17 00:00:00 2001 From: Oleh Date: Fri, 23 Jun 2023 17:46:59 +0300 Subject: [PATCH 6/7] Add weekdays section to README.md --- README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/README.md b/README.md index c53aa48..a534355 100644 --- a/README.md +++ b/README.md @@ -204,6 +204,21 @@ observer.setLifecycle(lifecycle) // If we want to unregister the observer on des observer.register() // The observer should be registered manually. ``` +## Weekdays + +By default, the calendar will use localized weekdays in 'short' format. The format of localized weekdays can be changed via `weekdayType`. Currently there's only two options: + +- WeekdayType.SHORT. If user locale is English, weekdays will look like: Mon, Tue, Wed, Thu, Fri, Sat, Sun +- WeekdayType.NARROW. If user locale is English, weekdays will look like: M, T, W, T, F, S, S. **This option only works when API level >= 24. If API level is lower, weekdays in 'short' format will be used. + +If you want to change weekdays, it can be done via `weekdays`. + +```kotlin +rangeCalendarView.weekdays = arrayOf("0", "1", "2", "3", "4", "5", "6") +``` + +If you want back to using localized weekdays, pass null to `weekdays`. + ## Other - `getVibrateOnSelectingCustomRange()/setVibrateOnSelectingCustomRange()` to get or set whether the device should From 039d97de9f3a284d853e147b18d3f539322513ba Mon Sep 17 00:00:00 2001 From: Oleh Date: Fri, 23 Jun 2023 17:49:16 +0300 Subject: [PATCH 7/7] Fix README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index a534355..6335d6f 100644 --- a/README.md +++ b/README.md @@ -208,10 +208,10 @@ observer.register() // The observer should be registered manually. By default, the calendar will use localized weekdays in 'short' format. The format of localized weekdays can be changed via `weekdayType`. Currently there's only two options: -- WeekdayType.SHORT. If user locale is English, weekdays will look like: Mon, Tue, Wed, Thu, Fri, Sat, Sun -- WeekdayType.NARROW. If user locale is English, weekdays will look like: M, T, W, T, F, S, S. **This option only works when API level >= 24. If API level is lower, weekdays in 'short' format will be used. +- `WeekdayType.SHORT`. If user locale is English, weekdays will look like: Mon, Tue, Wed, Thu, Fri, Sat, Sun +- `WeekdayType.NARROW`. If user locale is English, weekdays will look like: M, T, W, T, F, S, S. **This option only works when API level >= 24. If API level is lower, weekdays in 'short' format will be used.** -If you want to change weekdays, it can be done via `weekdays`. +If you want to change weekdays, it can be done via `weekdays`: ```kotlin rangeCalendarView.weekdays = arrayOf("0", "1", "2", "3", "4", "5", "6")