androidandroid-jetpack-composeandroid-jetpackandroid-tvandroid-jetpack-compose-tv

How to focus on a specific item in a row based on some flag?


Consider the following code :

val tvListState = rememberTvLazyListState()
val coScope = rememberCoroutineScope()
Column{
    TvLazyRow(
        horizontalArrangement = Arrangement.spacedBy(15.dp),
        state = tvListState,
        modifier = modifier
        .padding(end = 5.dp)
        , pivotOffsets = PivotOffsets(0f)) { *items* }
    TvLazyRow(
        horizontalArrangement = Arrangement.spacedBy(15.dp),
        state = tvListState,
        modifier = modifier
        .padding(end = 5.dp)
        , pivotOffsets = PivotOffsets(0f)) { *items* }
    TvLazyRow(
        horizontalArrangement = Arrangement.spacedBy(15.dp),
        state = tvListState,
        modifier = modifier
        .padding(end = 5.dp)
        , pivotOffsets = PivotOffsets(0f)) { *items* }
}

What I want to do is to focus on a specific item in one of the lazy rows when we enter the row, and have it tied to a flag, could be something like preFocusOnItem. In my current implementation I am having to call focus on this item using itemsRequester[preFocusOnItem].requestfocus() inside LaunchedEffect, where the array is used to assign a focus requester to each of the items. The problem with this approach is the focus first comes to the item that scrolling naturally will bring focus to, and after a split second the focus jumps to the item I want to focus on.

How do I directly call focus on the preFocusOnItem item when entering one of the TvLazyRows??


Solution

  • You can make use of focusProperties on the TvLazyRow to intercept the focus and implement the enter function in it to match with some condition and return respective focus requesters.

    In the following example, I am focussing on:

    val rows = 3
    val columns = 4
    
    val focusRequesters = remember {
        (1..(rows * columns)).map { FocusRequester() }
    }
    
    Surface(Modifier.fillMaxSize()) {
        TvLazyColumn {
            items(rows) { row ->
                TvLazyRow(
                    modifier = Modifier.focusProperties {
                        enter = {
                            when (row) {
                                0 -> focusRequesters[row * columns + 2]
                                1 -> focusRequesters[row * columns + 3]
                                2 -> focusRequesters[row * columns + 1]
                                else -> FocusRequester.Default
                            }
                        }
                    }
                ) {
                    items(columns) { column ->
                        val index = row * columns + column
    
                        AppCard(column, Modifier.focusRequester(focusRequesters[index]))
                    }
                }
            }
        }
    }
    
    @OptIn(ExperimentalTvMaterial3Api::class)
    @Composable
    fun AppCard(index: Int, modifier: Modifier = Modifier) {
        Surface(
            onClick = {},
            modifier = modifier.width(200.dp).aspectRatio(16f / 9)
        ) {
            Text(text = "Card=$index")
        }
    }