Skip to content

Commit 1e6f997

Browse files
committed
add buttons row selected item animation
1 parent 8875d1d commit 1e6f997

File tree

12 files changed

+94
-33
lines changed

12 files changed

+94
-33
lines changed

app/src/androidTest/java/com/example/util/simpletimetracker/StatisticsDetailTest.kt

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -714,8 +714,10 @@ class StatisticsDetailTest : BaseUiTest() {
714714
timeEnded = calendar.timeInMillis + TimeUnit.HOURS.toMillis(2),
715715
tagNames = listOf(tag2),
716716
)
717-
calendar = Calendar.getInstance()
718-
.apply { add(Calendar.MONTH, -1) }
717+
calendar = Calendar.getInstance().apply {
718+
add(Calendar.MONTH, -1)
719+
set(Calendar.DAY_OF_MONTH, 15)
720+
}
719721
testUtils.addRecord(
720722
typeName = name,
721723
timeStarted = calendar.timeInMillis,
@@ -729,7 +731,7 @@ class StatisticsDetailTest : BaseUiTest() {
729731
checkPreview(color, icon, name)
730732

731733
// Switch range
732-
clickOnCurrentDate()
734+
clickOnCurrentSelectedDate()
733735
clickOnViewWithText(coreR.string.range_month)
734736

735737
// Daily calendar

buildSrc/src/main/kotlin/com/example/util/simpletimetracker/Base.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ object Base {
55
const val namespace = "com.example.util.simpletimetracker"
66

77
// Raise by 2 to account for wear version code.
8-
const val versionCode = 113
8+
const val versionCode = 115
99
const val versionName = "1.51"
1010
const val minSDK = 21
1111
const val currentSDK = 35

core/src/main/java/com/example/util/simpletimetracker/core/delegates/iconSelection/viewDelegate/IconSelectionViewDelegate.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ object IconSelectionViewDelegate {
185185
data: List<ViewHolderType>,
186186
layout: IconSelectionLayoutBinding,
187187
) = with(layout) {
188-
btnIconSelectionSwitch.adapter.replace(data)
188+
btnIconSelectionSwitch.replace(data)
189189
}
190190

191191
private fun updateIconsState(

features/feature_base_adapter/src/main/java/com/example/util/simpletimetracker/feature_base_adapter/buttonsRow/ButtonsRowAdapterDelegate.kt

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,7 @@ fun createButtonsRowAdapterDelegate(
1919

2020
setMargins(top = item.marginTopDp)
2121
// Replace without animation on reuse for different buttons.
22-
if (tag != item.block) {
23-
adapter.replaceFast(item.data)
24-
} else {
25-
adapter.replace(item.data)
26-
}
22+
replace(item.data, isFast = tag != item.block)
2723
listener = { onClick(item.block, it) }
2824
tag = item.block
2925
}

features/feature_base_adapter/src/main/java/com/example/util/simpletimetracker/feature_base_adapter/buttonsRow/view/ButtonsRowView.kt

Lines changed: 68 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
package com.example.util.simpletimetracker.feature_base_adapter.buttonsRow.view
22

3+
import android.animation.ValueAnimator
34
import android.content.Context
45
import android.util.AttributeSet
56
import android.widget.FrameLayout
6-
import androidx.core.content.ContextCompat
7+
import androidx.core.view.updateLayoutParams
78
import com.example.util.simpletimetracker.feature_base_adapter.BaseRecyclerAdapter
9+
import com.example.util.simpletimetracker.feature_base_adapter.ViewHolderType
810
import com.example.util.simpletimetracker.feature_base_adapter.databinding.ButtonsRowViewLayoutBinding
9-
import com.example.util.simpletimetracker.feature_views.R
1011
import com.example.util.simpletimetracker.feature_views.extension.layoutInflater
1112
import com.google.android.flexbox.FlexDirection
1213
import com.google.android.flexbox.FlexWrap
@@ -25,30 +26,45 @@ class ButtonsRowView @JvmOverloads constructor(
2526

2627
var listener: ((ButtonsRowViewData) -> Unit)? = null
2728

28-
val adapter: BaseRecyclerAdapter by lazy {
29+
private val adapter: BaseRecyclerAdapter by lazy {
2930
BaseRecyclerAdapter(
30-
createButtonsRowViewInternalAdapterDelegate(selectedColor, ::onItemClick),
31+
createButtonsRowViewInternalAdapterDelegate(::onItemClick),
3132
)
3233
}
3334

3435
private val binding = ButtonsRowViewLayoutBinding.inflate(layoutInflater, this)
3536

36-
private val selectedColor by lazy {
37-
var defaultColor = ContextCompat.getColor(context, R.color.colorPrimary)
38-
runCatching {
39-
context.theme?.obtainStyledAttributes(intArrayOf(R.attr.appActiveColor))?.run {
40-
defaultColor = getColor(0, defaultColor)
41-
recycle()
42-
}
43-
}
44-
defaultColor
37+
private var selectedPositionAnimator: ValueAnimator? = null
38+
private var updateSelectedPositionOnLayout: Boolean = false
39+
private val selectedPositionAnimationDuration: Float by lazy {
40+
resources.getInteger(android.R.integer.config_shortAnimTime) * 0.85f
4541
}
4642

4743
init {
4844
initRecycler()
4945
initEditMode()
5046
}
5147

48+
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
49+
super.onLayout(changed, left, top, right, bottom)
50+
// In case container is not visible on adapter items replace.
51+
if (updateSelectedPositionOnLayout) {
52+
updateSelectedPositionOnLayout = false
53+
updateSelectedItem(adapter.currentList, isFast = false)
54+
}
55+
}
56+
57+
fun replace(items: List<ViewHolderType>, isFast: Boolean = false) {
58+
if (isFast) {
59+
adapter.replaceFast(items)
60+
} else {
61+
adapter.replace(items)
62+
}
63+
binding.rvButtonsRowView.post {
64+
updateSelectedItem(items, isFast)
65+
}
66+
}
67+
5268
private fun onItemClick(buttonsRowViewData: ButtonsRowViewData) {
5369
listener?.invoke(buttonsRowViewData)
5470
}
@@ -64,6 +80,45 @@ class ButtonsRowView @JvmOverloads constructor(
6480
}
6581
}
6682

83+
private fun updateSelectedItem(
84+
currentList: List<ViewHolderType>,
85+
isFast: Boolean,
86+
) = with(binding) {
87+
val containerWidth = rvButtonsRowView.width
88+
if (containerWidth == 0) {
89+
updateSelectedPositionOnLayout = true
90+
return@with
91+
}
92+
val selectedItemIndex = currentList
93+
.indexOfFirst { it is ButtonsRowViewData && it.isSelected }
94+
.takeUnless { it == -1 }
95+
?: return@with
96+
val itemsCount = currentList.size.takeIf { it > 0 }
97+
?: return@with
98+
val itemWidth = containerWidth / itemsCount
99+
100+
if (btnButtonsRowSelected.width != itemWidth) {
101+
btnButtonsRowSelected.updateLayoutParams { width = itemWidth }
102+
}
103+
104+
val currentPosition = btnButtonsRowSelected.translationX
105+
val newPosition = itemWidth.toFloat() * selectedItemIndex
106+
if (currentPosition != newPosition) {
107+
if (isFast) {
108+
btnButtonsRowSelected.translationX = newPosition
109+
} else {
110+
selectedPositionAnimator?.cancel()
111+
val animator = ValueAnimator.ofFloat(currentPosition, newPosition)
112+
selectedPositionAnimator = animator
113+
animator.duration = selectedPositionAnimationDuration.toLong()
114+
animator.addUpdateListener { value ->
115+
btnButtonsRowSelected.translationX = value.animatedValue as Float
116+
}
117+
animator.start()
118+
}
119+
}
120+
}
121+
67122
private fun initEditMode() {
68123
if (isInEditMode) {
69124
listOf(

features/feature_base_adapter/src/main/java/com/example/util/simpletimetracker/feature_base_adapter/buttonsRow/view/ButtonsRowViewInternalAdapterDelegate.kt

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
package com.example.util.simpletimetracker.feature_base_adapter.buttonsRow.view
22

3-
import android.graphics.Color
43
import android.util.TypedValue
54
import com.example.util.simpletimetracker.feature_base_adapter.createRecyclerBindingAdapterDelegate
65
import com.example.util.simpletimetracker.feature_views.extension.setOnClickWith
76
import com.example.util.simpletimetracker.feature_base_adapter.databinding.ButtonsRowItemLayoutBinding as Binding
87
import com.example.util.simpletimetracker.feature_base_adapter.buttonsRow.view.ButtonsRowViewData as ViewData
98

109
internal fun createButtonsRowViewInternalAdapterDelegate(
11-
selectedColor: Int,
1210
onItemClick: ((ViewData) -> Unit),
1311
) = createRecyclerBindingAdapterDelegate<ViewData, Binding>(
1412
Binding::inflate,
@@ -17,11 +15,8 @@ internal fun createButtonsRowViewInternalAdapterDelegate(
1715
with(binding) {
1816
item as ViewData
1917

20-
val color = if (item.isSelected) selectedColor else Color.TRANSPARENT
21-
2218
btnButtonsRowView.tag = if (item.isSelected) ViewData.SELECTED_BUTTON_TEST_TAG else ""
2319
btnButtonsRowView.text = item.name
24-
btnButtonsRowView.setBackgroundColor(color)
2520
btnButtonsRowView.setTextSize(
2621
TypedValue.COMPLEX_UNIT_SP,
2722
item.textSizeSp?.toFloat() ?: 14f,

features/feature_base_adapter/src/main/res/layout/buttons_row_view_layout.xml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,19 @@
1717
app:backgroundTint="?appInactiveColor"
1818
app:cornerRadius="@dimen/button_corner_radius" />
1919

20+
<com.google.android.material.button.MaterialButton
21+
android:id="@+id/btnButtonsRowSelected"
22+
style="@style/Widget.MaterialComponents.Button.UnelevatedButton"
23+
android:layout_width="match_parent"
24+
android:layout_height="@dimen/button_height"
25+
android:layout_marginStart="8dp"
26+
android:layout_marginEnd="8dp"
27+
android:clickable="false"
28+
android:focusable="false"
29+
app:backgroundTint="?appActiveColor"
30+
app:cornerRadius="@dimen/button_corner_radius"
31+
tools:layout_width="100dp" />
32+
2033
<androidx.recyclerview.widget.RecyclerView
2134
android:id="@+id/rvButtonsRowView"
2235
android:layout_width="match_parent"

features/feature_change_activity_filter/src/main/java/com/example/util/simpletimetracker/feature_change_activity_filter/view/ChangeActivityFilterFragment.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ class ChangeActivityFilterFragment :
135135
filterPreview.observeOnce(viewLifecycleOwner, ::updateUi)
136136
filterPreview.observe(::updatePreview)
137137
colors.observe(colorsAdapter::replace)
138-
filterTypeViewData.observe(buttonsChangeActivityFilterType.adapter::replace)
138+
filterTypeViewData.observe(buttonsChangeActivityFilterType::replace)
139139
viewData.observe(::updateTypes)
140140
chooserState.observe(::updateChooserState)
141141
keyboardVisibility.observe { visible ->

features/feature_change_goals/views/src/main/java/com/example/util/simpletimetracker/feature_change_goals/views/GoalsViewDelegate.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ object GoalsViewDelegate {
161161

162162
if (goal.subtypeItems.isNotEmpty()) {
163163
view.btnChangeRecordTypeGoalSubtype.visible = true
164-
view.btnChangeRecordTypeGoalSubtype.adapter.replace(goal.subtypeItems)
164+
view.btnChangeRecordTypeGoalSubtype.replace(goal.subtypeItems)
165165
} else {
166166
view.btnChangeRecordTypeGoalSubtype.visible = false
167167
}

features/feature_dialogs/src/main/java/com/example/util/simpletimetracker/feature_dialogs/cardSize/view/CardSizeDialogFragment.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ class CardSizeDialogFragment : BaseBottomSheetFragment<Binding>() {
6464

6565
override fun initViewModel(): Unit = with(viewModel) {
6666
recordTypes.observe(recordTypesAdapter::replace)
67-
buttons.observe(binding.buttonsCardSize.adapter::replace)
67+
buttons.observe(binding.buttonsCardSize::replace)
6868
defaultButton.observe(::updateDefaultButton)
6969
}
7070

0 commit comments

Comments
 (0)