From d41f8b311177002bd28bc7e7240c1b0db7addf74 Mon Sep 17 00:00:00 2001 From: AnastasiyaK97 Date: Fri, 5 Aug 2022 23:42:43 +0300 Subject: [PATCH 1/3] =?UTF-8?q?INTERNAL-301=20+=20INTERNAL-302:=20=D0=94?= =?UTF-8?q?=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=20=D1=84=D0=B8=D0=BB?= =?UTF-8?q?=D1=8C=D1=82=D1=80=20=D0=B4=D0=BB=D1=8F=20=D0=B2=D1=8B=D0=B1?= =?UTF-8?q?=D0=BE=D1=80=D0=B0=20=D0=BC=D0=B8=D0=BD=D0=B8=D0=BC=D0=B0=D0=BB?= =?UTF-8?q?=D1=8C=D0=BD=D0=BE=D0=B3=D0=BE=20=D0=B8=20=D0=BC=D0=B0=D0=BA?= =?UTF-8?q?=D1=81=D0=B8=D0=BC=D0=B0=D0=BB=D1=8C=D0=BD=D0=BE=D0=B3=D0=BE=20?= =?UTF-8?q?=D0=B7=D0=BD=D0=B0=D1=87=D0=B5=D0=BD=D0=B8=D1=8F=20=D0=B8=D0=B7?= =?UTF-8?q?=20=D0=B4=D0=B8=D0=B0=D0=BF=D0=BE=D0=B7=D0=BE=D0=BD=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- base-filters/README.md | 64 ++++++- .../base_filters/range/FilterRangeSlider.kt | 170 ++++++++++++++++++ .../base_filters/range/HintInputView.kt | 42 +++++ .../base_filters/range/RangeChoiceView.kt | 167 +++++++++++++++++ .../range/model/FilterRangeItem.kt | 34 ++++ .../res/drawable/background_hint_input.xml | 9 + .../cursor_background_text_input_view.xml | 11 ++ .../src/main/res/layout/range_choice_view.xml | 52 ++++++ .../src/main/res/layout/view_hint_input.xml | 25 +++ base-filters/src/main/res/values/attrs.xml | 27 +++ base-filters/src/main/res/values/colors.xml | 8 + base-filters/src/main/res/values/strings.xml | 5 + base-filters/src/main/res/values/styles.xml | 51 ++++++ 13 files changed, 663 insertions(+), 2 deletions(-) create mode 100644 base-filters/src/main/java/ru/touchin/roboswag/base_filters/range/FilterRangeSlider.kt create mode 100644 base-filters/src/main/java/ru/touchin/roboswag/base_filters/range/HintInputView.kt create mode 100644 base-filters/src/main/java/ru/touchin/roboswag/base_filters/range/RangeChoiceView.kt create mode 100644 base-filters/src/main/java/ru/touchin/roboswag/base_filters/range/model/FilterRangeItem.kt create mode 100644 base-filters/src/main/res/drawable/background_hint_input.xml create mode 100644 base-filters/src/main/res/drawable/cursor_background_text_input_view.xml create mode 100644 base-filters/src/main/res/layout/range_choice_view.xml create mode 100644 base-filters/src/main/res/layout/view_hint_input.xml create mode 100644 base-filters/src/main/res/values/colors.xml create mode 100644 base-filters/src/main/res/values/strings.xml diff --git a/base-filters/README.md b/base-filters/README.md index bd35f8f..2e66d86 100644 --- a/base-filters/README.md +++ b/base-filters/README.md @@ -4,6 +4,7 @@ 1. Выбор одного/нескольких из доступных значений списка 2. Выбор одного/нескольких значений из перечня тегов +3. Выбор минимального и максимального значения из диапозона # Использование @@ -85,8 +86,8 @@ val selectorView = ListSelectionView(newContext) ## 2. Выбор одного/нескольких значений из перечня тегов -`TagLayoutView` - view-контейнер для тегов -`TagView` - view для тега. Кастомная разметка для тега должна содержать в корне `TagView` +* `TagLayoutView` - view-контейнер для тегов +* `TagView` - view для тега. Кастомная разметка для тега должна содержать в корне `TagView` ### Как использовать ``` kotlin @@ -111,3 +112,62 @@ binding.tagItemLayout * `setSpacing(Int)`, `setSpacingHorizontal(Int`) и мsetSpacingVertical(Int)` можно использовать для настройки расстояния между тегами. По умолчанию - 0 * `onMoreValuesAction(FilterMoreAction)` и `onPropertySelectedAction(PropertySelectedAction)` используются для передачи колбэков на клик по тегу типа "Еще" и обычного тега соответственно * после вызова конфигурационных методов обязательно необходимо вызать метод `build(FilterItem)` + + +## 3. Выбор минимального и максимального значения из диапозона + +* `RangeChoiceView` - контейнер для слайдера и редактируемых полей +* `FilterRangeSlider` - слайдер - Можно использовать как отдельный элемент +* `HintInputView` - view для редактируемого поля начала и окончания диапозона + +### Как использовать + +В разметке +``` xml + +``` + +Настройка в коде +``` kotlin +fun setupValues(item: FilterRangeItem) { + binding.rangeValuesTest.setupRangeValues( + rangeFilterItem = item, + onChangeCallback = callback + ) + } + + fun resetValues() { + binding.rangeValuesTest.resetRangeValue() + } +``` +### Конфигурации +Вся конфигурация вьюх осуществляется через стили: +* Для `RangeChoiceView`: + * `filterRange_sliderMargin` - расстояние от слайдера до редактируемых полей + * `filterRange_startHint` - ссылка на строку с текстом подсказки в редактируемом поле для начального значения + * `filterRange_endHint` - ссылка на строку с текстом подсказки в редактируемом поле для конечного значения + * `filterRange_theme` - ссылка на тему +* В теме: + * атрибут `filterRange_sliderStyle` - ссылка на стиль слайдера + * атрибут `filterRange_hintViewStyle` - ссылка на стиль `HintInputView` + * атрибут `filterRange_hintTextStyle` - ссылка на стиль `TextView` внутри `HintInputView` + * атрибут `filterRange_valueEditTextStyle` - ссылка на стиль `EditText` внутри `HintInputView` +* Для `FilterRangeSlider`: + * `trackColorActive` + * `trackColorInactive` + * `trackHeight` + * `thumbElevation` + * `thumbColor` + * `labelBehavior` + * `haloRadius` + * `filterRange_stepTextAppearance` + * `filterRange_activeTickColor` + * `filterRange_inactiveTickColor` + * `filterRange_stepValueMarginTop` + * `filterRange_sliderPointSize` + * `filterRange_pointShape` diff --git a/base-filters/src/main/java/ru/touchin/roboswag/base_filters/range/FilterRangeSlider.kt b/base-filters/src/main/java/ru/touchin/roboswag/base_filters/range/FilterRangeSlider.kt new file mode 100644 index 0000000..1f3a371 --- /dev/null +++ b/base-filters/src/main/java/ru/touchin/roboswag/base_filters/range/FilterRangeSlider.kt @@ -0,0 +1,170 @@ +package ru.touchin.roboswag.base_filters.range + +import android.content.Context +import android.graphics.Canvas +import android.graphics.Paint +import android.graphics.Paint.Cap +import android.util.AttributeSet +import android.widget.TextView +import androidx.annotation.StyleRes +import androidx.core.content.withStyledAttributes +import com.google.android.material.shape.CornerFamily +import com.google.android.material.shape.MaterialShapeDrawable +import com.google.android.material.shape.ShapeAppearanceModel +import com.google.android.material.slider.RangeSlider +import ru.touchin.roboswag.base_filters.R +import ru.touchin.roboswag.components.utils.getColorSimple + +class FilterRangeSlider @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0, + defStyleRes: Int = 0 +) : RangeSlider(context, attrs, defStyleAttr) { + + var points: List? = null + set(value) { + field = value?.sorted()?.filter { it > valueFrom && it < valueTo } + } + + private val innerThumbRadius: Int = thumbRadius + + private var stepValueMarginTop = 0f + private var inactiveTickColor: Int = context.getColorSimple(R.color.slider_point_inactive) + private var activeTickColor: Int = context.getColorSimple(R.color.slider_point_active) + private var sliderPointSize: Float = 5f + @StyleRes private var stepTextAppearance: Int = -1 + private var shape: Shape = Shape.CIRCLE + + private var trackCenterY: Float = -1F + + init { + // Set original thumb radius to zero to draw custom one on top + thumbRadius = 0 + + context.withStyledAttributes(attrs, R.styleable.FilterRangeSlider, defStyleAttr, defStyleRes) { + stepValueMarginTop = getDimension(R.styleable.FilterRangeSlider_filterRange_stepValueMarginTop, stepValueMarginTop) + inactiveTickColor = getColor(R.styleable.FilterRangeSlider_filterRange_inactiveTickColor, inactiveTickColor) + activeTickColor = getColor(R.styleable.FilterRangeSlider_filterRange_activeTickColor, activeTickColor) + sliderPointSize = getDimension(R.styleable.FilterRangeSlider_filterRange_sliderPointSize, sliderPointSize) + stepTextAppearance = getResourceId(R.styleable.FilterRangeSlider_filterRange_stepTextAppearance, -1) + shape = Shape.values()[getInt(R.styleable.FilterRangeSlider_filterRange_pointShape, Shape.CIRCLE.ordinal)] + } + } + + private val thumbDrawable = MaterialShapeDrawable().apply { + shadowCompatibilityMode = MaterialShapeDrawable.SHADOW_COMPAT_MODE_ALWAYS + setBounds(0, 0, innerThumbRadius * 2, innerThumbRadius * 2) + elevation = thumbElevation + state = drawableState + fillColor = thumbTintList + shapeAppearanceModel = ShapeAppearanceModel + .builder() + .setAllCorners(shape.value, innerThumbRadius.toFloat()) + .build() + } + + private val inactiveTicksPaint = getDefaultTickPaint().apply { color = inactiveTickColor } + + private val activeTicksPaint = getDefaultTickPaint().apply { color = activeTickColor } + + private fun getDefaultTickPaint() = Paint().apply { + isAntiAlias = true + style = Paint.Style.STROKE + strokeCap = Cap.ROUND + strokeWidth = sliderPointSize + } + + // Using TextView as a bridge to get text params + private val stepValuePaint: Paint = TextView(context) + .apply { stepTextAppearance.takeIf { it != -1 }?.let { setTextAppearance(it) } } + .let { textView -> + Paint().apply { + isAntiAlias = true + color = textView.currentTextColor + textSize = textView.textSize + typeface = textView.typeface + textAlign = Paint.Align.CENTER + } + } + + override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec) + + trackCenterY = measuredHeight / 2F + + val height = trackCenterY + trackHeight / 2F + stepValueMarginTop + stepValuePaint.textSize + setMeasuredDimension(measuredWidth, height.toInt()) + } + + override fun onDraw(canvas: Canvas) { + super.onDraw(canvas) + + drawTicks(canvas) + drawThumb(canvas) + drawStepValues(canvas) + } + + private fun drawTicks(canvas: Canvas) { + if (points.isNullOrEmpty()) return + + val ticksCoordinates = mutableListOf() + points?.forEach { point -> + ticksCoordinates.add(normalizeValue(point.toFloat()) * trackWidth + trackSidePadding) + ticksCoordinates.add(trackCenterY) + } + + val leftPointsSize = points?.count { it < values[0] } ?: 0 + val rightPointSize = points?.count { it > values[1] } ?: 0 + val activePointSize = (points?.size ?: 0) - leftPointsSize - rightPointSize + + // Draw inactive ticks to the left of the smallest thumb. + canvas.drawPoints(ticksCoordinates.toFloatArray(), 0, leftPointsSize * 2, inactiveTicksPaint) + + // Draw active ticks between the thumbs. + canvas.drawPoints( + ticksCoordinates.toFloatArray(), + leftPointsSize * 2, + activePointSize * 2, + activeTicksPaint + ) + + // Draw inactive ticks to the right of the largest thumb. + canvas.drawPoints( + ticksCoordinates.toFloatArray(), + leftPointsSize * 2 + activePointSize * 2, + rightPointSize * 2, + inactiveTicksPaint + ) + } + + private fun drawThumb(canvas: Canvas) { + for (value in values) { + canvas.save() + canvas.translate( + (trackSidePadding + (normalizeValue(value) * trackWidth).toInt() - innerThumbRadius).toFloat(), + trackCenterY - innerThumbRadius + ) + thumbDrawable.draw(canvas) + canvas.restore() + } + } + + private fun drawStepValues(canvas: Canvas) { + points?.forEach { point -> + canvas.drawText( + point.toString(), + normalizeValue(point.toFloat()) * trackWidth + trackSidePadding, + trackCenterY + trackHeight / 2F + stepValueMarginTop + stepValuePaint.textSize - 3F, + stepValuePaint + ) + } + } + + private fun normalizeValue(value: Float) = (value - valueFrom) / (valueTo - valueFrom) + + private enum class Shape(val value: Int) { + CIRCLE(CornerFamily.ROUNDED), + CUT(CornerFamily.CUT) + } +} diff --git a/base-filters/src/main/java/ru/touchin/roboswag/base_filters/range/HintInputView.kt b/base-filters/src/main/java/ru/touchin/roboswag/base_filters/range/HintInputView.kt new file mode 100644 index 0000000..e7934c3 --- /dev/null +++ b/base-filters/src/main/java/ru/touchin/roboswag/base_filters/range/HintInputView.kt @@ -0,0 +1,42 @@ +package ru.touchin.roboswag.base_filters.range + +import android.content.Context +import android.util.AttributeSet +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.constraintlayout.widget.ConstraintLayout +import androidx.core.content.withStyledAttributes +import androidx.core.view.updateLayoutParams +import ru.touchin.roboswag.base_filters.R +import ru.touchin.roboswag.base_filters.databinding.ViewHintInputBinding +import ru.touchin.roboswag.components.utils.px + +class HintInputView @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0, + defStyleRes: Int = 0 +) : ConstraintLayout(context, attrs, defStyleAttr, defStyleRes) { + + private val binding = ViewHintInputBinding.inflate(LayoutInflater.from(context), this) + + var inputText: String = "" + set(value) { + setText(value) + field = value + } + + fun setHint(value: String?) { + binding.startHint.text = value.orEmpty() + } + + private fun setText(value: String) { + binding.editText.run { + setText(value) + setSelection(text?.length ?: 0) + } + } + + fun getEditText() = binding.editText + +} diff --git a/base-filters/src/main/java/ru/touchin/roboswag/base_filters/range/RangeChoiceView.kt b/base-filters/src/main/java/ru/touchin/roboswag/base_filters/range/RangeChoiceView.kt new file mode 100644 index 0000000..644c270 --- /dev/null +++ b/base-filters/src/main/java/ru/touchin/roboswag/base_filters/range/RangeChoiceView.kt @@ -0,0 +1,167 @@ +package ru.touchin.roboswag.base_filters.range + +import android.content.Context +import android.util.AttributeSet +import android.view.ContextThemeWrapper +import android.view.LayoutInflater +import android.view.inputmethod.EditorInfo +import androidx.constraintlayout.widget.ConstraintLayout +import androidx.core.content.withStyledAttributes +import androidx.core.view.updateLayoutParams +import com.google.android.material.slider.RangeSlider +import ru.touchin.roboswag.base_filters.R +import ru.touchin.roboswag.base_filters.databinding.RangeChoiceViewBinding +import ru.touchin.roboswag.base_filters.range.model.FilterRangeItem +import ru.touchin.roboswag.base_filters.range.model.SelectedValues +import kotlin.properties.Delegates + +typealias FilterRangeChanged = (FilterRangeItem) -> Unit + +class RangeChoiceView @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0, + defStyleRes: Int = 0 +) : ConstraintLayout(context, attrs, defStyleAttr, defStyleRes) { + + private val defaultTheme = R.style.Theme_FilterRangeSlider + private var binding: RangeChoiceViewBinding by Delegates.notNull() + + private var valueChangedAction: FilterRangeChanged? = null + + private var rangeItem: FilterRangeItem? = null + set(value) { + field = value + + binding.fromInput.inputText = value?.selectedValues?.min?.toString() + ?: value?.start?.toString().orEmpty() + binding.toInput.inputText = value?.selectedValues?.max?.toString() + ?: value?.end?.toString().orEmpty() + + binding.rangeSlider.run { + values = listOf( + value?.selectedValues?.min?.toFloat() ?: value?.start?.toFloat(), + value?.selectedValues?.max?.toFloat() ?: value?.end?.toFloat() + ) + } + } + + init { + context.withStyledAttributes(attrs, R.styleable.FilterRangeChoice, defStyleAttr, defStyleRes) { + val theme = getResourceId(R.styleable.FilterRangeChoice_filterRange_theme, defaultTheme) + val themeContext = ContextThemeWrapper(context, theme) + binding = RangeChoiceViewBinding.inflate(LayoutInflater.from(themeContext), this@RangeChoiceView) + + binding.fromInput.setHint(getString(R.styleable.FilterRangeChoice_filterRange_startHint)) + binding.toInput.setHint(getString(R.styleable.FilterRangeChoice_filterRange_endHint)) + binding.rangeSliderGuideline.updateLayoutParams { + topMargin = getDimension(R.styleable.FilterRangeChoice_filterRange_sliderMargin, 0f).toInt() + } + } + } + + fun setupRangeValues( + rangeFilterItem: FilterRangeItem, + onChangeCallback: FilterRangeChanged + ) { + rangeItem = rangeFilterItem + valueChangedAction = onChangeCallback + + with(binding) { + addChangeValuesListener() + setupRangeSlider(rangeFilterItem) + } + } + + fun resetRangeValue() { + rangeItem = rangeItem?.resetSelectedValues() + } + + private fun addChangeValuesListener() { + binding.fromInput.addChangeValueListener { rangeItem?.setValue(selectedMinValue = it.toIntOrNull()) } + binding.toInput.addChangeValueListener { rangeItem?.setValue(selectedMaxValue = it.toIntOrNull()) } + } + + private fun setupRangeSlider(rangeFilterItem: FilterRangeItem) { + with(binding) { + rangeSlider.apply { + valueFrom = rangeFilterItem.start.toFloat() + valueTo = rangeFilterItem.end.toFloat() + points = rangeFilterItem.intermediates + } + + rangeSlider.addOnChangeListener { _, _, _ -> + fromInput.inputText = rangeSlider.values[0].toInt().toString() + toInput.inputText = rangeSlider.values[1].toInt().toString() + } + + rangeSlider.addOnSliderTouchListener(object : RangeSlider.OnSliderTouchListener { + @SuppressWarnings("detekt.EmptyFunctionBlock") + override fun onStartTrackingTouch(slider: RangeSlider) { + } + + override fun onStopTrackingTouch(slider: RangeSlider) { + binding.rangeSlider.apply { + when(focusedThumbIndex) { + 0 -> { + rangeItem = rangeItem?.setValue(selectedMinValue = from().toInt()) + rangeItem?.let { valueChangedAction?.invoke(it) } + } + 1 -> { + rangeItem = rangeItem?.setValue(selectedMaxValue = to().toInt()) + rangeItem?.let { valueChangedAction?.invoke(it) } + } + } + } + } + }) + } + } + + private fun HintInputView.addChangeValueListener(updateValue: (String) -> FilterRangeItem?) { + getEditText().setOnEditorActionListener { view, actionId, _ -> + if (actionId == EditorInfo.IME_ACTION_DONE) { + rangeItem = updateValue(view.text.toString().filterNot { it.isWhitespace() }) + rangeItem?.let { valueChangedAction?.invoke(it) } + } + false + } + } + + private fun RangeSlider.from() = values[0].toInt().toString() + + private fun RangeSlider.to() = values[1].toInt().toString() + + @SuppressWarnings("detekt.ComplexMethod") + private fun FilterRangeItem.setValue( + selectedMaxValue: Int? = selectedValues?.max, + selectedMinValue: Int? = selectedValues?.min + ): FilterRangeItem { + val isMaxValueUpdated = selectedMaxValue != selectedValues?.max + val isMinValueUpdated = selectedMinValue != selectedValues?.min + + val isMinValueOutOfRange = selectedMinValue != null && isMinValueUpdated && selectedMinValue > selectedMaxValue ?: end + val isMaxValueOutOfRange = selectedMaxValue != null && isMaxValueUpdated && selectedMaxValue < selectedMinValue ?: start + + val updatedValues = when { + selectedMaxValue == end && selectedMinValue == start -> null + isMinValueOutOfRange -> SelectedValues( + max = selectedMaxValue ?: end, + min = selectedMaxValue ?: end + ) + isMaxValueOutOfRange -> SelectedValues( + max = selectedMinValue ?: start, + min = selectedMinValue ?: start + ) + else -> SelectedValues( + max = selectedMaxValue?.takeIf { it < end } ?: end, + min = selectedMinValue?.takeIf { it > start } ?: start + ) + } + + return copyWithSelectedValue(selectedValues = updatedValues) + } + + private fun FilterRangeItem.resetSelectedValues() = copyWithSelectedValue(selectedValues = null) + +} diff --git a/base-filters/src/main/java/ru/touchin/roboswag/base_filters/range/model/FilterRangeItem.kt b/base-filters/src/main/java/ru/touchin/roboswag/base_filters/range/model/FilterRangeItem.kt new file mode 100644 index 0000000..1c452de --- /dev/null +++ b/base-filters/src/main/java/ru/touchin/roboswag/base_filters/range/model/FilterRangeItem.kt @@ -0,0 +1,34 @@ +package ru.touchin.roboswag.base_filters.range.model + +import android.os.Parcelable +import kotlinx.parcelize.Parcelize + +@Parcelize +open class FilterRangeItem( + val id: String, + val start: Int, + val end: Int, + val title: String, + val intermediates: List? = null, + val step: Int? = null, + val selectedValues: SelectedValues? = null +) : Parcelable { + + fun isCorrectValues() = end > start + + fun copyWithSelectedValue(selectedValues: SelectedValues?) = FilterRangeItem( + id = id, + start = start, + end = end, + title = title, + intermediates = intermediates, + step = step, + selectedValues = selectedValues + ) +} + +@Parcelize +data class SelectedValues( + val max: Int, + val min: Int +) : Parcelable diff --git a/base-filters/src/main/res/drawable/background_hint_input.xml b/base-filters/src/main/res/drawable/background_hint_input.xml new file mode 100644 index 0000000..354e22c --- /dev/null +++ b/base-filters/src/main/res/drawable/background_hint_input.xml @@ -0,0 +1,9 @@ + + + + + + diff --git a/base-filters/src/main/res/drawable/cursor_background_text_input_view.xml b/base-filters/src/main/res/drawable/cursor_background_text_input_view.xml new file mode 100644 index 0000000..4633444 --- /dev/null +++ b/base-filters/src/main/res/drawable/cursor_background_text_input_view.xml @@ -0,0 +1,11 @@ + + + + + + diff --git a/base-filters/src/main/res/layout/range_choice_view.xml b/base-filters/src/main/res/layout/range_choice_view.xml new file mode 100644 index 0000000..6d749ef --- /dev/null +++ b/base-filters/src/main/res/layout/range_choice_view.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + diff --git a/base-filters/src/main/res/layout/view_hint_input.xml b/base-filters/src/main/res/layout/view_hint_input.xml new file mode 100644 index 0000000..1f66eb4 --- /dev/null +++ b/base-filters/src/main/res/layout/view_hint_input.xml @@ -0,0 +1,25 @@ + + + + + + + + diff --git a/base-filters/src/main/res/values/attrs.xml b/base-filters/src/main/res/values/attrs.xml index bd6618a..32e1c69 100644 --- a/base-filters/src/main/res/values/attrs.xml +++ b/base-filters/src/main/res/values/attrs.xml @@ -6,4 +6,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/base-filters/src/main/res/values/colors.xml b/base-filters/src/main/res/values/colors.xml new file mode 100644 index 0000000..aed3bb2 --- /dev/null +++ b/base-filters/src/main/res/values/colors.xml @@ -0,0 +1,8 @@ + + + #B9B9B9 + #E35100 + #EE9766 + #E7E7E7 + #E35100 + diff --git a/base-filters/src/main/res/values/strings.xml b/base-filters/src/main/res/values/strings.xml new file mode 100644 index 0000000..6f75b53 --- /dev/null +++ b/base-filters/src/main/res/values/strings.xml @@ -0,0 +1,5 @@ + + + от + до + diff --git a/base-filters/src/main/res/values/styles.xml b/base-filters/src/main/res/values/styles.xml index 3520002..66d37ff 100644 --- a/base-filters/src/main/res/values/styles.xml +++ b/base-filters/src/main/res/values/styles.xml @@ -18,4 +18,55 @@ 16dp + + + + + + + + + + + + From 6eeea0351a61fa6efc53d948e75adfcf4d4adb1e Mon Sep 17 00:00:00 2001 From: AnastasiyaK97 Date: Mon, 19 Sep 2022 19:29:51 +0300 Subject: [PATCH 2/3] fix static --- .../ru/touchin/roboswag/base_filters/range/HintInputView.kt | 5 ----- 1 file changed, 5 deletions(-) diff --git a/base-filters/src/main/java/ru/touchin/roboswag/base_filters/range/HintInputView.kt b/base-filters/src/main/java/ru/touchin/roboswag/base_filters/range/HintInputView.kt index e7934c3..b0ec126 100644 --- a/base-filters/src/main/java/ru/touchin/roboswag/base_filters/range/HintInputView.kt +++ b/base-filters/src/main/java/ru/touchin/roboswag/base_filters/range/HintInputView.kt @@ -3,13 +3,8 @@ package ru.touchin.roboswag.base_filters.range import android.content.Context import android.util.AttributeSet import android.view.LayoutInflater -import android.view.ViewGroup import androidx.constraintlayout.widget.ConstraintLayout -import androidx.core.content.withStyledAttributes -import androidx.core.view.updateLayoutParams -import ru.touchin.roboswag.base_filters.R import ru.touchin.roboswag.base_filters.databinding.ViewHintInputBinding -import ru.touchin.roboswag.components.utils.px class HintInputView @JvmOverloads constructor( context: Context, From a475bf787ae7e006baf266db01a8a42529958c92 Mon Sep 17 00:00:00 2001 From: Grigorii Date: Thu, 6 Oct 2022 20:43:50 +0300 Subject: [PATCH 3/3] Small refactor --- .../base_filters/range/FilterRangeSlider.kt | 29 ++++++++++--------- .../base_filters/range/HintInputView.kt | 6 ++-- .../base_filters/range/RangeChoiceView.kt | 15 +++++----- 3 files changed, 28 insertions(+), 22 deletions(-) diff --git a/base-filters/src/main/java/ru/touchin/roboswag/base_filters/range/FilterRangeSlider.kt b/base-filters/src/main/java/ru/touchin/roboswag/base_filters/range/FilterRangeSlider.kt index 1f3a371..0fdfc1d 100644 --- a/base-filters/src/main/java/ru/touchin/roboswag/base_filters/range/FilterRangeSlider.kt +++ b/base-filters/src/main/java/ru/touchin/roboswag/base_filters/range/FilterRangeSlider.kt @@ -5,9 +5,10 @@ import android.graphics.Canvas import android.graphics.Paint import android.graphics.Paint.Cap import android.util.AttributeSet -import android.widget.TextView import androidx.annotation.StyleRes +import androidx.appcompat.widget.AppCompatTextView import androidx.core.content.withStyledAttributes +import androidx.core.widget.TextViewCompat import com.google.android.material.shape.CornerFamily import com.google.android.material.shape.MaterialShapeDrawable import com.google.android.material.shape.ShapeAppearanceModel @@ -33,7 +34,9 @@ class FilterRangeSlider @JvmOverloads constructor( private var inactiveTickColor: Int = context.getColorSimple(R.color.slider_point_inactive) private var activeTickColor: Int = context.getColorSimple(R.color.slider_point_active) private var sliderPointSize: Float = 5f - @StyleRes private var stepTextAppearance: Int = -1 + + @StyleRes + private var stepTextAppearance: Int = -1 private var shape: Shape = Shape.CIRCLE private var trackCenterY: Float = -1F @@ -76,17 +79,17 @@ class FilterRangeSlider @JvmOverloads constructor( } // Using TextView as a bridge to get text params - private val stepValuePaint: Paint = TextView(context) - .apply { stepTextAppearance.takeIf { it != -1 }?.let { setTextAppearance(it) } } - .let { textView -> - Paint().apply { - isAntiAlias = true - color = textView.currentTextColor - textSize = textView.textSize - typeface = textView.typeface - textAlign = Paint.Align.CENTER - } - } + private val stepValuePaint: Paint = AppCompatTextView(context).apply { + stepTextAppearance.takeIf { it != -1 }?.let { TextViewCompat.setTextAppearance(this, it) } + }.let { textView -> + Paint().apply { + isAntiAlias = true + color = textView.currentTextColor + textSize = textView.textSize + typeface = textView.typeface + textAlign = Paint.Align.CENTER + } + } override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { super.onMeasure(widthMeasureSpec, heightMeasureSpec) diff --git a/base-filters/src/main/java/ru/touchin/roboswag/base_filters/range/HintInputView.kt b/base-filters/src/main/java/ru/touchin/roboswag/base_filters/range/HintInputView.kt index b0ec126..9f860a6 100644 --- a/base-filters/src/main/java/ru/touchin/roboswag/base_filters/range/HintInputView.kt +++ b/base-filters/src/main/java/ru/touchin/roboswag/base_filters/range/HintInputView.kt @@ -3,6 +3,7 @@ package ru.touchin.roboswag.base_filters.range import android.content.Context import android.util.AttributeSet import android.view.LayoutInflater +import android.widget.TextView import androidx.constraintlayout.widget.ConstraintLayout import ru.touchin.roboswag.base_filters.databinding.ViewHintInputBinding @@ -25,6 +26,9 @@ class HintInputView @JvmOverloads constructor( binding.startHint.text = value.orEmpty() } + fun setOnEditorActionListener(listener: TextView.OnEditorActionListener) = + binding.editText.setOnEditorActionListener(listener) + private fun setText(value: String) { binding.editText.run { setText(value) @@ -32,6 +36,4 @@ class HintInputView @JvmOverloads constructor( } } - fun getEditText() = binding.editText - } diff --git a/base-filters/src/main/java/ru/touchin/roboswag/base_filters/range/RangeChoiceView.kt b/base-filters/src/main/java/ru/touchin/roboswag/base_filters/range/RangeChoiceView.kt index 644c270..c03143a 100644 --- a/base-filters/src/main/java/ru/touchin/roboswag/base_filters/range/RangeChoiceView.kt +++ b/base-filters/src/main/java/ru/touchin/roboswag/base_filters/range/RangeChoiceView.kt @@ -1,5 +1,6 @@ package ru.touchin.roboswag.base_filters.range +import android.annotation.SuppressLint import android.content.Context import android.util.AttributeSet import android.view.ContextThemeWrapper @@ -96,13 +97,13 @@ class RangeChoiceView @JvmOverloads constructor( } rangeSlider.addOnSliderTouchListener(object : RangeSlider.OnSliderTouchListener { - @SuppressWarnings("detekt.EmptyFunctionBlock") - override fun onStartTrackingTouch(slider: RangeSlider) { - } + @SuppressLint("RestrictedApi") + override fun onStartTrackingTouch(slider: RangeSlider) = Unit + @SuppressLint("RestrictedApi") override fun onStopTrackingTouch(slider: RangeSlider) { binding.rangeSlider.apply { - when(focusedThumbIndex) { + when (focusedThumbIndex) { 0 -> { rangeItem = rangeItem?.setValue(selectedMinValue = from().toInt()) rangeItem?.let { valueChangedAction?.invoke(it) } @@ -119,7 +120,7 @@ class RangeChoiceView @JvmOverloads constructor( } private fun HintInputView.addChangeValueListener(updateValue: (String) -> FilterRangeItem?) { - getEditText().setOnEditorActionListener { view, actionId, _ -> + setOnEditorActionListener { view, actionId, _ -> if (actionId == EditorInfo.IME_ACTION_DONE) { rangeItem = updateValue(view.text.toString().filterNot { it.isWhitespace() }) rangeItem?.let { valueChangedAction?.invoke(it) } @@ -140,8 +141,8 @@ class RangeChoiceView @JvmOverloads constructor( val isMaxValueUpdated = selectedMaxValue != selectedValues?.max val isMinValueUpdated = selectedMinValue != selectedValues?.min - val isMinValueOutOfRange = selectedMinValue != null && isMinValueUpdated && selectedMinValue > selectedMaxValue ?: end - val isMaxValueOutOfRange = selectedMaxValue != null && isMaxValueUpdated && selectedMaxValue < selectedMinValue ?: start + val isMinValueOutOfRange = selectedMinValue != null && isMinValueUpdated && selectedMinValue > (selectedMaxValue ?: end) + val isMaxValueOutOfRange = selectedMaxValue != null && isMaxValueUpdated && selectedMaxValue < (selectedMinValue ?: start) val updatedValues = when { selectedMaxValue == end && selectedMinValue == start -> null