Maybe it will be useful for someone. Code of previous answer's will not work when the list is scrolled fast. Because in this case firstVisiblePosition may be change irregular. In my code I do not use an array to store positions
fun setOnTouchScrollListener(lv: AbsListView, onTouchScrollListener: (isDown: Boolean, offset: Int?, isScrollWorking: Boolean) -> Unit) {
var lastItemPosition: Int = -1
var lastItemTop: Int? = null
var lastItemHeight: Int? = null
var lastScrollState: Int? = AbsListView.OnScrollListener.SCROLL_STATE_IDLE
lv.setOnScrollListener(object : AbsListView.OnScrollListener {
override fun onScroll(view: AbsListView, firstVisibleItem: Int, visibleItemCount: Int, totalItemCount: Int) {
val child = lv.getChildAt(0)
var offset: Int? = null
var isDown: Boolean? = if (firstVisibleItem == lastItemPosition || lastItemPosition == -1) null else firstVisibleItem > lastItemPosition
val dividerHeight = if (lv is ListView) lv.dividerHeight else 0
val columnCount = if (lv is GridView) lv.numColumns else 1
if (child != null) {
if (lastItemPosition != -1) {
when (firstVisibleItem - lastItemPosition) {
0 -> {
isDown = if (lastItemTop == child.top) null else lastItemTop!! > child.top
offset = Math.abs(lastItemTop!! - child.top)
}
columnCount -> offset = lastItemHeight!! + lastItemTop!! - child.top + dividerHeight
-columnCount -> offset = child.height + child.top - lastItemTop!! + dividerHeight
}
}
lastItemPosition = firstVisibleItem
lastItemHeight = child.height
lastItemTop = child.top
if (isDown != null && (offset == null || offset != 0)
&& lastScrollState != AbsListView.OnScrollListener.SCROLL_STATE_IDLE) {
onTouchScrollListener(isDown, offset, true)
}
}
}
override fun onScrollStateChanged(view: AbsListView, scrollState: Int) {
lastScrollState = scrollState
if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE) {
onTouchScrollListener(true, 0, false)
lastItemPosition = -1
lastItemHeight = null
lastItemTop = null
}
}
})
}
Then we can use when a listview is scrolled beyond threshold
private var lastThresholdOffset = 0
private var thresholdHeight = some value
setOnTouchScrollListener(listView) { isDown: Boolean, offset: Int?, isScrollWorking: Boolean ->
val offset = offset ?: if (isDown) 0 else thresholdHeight
lastThresholdOffset = if (isDown) Math.min(thresholdHeight, lastThresholdOffset + offset)
else Math.max(0, lastThresholdOffset - offset)
val result = lastThresholdOffset > thresholdHeight
}
isScrollWorking - can be used for animation