Twidere-App-Android-Twitter.../twidere/src/main/kotlin/org/mariotaku/twidere/view/viewer/MediaSwipeCloseContainer.kt

127 lines
4.7 KiB
Kotlin

package org.mariotaku.twidere.view.viewer
import android.content.Context
import android.support.annotation.FloatRange
import android.support.v4.view.ViewCompat
import android.support.v4.widget.ViewDragHelper
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
import android.view.ViewConfiguration
import android.view.ViewGroup
import org.mariotaku.ktextension.coerceInOr
/**
* Created by mariotaku on 2017/1/29.
*/
class MediaSwipeCloseContainer(context: Context, attrs: AttributeSet? = null) : ViewGroup(context, attrs) {
private val dragHelper: ViewDragHelper = ViewDragHelper.create(this, 0.5f, object : ViewDragHelper.Callback() {
override fun onViewPositionChanged(changedView: View?, left: Int, top: Int, dx: Int, dy: Int) {
val container = this@MediaSwipeCloseContainer
container.childTop = top
container.listener?.onSwipeOffsetChanged(top)
}
override fun onViewDragStateChanged(state: Int) {
val container = this@MediaSwipeCloseContainer
container.listener?.onSwipeStateChanged(container.dragHelper.viewDragState)
}
override fun tryCaptureView(child: View, pointerId: Int): Boolean {
// Only when child can't scroll vertically
return !child.canScrollVertically(-1) && !child.canScrollVertically(1)
}
override fun clampViewPositionVertical(child: View, top: Int, dy: Int): Int {
val container = this@MediaSwipeCloseContainer
return top.coerceInOr(-container.height..container.height, 0)
}
override fun getViewVerticalDragRange(child: View?): Int {
val container = this@MediaSwipeCloseContainer
return container.height
}
override fun onViewReleased(releasedChild: View, xvel: Float, yvel: Float) {
val container = this@MediaSwipeCloseContainer
val minVel = ViewConfiguration.get(context).scaledMinimumFlingVelocity
if (Math.abs(yvel) < Math.abs(xvel)) {
container.dragHelper.smoothSlideViewTo(releasedChild, 0, 0)
} else when {
yvel > minVel && childTop > 0 -> {
// Settle downward
container.dragHelper.settleCapturedViewAt(0, container.height)
}
yvel < -minVel && childTop < 0 -> {
// Settle upward
container.dragHelper.settleCapturedViewAt(0, -container.height)
}
yvel <= 0 && childTop < -container.height / 4 -> {
container.dragHelper.smoothSlideViewTo(releasedChild, 0, -container.height)
}
yvel >= 0 && childTop > container.height / 4 -> {
container.dragHelper.smoothSlideViewTo(releasedChild, 0, container.height)
}
else -> {
container.dragHelper.smoothSlideViewTo(releasedChild, 0, 0)
}
}
ViewCompat.postInvalidateOnAnimation(container)
}
})
private var childTop: Int = 0
var listener: Listener? = null
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
measureChildren(widthMeasureSpec, heightMeasureSpec)
setMeasuredDimension(getDefaultSize(suggestedMinimumWidth, widthMeasureSpec),
getDefaultSize(suggestedMinimumHeight, heightMeasureSpec))
}
override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
for (i in 0 until childCount) {
val child = getChildAt(i)
if (child.visibility != View.GONE) {
child.layout(0, childTop, child.measuredWidth, childTop + child.measuredHeight)
}
}
}
override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
if (dragHelper.shouldInterceptTouchEvent(ev)) {
return true
}
return super.onInterceptTouchEvent(ev)
}
override fun onTouchEvent(event: MotionEvent): Boolean {
dragHelper.processTouchEvent(event)
return true
}
override fun computeScroll() {
if (dragHelper.continueSettling(true)) {
ViewCompat.postInvalidateOnAnimation(this)
} else if (childTop <= -height || childTop >= height) {
listener?.onSwipeCloseFinished()
}
}
interface Listener {
fun onSwipeCloseFinished() {}
fun onSwipeOffsetChanged(offset: Int) {}
fun onSwipeStateChanged(state: Int) {}
}
var backgroundAlpha: Float
get() = (background?.alpha ?: 0) / 255f
set(@FloatRange(from = 0.0, to = 1.0) value) {
background?.alpha = Math.round(value * 0xFF)
}
}