Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save nikclayton/6eddadbb864e1e541a91f6018fd8d803 to your computer and use it in GitHub Desktop.
Save nikclayton/6eddadbb864e1e541a91f6018fd8d803 to your computer and use it in GitHub Desktop.
/*
* Copyright 2023 Tusky Contributors
*
* This file is a part of Tusky.
*
* 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.
*
* Tusky 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 Tusky; if not,
* see <http://www.gnu.org/licenses>.
*/
package com.keylesspalace.tusky.view
import android.content.Context
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import kotlin.math.abs
/**
* [LinearLayoutManager] with a fixed size scroll bar thumb.
*
* The normal [LinearLayoutManager] handles scrolling through items of different heights by
* varying the size of the scrollbar thumb.
*
* This code maintains a fixed thumb size (relative to the number of items in the list), and
* adjusts the thumb position to compensate.
*
* Based on https://medium.com/dowjones/scrolling-in-android-custom-scroll-behavior-for-a-list-of-varying-height-7426f0f9046
*/
class FixedScrollThumbLinearLayoutManager(context: Context?) : LinearLayoutManager(context) {
init {
isSmoothScrollbarEnabled = false
}
override fun computeVerticalScrollExtent(state: RecyclerView.State): Int {
return if (childCount > 0) SMOOTH_VALUE * 3 else 0
}
override fun computeVerticalScrollRange(state: RecyclerView.State): Int {
return ((itemCount - 1) * SMOOTH_VALUE).coerceAtLeast(0)
}
override fun computeVerticalScrollOffset(state: RecyclerView.State): Int {
val count = childCount
if (count <= 0) return 0
if (findLastCompletelyVisibleItemPosition() == itemCount - 1) {
return ((itemCount - 1) * SMOOTH_VALUE).coerceAtLeast(0)
}
val firstPosition = findFirstVisibleItemPosition()
if (firstPosition == RecyclerView.NO_POSITION) return 0
val view = findViewByPosition(firstPosition) ?: return 0
val top = getDecoratedTop(view)
val height = getDecoratedMeasuredHeight(view)
val heightOfScreen = if (height <= 0) 0 else abs(SMOOTH_VALUE * top / height)
if (heightOfScreen == 0 && firstPosition > 0) return SMOOTH_VALUE * firstPosition - 1
return (SMOOTH_VALUE * firstPosition) + heightOfScreen
}
companion object {
private const val SMOOTH_VALUE = 100
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment