Last active
May 26, 2022 17:27
-
-
Save valkriaine/bb0672445f9b20fcc5594a071f3c7cce to your computer and use it in GitHub Desktop.
Add bouncy effect to any recyclerview
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// add bouncy effect to this view | |
var overscrollAnimationSize = 0.5f | |
var flingAnimationSize = 0.5f | |
@Suppress("MemberVisibilityCanBePrivate") | |
var dampingRatio = SpringForce.DAMPING_RATIO_NO_BOUNCY | |
set(value) | |
{ | |
field = value | |
this.spring.spring = SpringForce() | |
.setFinalPosition(0f) | |
.setDampingRatio(value) | |
.setStiffness(stiffness) | |
} | |
@Suppress("MemberVisibilityCanBePrivate") | |
var stiffness = SpringForce.STIFFNESS_LOW | |
set(value) | |
{ | |
field = value | |
this.spring.spring = SpringForce() | |
.setFinalPosition(0f) | |
.setDampingRatio(dampingRatio) | |
.setStiffness(value) | |
} | |
var spring: SpringAnimation = SpringAnimation(this, SpringAnimation.TRANSLATION_Y) | |
.setSpring( | |
SpringForce() | |
.setFinalPosition(0f) | |
.setDampingRatio(dampingRatio) | |
.setStiffness(stiffness) | |
) | |
var touched: Boolean = false | |
var orientation : Int? = 1 | |
set(value) | |
{ | |
field = value | |
setupDirection(value) | |
} | |
override fun setLayoutManager(layout: LayoutManager?) | |
{ | |
super.setLayoutManager(layout) | |
if (layout is LinearLayoutManager) | |
{ | |
orientation = layout.orientation | |
setupDirection(orientation) | |
} | |
} | |
private fun setupDirection(orientation : Int?) | |
{ | |
if (stiffness > 0) | |
{ | |
when (orientation) | |
{ | |
HORIZONTAL -> spring = SpringAnimation(this, SpringAnimation.TRANSLATION_X) | |
.setSpring(SpringForce() | |
.setFinalPosition(0f) | |
.setDampingRatio(dampingRatio) | |
.setStiffness(stiffness)) | |
VERTICAL -> spring = SpringAnimation(this, SpringAnimation.TRANSLATION_Y) | |
.setSpring(SpringForce() | |
.setFinalPosition(0f) | |
.setDampingRatio(dampingRatio) | |
.setStiffness(stiffness)) | |
} | |
} | |
} | |
@SuppressLint("ClickableViewAccessibility") | |
override fun onTouchEvent(e: MotionEvent?): Boolean | |
{ | |
touched = when (e?.actionMasked) | |
{ | |
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> false | |
else -> true | |
} | |
return super.onTouchEvent(e) | |
} | |
// call this at the end of init{...} | |
// example: configureEdgeEffect(this) | |
private fun configureEdgeEffect(rc : RecyclerView) | |
{ | |
//create edge effect | |
this.edgeEffectFactory = object : EdgeEffectFactory() | |
{ | |
override fun createEdgeEffect(recyclerView: RecyclerView, direction: Int): EdgeEffect | |
{ | |
return object : EdgeEffect(recyclerView.context) | |
{ | |
override fun onPull(deltaDistance: Float) | |
{ | |
super.onPull(deltaDistance) | |
onPullAnimation(deltaDistance) | |
} | |
override fun onPull(deltaDistance: Float, displacement: Float) | |
{ | |
super.onPull(deltaDistance, displacement) | |
onPullAnimation(deltaDistance) | |
} | |
private fun onPullAnimation(deltaDistance: Float) | |
{ | |
if (orientation == VERTICAL) | |
{ | |
val delta: Float = | |
if (direction == DIRECTION_BOTTOM) | |
-1 * recyclerView.width * deltaDistance * overscrollAnimationSize | |
else | |
1 * recyclerView.width * deltaDistance * overscrollAnimationSize | |
rc.translationY += delta | |
spring.cancel() | |
} | |
else | |
{ | |
val delta: Float = | |
if (direction == DIRECTION_RIGHT) | |
-1 * recyclerView.width * deltaDistance * overscrollAnimationSize | |
else | |
1 * recyclerView.width * deltaDistance * overscrollAnimationSize | |
rc.translationX += delta | |
spring.cancel() | |
} | |
} | |
override fun onRelease() | |
{ | |
super.onRelease() | |
if (touched) | |
return | |
spring.start() | |
} | |
override fun onAbsorb(velocity: Int) | |
{ | |
super.onAbsorb(velocity) | |
if (orientation == VERTICAL) | |
{ | |
val v: Float = if (direction == DIRECTION_BOTTOM) | |
-1 * velocity * flingAnimationSize | |
else | |
1 * velocity * flingAnimationSize | |
spring.setStartVelocity(v).start() | |
} | |
else | |
{ | |
val v: Float = if (direction == DIRECTION_RIGHT) | |
-1 * velocity * flingAnimationSize | |
else | |
1 * velocity * flingAnimationSize | |
spring.setStartVelocity(v).start() | |
} | |
} | |
override fun draw(canvas: Canvas?): Boolean | |
{ | |
setSize(0, 0) | |
return super.draw(canvas) | |
} | |
} | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
copy and paste this code anywhere in your recyclerview class, and call configureEdgeEffect() under init{...}