Skip to content

Repository files navigation

RulerKit

API Kotlin Compose License


Why RulerKit?

Most ruler pickers are XML-only, don't support unit conversion, and lack Compose support. RulerKit does all three — with smooth fling/snap, haptic feedback, and a KMP-ready architecture out of the box.


Used in Production

Built from real-world experience building Mamily — a 100K+ download health app on the Play Store.


Input Types

Weight · Height · Distance · Custom — all in one demo:

A highly customizable horizontal ruler picker for Android. Supports weight, height, distance, and fully custom input types. Available as both a classic View (RulerPickerView) and a native Jetpack Compose composable (RulerPicker). Built on Canvas with smooth fling/snap animation, haptic feedback, and automatic unit conversion.


Installation

Step 1. Add JitPack to your root settings.gradle.kts:

dependencyResolutionManagement {
    repositories {
        maven("https://jitpack.io")
    }
}

Step 2. Add the dependency:

dependencies {
    implementation("com.github.theadityatiwari:RulerKit:v1.0.1")
}

View — Quick Start

XML

<com.nativeknights.rulerkit.RulerPickerView
    android:id="@+id/rulerPicker"
    android:layout_width="match_parent"
    android:layout_height="160dp"
    app:rulerInputType="weight"
    app:rulerUnit="kg"
    app:rulerInitialValue="70"
    app:indicatorType="triangle"
    app:indicatorColor="#E91E63" />

Kotlin

val ruler = findViewById<RulerPickerView>(R.id.rulerPicker)

// Value changes while scrolling
ruler.onValueChanged = { value, unit ->
    println("$value $unit")
}

// Settled value after drag/fling ends
ruler.onScrollEnd = { value, unit ->
    println("Settled at $value $unit")
}

// Switch units at runtime (converts value automatically)
ruler.setUnit(WeightUnit.LB)

// Set value programmatically
ruler.setValue(75f, animate = true)

// Read current value
val current = ruler.getValue()

Programmatic config

ruler.config = RulerConfig(
    inputType      = InputType.Weight(WeightUnit.KG),
    initialValue   = 70f,
    indicatorType  = IndicatorType.ARROW,
    indicatorColor = 0xFF2196F3.toInt(),
    showLabels     = true,
    tickSpacingDp  = 14f
)

Custom ruler

ruler.config = RulerConfig.custom(
    min         = 1f,
    max         = 120f,
    step        = 1f,
    unitLabel   = "yrs",
    majorEvery  = 10,
    mediumEvery = 5
)

Compose — Quick Start

val state = rememberRulerPickerState(RulerConfig.weight())

RulerPicker(
    state    = state,
    modifier = Modifier.fillMaxWidth().height(160.dp),
    onValueChanged = { value, unit ->
        println("$value $unit")
    },
    onScrollEnd = { value, unit ->
        println("Settled at $value $unit")
    }
)

Unit switch

val scope = rememberCoroutineScope()

Button(onClick = { scope.launch { state.setUnit(WeightUnit.LB) } }) {
    Text("lb")
}

Programmatic value

scope.launch { state.setValue(75f, animate = true) }

Custom config

val state = rememberRulerPickerState(
    RulerConfig(
        inputType      = InputType.Weight(WeightUnit.KG),
        initialValue   = 70f,
        indicatorType  = IndicatorType.ARROW,
        indicatorColor = 0xFF2196F3.toInt(),
        tickSpacingDp  = 14f
    )
)

Read current value

val live    = state.currentValue   // updates every frame
val settled = state.snappedValue   // stable after gesture ends

XML Attributes (View only)

Attribute Values / Type Default
rulerInputType weight height distance custom weight
rulerUnit kg lb cm inch km miles meters kg
rulerMinValue float unit default
rulerMaxValue float unit default
rulerInitialValue float min
rulerCustomStep float —
rulerCustomUnitLabel string —
rulerCustomMajorEvery integer 10
rulerCustomMediumEvery integer 5
indicatorType line triangle arrow pin triangle
indicatorPosition centerTop centerBottom centerBottom
indicatorColor color #E91E63
indicatorSize dimension 20dp
indicatorWidth dimension 2dp
majorLineHeight dimension 56dp
majorLineWidth dimension 2dp
majorLineColor color #212121
mediumLineHeight dimension 36dp
mediumLineWidth dimension 1.5dp
mediumLineColor color #757575
minorLineHeight dimension 20dp
minorLineWidth dimension 1dp
minorLineColor color #BDBDBD
showLabels boolean true
labelTextSize dimension 11sp
labelTextColor color #424242
showValueLabel boolean true
valueLabelTextSize dimension 26sp
valueLabelTextColor color #212121
rulerBackgroundColor color #FFFFFF
tickSpacing dimension 12dp
flipVertically boolean false
enableHapticFeedback boolean true

All RulerConfig properties are also available programmatically for both RulerPickerView and RulerPicker.


Requirements

  • minSdk 24
  • Kotlin 2.x
  • Compose BOM 2024.09.00+ (for RulerPicker)
  • android.permission.VIBRATE is merged automatically from the library manifest

Roadmap

  • Phase 2 — Jetpack Compose (RulerPicker composable) ✅ Done
  • Phase 4 — Kotlin Multiplatform / Compose Multiplatform (iOS, Desktop)

About

A highly customizable horizontal ruler picker for Android

Topics

Resources

Stars

8 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages