Create a Custom Number Picker in Android

In this post I will show you how to create a custom number picker in Android Studio using Jetpack Compose.

The foundation for this custom element is the LazyColumn. The LazyColumn has enough of the built-in attributes that we can use to create this with a few changes.

I use lazyListState as the state to hold the value returned from scrolling.

Below is the sample code. You may need to change or update it based on your project use case / needs.

class MainActivity : ComponentActivity() {
    @OptIn(ExperimentalFoundationApi::class)
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            SampleNumberPickerTheme {
                Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
                    Column(
                        modifier = Modifier
                            .padding(innerPadding)
                            .fillMaxWidth(),
                        verticalArrangement = Arrangement.Center
                    ) {
                        val lazyListState = rememberLazyListState(initialFirstVisibleItemIndex = 0)
                        var values = remember {
                            (0..20).map { it.toString() }
                        }
                        Row(
                            modifier = Modifier
                                .fillMaxWidth(),
                            horizontalArrangement = Arrangement.Center
                        ) {
                            NumberPicker(
                                modifier = Modifier,
                                list = values,
                                fontSize = 32.sp,
                                state = lazyListState,
                                flingBehavior = rememberSnapFlingBehavior(lazyListState = lazyListState)
                            )

                        }
                        Row() {
                            Text(text = "${values[lazyListState.firstVisibleItemIndex+1]}")
                        }
                    }
                }
            }
        }
    }
}

@Composable
fun NumberPicker(
    modifier: Modifier = Modifier,
    list: List<String>,
    fontSize: TextUnit,
    state: LazyListState,
    flingBehavior: FlingBehavior
) {

    Box(
        modifier = modifier
    )
    {
        // add 1 for size and offset
        val shownCount = 3 + 1
        val height = with(LocalDensity.current) {
            fontSize.toDp()
        }
        LazyColumn(
            state = state,
            flingBehavior = flingBehavior,
            modifier = Modifier
                .padding(5.dp)
                .height(height * shownCount)
                .graphicsLayer { compositingStrategy = CompositingStrategy.Offscreen }
                .drawWithContent {
                    drawContent()
                    drawRect(
                        brush = Brush.verticalGradient(
                            0f to Color.Transparent,
                            0.5f to Color.Black,
                            1f to Color.Transparent
                        ),
                        blendMode = BlendMode.DstIn
                    )
                }) {
            items(list.size) { index ->
            //if first item then add end empty slot in the beginning so the first item can be scrolled to center of screen
                if(index == 0){
                    Text(
                        modifier = Modifier.padding(5.dp),
                        text = " ", fontSize = fontSize
                    )
                } else {
                    Text(
                        modifier = Modifier.padding(5.dp),
                        text = "${list[index]}", fontSize = fontSize
                    )
                }
                //if end of list then create a empty slot so the last item can scroll to center of screen
                if(index == list.size-1){
                    Text(
                        modifier = Modifier.padding(5.dp),
                        text = " ", fontSize = fontSize
                    )
                }

            }
        }
    }
}

Problem I encountered while creating

I initially creating this from a Canvas instead of LazyColumn. The last elements I have needed custom elements for, I used a Canvas so that was my starting point. The issue was the scrolling and stopping at a position.

Fix

This is why I ended up with using the LazyColumn. It was faster to just use the built-in FlingBehavior for the scrolling stop then to attempt to create one.

Note

This is not just for numbers. This can be used for any type you can feed into it. At time of creation, I needed it to show numbers.


Comments

Leave a Reply

Your email address will not be published. Required fields are marked *