Skip to content

Instantly share code, notes, and snippets.

@forceLain
Created June 1, 2017 15:17
Show Gist options
  • Save forceLain/49e02e8d772e99707ebda9137b3b6ccb to your computer and use it in GitHub Desktop.
Save forceLain/49e02e8d772e99707ebda9137b3b6ccb to your computer and use it in GitHub Desktop.
SlidingActivity
abstract class SlidingActivity : AppCompatActivity() {
private val GESTURE_THRESHOLD = 10
private lateinit var root: View
private var startX = 0f
private var startY = 0f
private lateinit var screenSize : Point
private var isSliding = false
private lateinit var windowScrim: ColorDrawable
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
screenSize = Point().apply { windowManager.defaultDisplay.getSize(this) }
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
window.statusBarColor = Color.TRANSPARENT
}
windowScrim = ColorDrawable(Color.argb(0xE0, 0, 0, 0))
windowScrim.alpha = 0
window.setBackgroundDrawable(windowScrim)
}
override fun onPostCreate(savedInstanceState: Bundle?) {
super.onPostCreate(savedInstanceState)
root = getRootView()
}
abstract fun getRootView(): View
override fun dispatchTouchEvent(ev: MotionEvent): Boolean {
var handled = false
when (ev.action) {
MotionEvent.ACTION_DOWN -> {
startX = ev.x
startY = ev.y
}
MotionEvent.ACTION_MOVE -> {
if ((shouldHandle(startX, startY, ev) && canSlideDown()) || isSliding) {
if (!isSliding) {
isSliding = true
onSlidingStarted()
ev.action = MotionEvent.ACTION_CANCEL
super.dispatchTouchEvent(ev)
}
root.y = (ev.y - startY).coerceAtLeast(0f)
updateScrim()
handled = true
}
}
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
if (isSliding) {
isSliding = false
onSlidingFinished()
handled = true
if (shouldClose(ev.y - startY)) {
closeDownAndDismiss()
} else {
root.y = 0f
}
}
startX = 0f
startY = 0f
}
}
return if (handled) true else super.dispatchTouchEvent(ev)
}
private fun shouldHandle(startX: Float, startY: Float, ev: MotionEvent): Boolean {
val deltaX = (startX - ev.x).abs()
if (deltaX > GESTURE_THRESHOLD) return false
val deltaY = ev.y - startY
return deltaY > GESTURE_THRESHOLD
}
abstract fun onSlidingFinished()
abstract fun onSlidingStarted()
abstract fun canSlideDown(): Boolean
private fun shouldClose(delta: Float): Boolean {
return delta > screenSize.y / 3
}
private fun closeDownAndDismiss() {
val start = root.y
val finish = screenSize.y.toFloat()
val positionAnimator = ObjectAnimator.ofFloat(root, "y", start, finish)
positionAnimator.duration = 100
positionAnimator.addListener(object : Animator.AnimatorListener {
override fun onAnimationRepeat(animation: Animator) {
}
override fun onAnimationEnd(animation: Animator) {
root.y = screenSize.y.toFloat()
updateScrim()
finish()
}
override fun onAnimationCancel(animation: Animator) {
updateScrim()
}
override fun onAnimationStart(animation: Animator) {
}
})
positionAnimator.addUpdateListener {
updateScrim()
}
positionAnimator.start()
}
private fun updateScrim() {
val progress = root.y / screenSize.y
val alpha = (progress * 255f).toInt()
windowScrim.alpha = 255 - alpha
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment