Coder Social home page Coder Social logo

nanihadesuka / lazycolumnscrollbar Goto Github PK

View Code? Open in Web Editor NEW
132.0 1.0 14.0 311 KB

Android Jetpack Compose LazyColumn scrollbar implementation

License: MIT License

Kotlin 100.00%
jetpack-compose android scrollbar library android-ui compose fastscroll fastscroller scroller

lazycolumnscrollbar's Introduction

License Foo23 - Bar Foo23 - Bar

Scrollbars implementation for jetpack compose

Compose implementation of the scroll bar. Can drag, scroll smoothly and includes animations.

Features:

  • Support for:
    • Column, Row, LazyColumn, LazyRow, LazyVerticalGrid, LazyHorizontalGrid
  • Takes into account:
    • sticky headers
    • reverseLayout
  • Optional current position indicator
  • Multiple selection modes:
    • States (Disabled, Full, Thumb)
    • Actionable states (Always, WhenVisible)
  • Customizable look
  • Easy integration with other composables
  • Extensive UI tests
  • Sample app

Installation

Add it in your root build.gradle at the end of repositories:

allprojects {
    repositories {
        maven { url 'https://jitpack.io' }
    }
}

Add it to your app build.gradle

dependencies {
    implementation 'com.github.nanihadesuka:LazyColumnScrollbar:2.1.0'
}

Available scrolls components

  • ColumnScrollbar
  • RowScrollbar
  • LazyColumnScrollbar
  • LazyRowScrollbar
  • LazyVerticalGridScrollbar
  • LazyHorizontalGridScrollbar

Example for LazyColumn

val listData = (0..1000).toList()
val listState = rememberLazyListState()

LazyColumnScrollbar(
  state = listState,
  settings = ScrollbarSettings.Default  
) {
    LazyColumn(state = listState) {
        items(listData) {
            Text("Item $it")
        }
    }
}

indicatorContent example:

indicatorContent = { index, isThumbSelected ->
    Text(
        text = "i: $index",
        Modifier.background(if (isThumbSelected) Color.Red else Color.Black, CircleShape)
    )
}

Default settings parameters

/**
 * @param thumbMinLength Thumb minimum length proportional to total scrollbar's length (eg: 0.1 -> 10% of total)
 */
@Stable
data class ScrollbarSettings(
  val enabled: Boolean = Default.enabled,
  val side: ScrollbarLayoutSide = Default.side,
  val alwaysShowScrollbar: Boolean = Default.alwaysShowScrollbar,
  val scrollbarPadding: Dp = Default.scrollbarPadding,
  val thumbThickness: Dp = Default.thumbThickness,
  val thumbShape: Shape = Default.thumbShape,
  val thumbMinLength: Float = Default.thumbMinLength,
  val thumbUnselectedColor: Color = Default.thumbUnselectedColor,
  val thumbSelectedColor: Color = Default.thumbSelectedColor,
  val selectionMode: ScrollbarSelectionMode = Default.selectionMode,
  val selectionActionable: ScrollbarSelectionActionable = Default.selectionActionable,
  val hideDelayMillis: Int = Default.hideDelayMillis,
  val hideDisplacement: Dp = Default.hideDisplacement,
  val hideEasingAnimation: Easing = Default.hideEasingAnimation,
  val durationAnimationMillis: Int = Default.durationAnimationMillis,
) {
  companion object {
    val Default = ScrollbarSettings(
      enabled = true,
      side = ScrollbarLayoutSide.End,
      alwaysShowScrollbar = false,
      thumbThickness = 6.dp,
      scrollbarPadding = 8.dp,
      thumbMinLength = 0.1f,
      thumbUnselectedColor = Color(0xFF2A59B6),
      thumbSelectedColor = Color(0xFF5281CA),
      thumbShape = CircleShape,
      selectionMode = ScrollbarSelectionMode.Thumb,
      selectionActionable = ScrollbarSelectionActionable.Always,
      hideDelayMillis = 400,
      hideDisplacement = 14.dp,
      hideEasingAnimation = FastOutSlowInEasing,
      durationAnimationMillis = 500,
    )
  }
}

License

Copyright © 2024, nani, Released under MIT License

lazycolumnscrollbar's People

Contributors

8cayqpvkio avatar guhdoy avatar kazemcodes avatar kuhakupixel avatar matiasilveiro avatar nanihadesuka avatar savvasdalkitsis avatar walt-zhong avatar xiaoqiwen avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

lazycolumnscrollbar's Issues

documentation on usage not 100% correct.

On the gitpage it gives this usage example:

LazyColumnScrollbar(listState = listState) {
LazyColumn(....)
}

Howver, since this actually matches two different signatures of the composable named LazyColumnScrollbar (as the name is overloaded), it will not compile in Android Studio.

I had to do this:
LazyColumnScrollbar(listState = listState, enabled = true) {
LazyColumn(....)

}
as this only matches one composable signature to get it to work.
Otherwise, great library!

contentPadding support

