Ответ 1
Я бы посмотрел на пакет анимации поддержки Google. В частности, https://developer.android.com/reference/android/support/animation/DynamicAnimation#SCROLL_X
Это будет выглядеть примерно так:
SpringAnimation(recyclerView, DynamicAnimation.SCROLL_X, 0f)
.setStartVelocity(1000)
.start()
ОБНОВИТЬ:
Похоже, это тоже не работает. Я посмотрел на некоторые источники для RecyclerView и причина, по которой интерполятор отказов не работает, потому что RecyclerView неправильно использует интерполятор. Там вызов computeScrollDuration
вызывает интерполятор, а затем получает необработанное время анимации в секундах вместо значения как% от общего времени анимации. Это значение также не вполне предсказуемо, я проверил несколько значений и увидел где-то от 100 мс до 250 мс. В любом случае, из того, что я вижу, у вас есть два варианта (я тестировал оба)
-
Пользователь другой библиотеки, такой как https://github.com/EverythingMe/overscroll-decor
-
Внесите свою собственную собственность и используйте анимацию весны:
class ScrollXProperty : FloatPropertyCompat("scrollX") {
override fun setValue(obj: RecyclerView, value: Float) {
obj.scrollBy(value.roundToInt() - getValue(obj).roundToInt(), 0)
}
override fun getValue(obj: RecyclerView): Float =
obj.computeHorizontalScrollOffset().toFloat()
}
Затем в своем отказе замените вызов smoothScrollBy
с изменением:
SpringAnimation(recyclerView, ScrollXProperty())
.setSpring(SpringForce()
.setFinalPosition(0f)
.setStiffness(SpringForce.STIFFNESS_LOW)
.setDampingRatio(SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY))
.start()
ОБНОВИТЬ:
Второе решение работает вне коробки, без изменений в вашем RecyclerView, и это тот, который я написал и протестировал полностью.
Подробнее о интерполяторах, smoothScrollBy
не работает с интерполяторами (вероятно, ошибка). При использовании интерполятора вы в основном сопоставляете значение 0-1 с другим, что является мультипликатором для анимации. Пример: t = 0, interp (0) = 0 означает, что в начале анимации значение должно быть таким же, как оно было начато, t =.5, interp (.5) =. 25 означает, что элемент будет анимировать 1/4 пути и т.д. Интерполяторы отскока в основном возвращают значения> 1 в какой-то момент и колеблются около 1 до окончательного осаждения при 1 при t = 1.
Какое решение № 2 используется с использованием весеннего аниматора, но для обновления прокрутки. Причина, по которой SCROLL_X не работает, заключается в том, что RecyclerView фактически не прокручивается (это была моя ошибка). Он вычисляет, где представления должны быть основаны на другом расчете, поэтому вам нужен вызов для computeHorizontalScrollOffset
. ScrollXProperty
позволяет вам изменять горизонтальную прокрутку RecyclerView, как если бы вы scrollX
свойство scrollX
в ScrollView, это в основном адаптер. RecyclerViews не поддерживают прокрутку к определенному смещению пикселей, только при плавной прокрутке, но SpringAnimation уже делает это плавно для вас, поэтому нам это не нужно. Вместо этого мы хотим перейти к отдельной позиции. См. Https://android.googlesource.com/platform/frameworks/support/+/247185b98675b09c5e98c87448dd24aef4dffc9d/v7/recyclerview/src/android/support/v7/widget/RecyclerView.java#387
ОБНОВИТЬ:
Здесь код, который я использовал для проверки https://github.com/yperess/StackOverflow/tree/52148251
ОБНОВИТЬ:
Получил ту же концепцию, которая работает с интерполяторами:
class ScrollXProperty : Property<RecyclerView, Int>(Int::class.java, "horozontalOffset") {
override fun get('object': RecyclerView): Int =
'object'.computeHorizontalScrollOffset()
override fun set('object': RecyclerView, value: Int) {
'object'.scrollBy(value - get('object'), 0)
}
}
ObjectAnimator.ofInt(recycler_view, ScrollXProperty(), 0).apply {
interpolator = BounceInterpolator()
duration = 500L
}.start()
Обновлен демонстрационный проект GitHub
Я обновил ScrollXProperty
чтобы включить оптимизацию, кажется, хорошо работает на моем Pixel, но я не тестировал старые устройства.
class ScrollXProperty(
private val enableOptimizations: Boolean
) : Property<RecyclerView, Int>(Int::class.java, "horizontalOffset") {
private var lastKnownValue: Int? = null
override fun get('object': RecyclerView): Int =
'object'.computeHorizontalScrollOffset().also {
if (enableOptimizations) {
lastKnownValue = it
}
}
override fun set('object': RecyclerView, value: Int) {
val currentValue = lastKnownValue?.takeIf { enableOptimizations } ?: get('object')
if (enableOptimizations) {
lastKnownValue = value
}
'object'.scrollBy(value - currentValue, 0)
}
}
Проект GitHub теперь включает демонстрацию со следующими интерполяторами:
<string-array name="interpolators">
<item>AccelerateDecelerate</item>
<item>Accelerate</item>
<item>Anticipate</item>
<item>AnticipateOvershoot</item>
<item>Bounce</item>
<item>Cycle</item>
<item>Decelerate</item>
<item>Linear</item>
<item>Overshoot</item>
</string-array>