'Reduce opacity of non focused items

I have a list of items which are displayed on the home screen, the list is vertically scrollable. At the moment I have 5 items but items are added as the user approaches the last item creating an infinite scroll.

I want to have all the items faded out but the one in focus should be fully trasparent like so:

enter image description here

As you can see the item to the left and right are faded and are a little smaller whilst the item in focus is fully transparent and is slightly bigger indicating that this is the item in focus.

This is my Composable list:

@Composable
fun HomeScreenList() {
    val homeScreenItems = getItems()
    val listState = rememberLazyListState(Int.MAX_VALUE / 2)
    LazyRow(
        state = listState,
        modifier = Modifier
            .fillMaxWidth(),
        contentPadding = PaddingValues(horizontal = 16.dp, vertical = 8.dp)
    ) {
        items(Int.MAX_VALUE, itemContent = {
            val index = it % homeScreenItems.size
            HomeScreenItem(model = homeScreenItems[index])
        })
    }
}

And the HomeScreenItem:

@Composable
fun HomeScreenItem(model: HomeScreenViewModel) {
    Card(
        modifier = Modifier
            .padding(horizontal = 10.dp, vertical = 20.dp),
        elevation = 2.dp,
        backgroundColor = model.mBackgroundColor,
        shape = Shapes.large,
    ) {
        Row {
             Image(
        painter = painterResource(id = model.mIcon),
        contentDescription = null,
        contentScale = ContentScale.Crop,
        modifier = Modifier
            .padding(20.dp)
            .size(100.dp)
    )
        }
    }
}

Does anyone know how I should achieve this?



Solution 1:[1]

You can use the list state to determine how far away each item is from the center, and apply opacity accordingly:

val homeScreenItems = remember {
    listOf(
        Icons.Default.Person,
        Icons.Default.Usb,
        Icons.Default.Keyboard,
    )
}
val listState = rememberLazyListState(Int.MAX_VALUE / 2)
val (rowHalfSize, setRowHalfSize) = remember { mutableStateOf<Int?>(null) }
val horizontalContentPadding = 16.dp
val density = LocalDensity.current
LazyRow(
    state = listState,
    contentPadding = PaddingValues(horizontal = horizontalContentPadding, vertical = 8.dp),
    modifier = Modifier
        .fillMaxWidth()
        .onSizeChanged {
            setRowHalfSize(it.width / 2 - with(density) { horizontalContentPadding.roundToPx() })
        }
) {
    items(Int.MAX_VALUE) { globalIndex ->
        val index = globalIndex % homeScreenItems.size
        val opacity by remember(rowHalfSize) {
            derivedStateOf {
                if (rowHalfSize == null) return@derivedStateOf 0.5f
                val currentItemInfo = listState.layoutInfo.visibleItemsInfo
                    .firstOrNull() { it.index == globalIndex }
                    ?: return@derivedStateOf 0.5f
                val itemHalfSize = currentItemInfo.size / 2
                (1f - minOf(1f, abs(currentItemInfo.offset + itemHalfSize - rowHalfSize).toFloat() / itemHalfSize) * 0.5f)
            }
        }
        Icon(
            homeScreenItems[index], null,
            modifier = Modifier
                .alpha(opacity)
                .scale(opacity)
        )
    }
}

Result:

Solution 2:[2]

add isFocused param for HomeScreenItem to handle its state.

Now handle this param based on some condition, when that particular item will get in focus.

Condition to get the item in focus:

val scrollState = rememberLazyListState()
val focusableIndex = scrollState.firstVisibleItemIndex + scrollState.layoutInfo.visibleItemsInfo.size/2

or have some custom logic to do that.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2