LazyColumn has a param named contentPadding which is related to scroll bars. The current implementation doesn't take it into account, so I believe something needs to be done. I've implemented a ViewHelper for RecycleView that I think has the same logic as yours but considered padding, but I don't quite understand part of your code and don't know how to convert it. Do you have any suggestions for this?

Scrollbar is usable in its hidden state

This can be confusing and inconvenient for the user if they, for example, use the back gesture or scroll a lazy row and the scroll bar is right where the user performs the gesture despite not actually being rendered

Weird crash on Column that didn't fill the screen height

java.lang.IllegalArgumentException: Cannot coerce value to an empty range: maximum -0.28374994 is less than minimum 0.0.
                                                                                                    	at kotlin.ranges.RangesKt___RangesKt.coerceIn{_Ranges.kt:1131}
                                                                                                    	at my.nanihadesuka.compose.LazyColumnScrollbarKt.LazyColumnScrollbar_WteSXyg$setDragOffset{LazyColumnScrollbar.kt:150}
                                                                                                    	at my.nanihadesuka.compose.LazyColumnScrollbarKt.access$LazyColumnScrollbar_WteSXyg$setDragOffset{LazyColumnScrollbar.kt:1}
                                                                                                    	at my.nanihadesuka.compose.LazyColumnScrollbarKt$LazyColumnScrollbar$3$3.invokeSuspend{LazyColumnScrollbar.kt:254}
                                                                                                    	at my.nanihadesuka.compose.LazyColumnScrollbarKt$LazyColumnScrollbar$3$3.invoke-d-4ec7I{Unknown Source:32}
                                                                                                    	at my.nanihadesuka.compose.LazyColumnScrollbarKt$LazyColumnScrollbar$3$3.invoke{Unknown Source:13}
                                                                                                    	at androidx.compose.foundation.gestures.DragLogic.processDragStart{Draggable.kt:393}
                                                                                                    	at androidx.compose.foundation.gestures.DraggableKt$draggable$9$2.invokeSuspend{Draggable.kt:237}
                                                                                                    	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith{ContinuationImpl.kt:33}
                                                                                                    	at kotlinx.coroutines.DispatchedTask.run{DispatchedTask.kt:106}
                                                                                                    	at androidx.compose.ui.platform.AndroidUiDispatcher.performTrampolineDispatch{AndroidUiDispatcher.android.kt:81}
                                                                                                    	at androidx.compose.ui.platform.AndroidUiDispatcher.access$performTrampolineDispatch{AndroidUiDispatcher.android.kt:41}
                                                                                                    	at androidx.compose.ui.platform.AndroidUiDispatcher$dispatchCallback$1.run{AndroidUiDispatcher.android.kt:57}
                                                                                                    	at android.os.Handler.handleCallback{Handler.java:938}
                                                                                                    	at android.os.Handler.dispatchMessage{Handler.java:99}
                                                                                                    	at android.os.Looper.loopOnce{Looper.java:201}
                                                                                                    	at android.os.Looper.loop{Looper.java:288}
                                                                                                    	at android.app.ActivityThread.main{ActivityThread.java:7870}
                                                                                                    	at java.lang.reflect.Method.invoke{Native Method}
                                                                                                    	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run{RuntimeInit.java:548}
                                                                                                    	at com.android.internal.os.ZygoteInit.main{ZygoteInit.java:1003}
                                                                                                    	Suppressed: kotlinx.coroutines.DiagnosticCoroutineContextException: [androidx.compose.ui.platform.MotionDurationScaleImpl@c623127, androidx.compose.runtime.BroadcastFrameClock@bf2ffd4, StandaloneCoroutine{Cancelling}@261b57d, AndroidUiDispatcher@4948c72]
                                                                                                     (_Ranges.kt:1131)

Support ReverseLayout

Using this with a LazyColumn where reverseLayout=true results in the scrollbar going from top-to-bottom instead of bottom-to-top. It should be an easy enhancement to add support for reversing the top/bottom positions. (I may follow-up with a PR if I can find time.)

Please add some example images

It would be very useful to have some example images in the readme, of your library implemented. Now I have no clue if this library indeed does what I need it to.

Make scrollbar only react to touches on knob

Currently touching anywhere on the scrollbar gutter will move the scrollbar under your finger, making pressing back on phones with gesture navigation mess up the scroll position.

I feel like the scrollbar should only react to touch events when the knob is visible and when the initiating gesture starts on the knob itself

Due to Snapshot issue, the app dies after some time.

I found an issue where the initial installation might be fine, but the app just quits after a while. This issue may appear immediately after installing the app, or may appear after a random amount of time.

The sample app in the repository also shows the same problem.

E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.*****.*********, PID: 32681
    java.lang.IllegalStateException: Reading a state that was created after the snapshot was taken or in a snapshot that has not yet been applied

I confirmed that the part I found about this issue appeared in the part where translationY was calculated and applied.
Like this line: translationY = constraints.maxHeight.toFloat() * normalizedOffsetPosition

Digging deeper, it is expected that the problem occurs in the part where the normalizedThumbSizeReal is accessed in the offsetCorrection function.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.