Twidere-App-Android-Twitter.../twidere/src/main/kotlin/org/mariotaku/twidere/util/ContentScrollHandler.kt

174 lines
5.5 KiB
Kotlin

/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2015 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.util
import android.support.v4.app.Fragment
import android.view.MotionEvent
import android.view.View
import org.mariotaku.twidere.adapter.iface.ILoadMoreSupportAdapter
import org.mariotaku.twidere.adapter.iface.ILoadMoreSupportAdapter.IndicatorPosition
/**
* Created by mariotaku on 15/3/15.
*/
open class ContentScrollHandler<A>(
private val contentListSupport: ContentScrollHandler.ContentListSupport<A>,
private val viewCallback: ContentScrollHandler.ViewCallback?
) {
val touchListener: View.OnTouchListener
var touchSlop: Int = 0
var reversed: Boolean = false
protected var scrollState: Int = 0
private set
private var scrollSum: Int = 0
private var scrollDirection: Int = 0
init {
touchListener = TouchListener(this)
}
private fun postNotifyScrollStateChanged() {
if (contentListSupport is Fragment) {
if (contentListSupport.context == null) return
}
if (viewCallback != null) {
viewCallback.post(object : Runnable {
override fun run() {
if (viewCallback.computingLayout) {
viewCallback.post(this)
} else {
notifyScrollStateChanged()
}
}
})
} else {
notifyScrollStateChanged()
}
}
private fun notifyScrollStateChanged() {
if (contentListSupport is Fragment) {
if (contentListSupport.context == null) return
}
val adapter = contentListSupport.adapter as? ILoadMoreSupportAdapter ?: return
if (!contentListSupport.refreshing && adapter.loadMoreSupportedPosition != ILoadMoreSupportAdapter.NONE
&& adapter.loadMoreIndicatorPosition == ILoadMoreSupportAdapter.NONE) {
var position: Long = 0
if (contentListSupport.reachingEnd && scrollDirection < 0) {
position = position or ILoadMoreSupportAdapter.END
}
if (contentListSupport.reachingStart && scrollDirection > 0) {
position = position or ILoadMoreSupportAdapter.START
}
resetScrollDirection()
if (position != 0L) {
contentListSupport.onLoadMoreContents(position)
}
}
}
fun handleScrollStateChanged(scrollState: Int, idleState: Int) {
if (contentListSupport is Fragment) {
if (contentListSupport.context == null) return
}
if (this.scrollState != idleState) {
postNotifyScrollStateChanged()
}
this.scrollState = scrollState
}
fun handleScroll(dy: Int, scrollState: Int, oldState: Int, idleState: Int) {
if (contentListSupport is Fragment) {
if (contentListSupport.context == null) return
}
//Reset mScrollSum when scrolling in reverse direction
if (dy * scrollSum < 0) {
scrollSum = 0
}
scrollSum += dy
if (Math.abs(scrollSum) > touchSlop) {
contentListSupport.setControlVisible(reversed xor (dy < 0))
scrollSum = 0
}
if (scrollState == idleState && oldState != scrollState) {
postNotifyScrollStateChanged()
}
}
private fun setScrollDirection(direction: Int) {
scrollDirection = direction
}
private fun resetScrollDirection() {
scrollDirection = 0
}
internal class TouchListener<A>(private val listener: ContentScrollHandler<A>) : View.OnTouchListener {
private var lastY: Float = 0f
override fun onTouch(v: View, event: MotionEvent): Boolean {
when (event.action) {
MotionEvent.ACTION_DOWN -> {
listener.resetScrollDirection()
lastY = Float.NaN
}
MotionEvent.ACTION_MOVE -> {
if (lastY.isNaN()) {
lastY = event.y
} else {
val delta = event.y - lastY
listener.setScrollDirection(if (delta < 0) -1 else 1)
}
}
MotionEvent.ACTION_UP -> {
lastY = Float.NaN
}
}
return false
}
}
interface ViewCallback {
val computingLayout: Boolean
fun post(runnable: Runnable)
}
interface ContentListSupport<out A> {
val adapter: A?
val refreshing: Boolean
val reachingStart: Boolean
val reachingEnd: Boolean
fun onLoadMoreContents(@IndicatorPosition position: Long)
fun setControlVisible(visible: Boolean)
}
}