After creating a sealed class for my LazyColumn
, how can I use the inital of every item for an alphabet scroller? it.?
is where my problem occurs as for some reason, it does not let me accesss my sealed class and use it, i.e. itemName
.
sealed class Clothes {
data class FixedSizeClothing(val itemName: Int, val sizePlaceholder: Int): Clothes()
data class MultiSizeClothing(val itemName: Int, val sizePlaceholders:
List<Int>): Clothes()
}
val clothingItems = remember { listOf(
Clothes.FixedSizeClothing(itemName = R.string.jumper, itemPlaceholder = 8),
Clothes.MultiSizeClothing(itemName = R.string.dress, itemPlaceholders = listOf(0, 2))
)
}
val headers = remember { clothingItems.map { getString(it.?).first().uppercase() }.toSet().toList() }
val listState = rememberLazyListState()
LazyColumn(
state = listState,
modifier = Modifier
.weight(1f)
.padding(it)
) {
items(clothingItems) {
val text1 = when (it) {
is Clothes.FixedSizeClothing ->
stringResource(id = it.itemName)
is Clothes.MultiSizeClothing ->
stringResource(id = it.itemName)
}
val text2 = when (it) {
is Clothes.FixedSizeClothing ->
stringResource(id = R.string.size_placeholder, it.sizePlaceholder)
is Clothes.MultiSizeClothing ->
stringResource(id = R.string.size_placeholder_and_placeholder, it.itemPlaceholders[0], it.itemPlaceholders[1])
}
Column(modifier = Modifier
.fillMaxWidth()
.clickable {}) {...}
}
}
val offsets = remember { mutableStateMapOf<Int, Float>() }
var selectedHeaderIndex by remember { mutableStateOf(0) }
val scope = rememberCoroutineScope()
fun updateSelectedIndexIfNeeded(offset: Float) {
val index = offsets
.mapValues { abs(it.value - offset) }
.entries
.minByOrNull { it.value }
?.key ?: return
if (selectedHeaderIndex == index) return
selectedHeaderIndex = index
val selectedItemIndex = clothingItems.indexOfFirst { getString(it.?).first().uppercase() == headers[selectedHeaderIndex] }
scope.launch {
listState.scrollToItem(selectedItemIndex)
}
}
Column(
verticalArrangement = Arrangement.SpaceEvenly,
modifier = Modifier
.fillMaxHeight()
.background(Color.Gray)
.pointerInput(Unit) {
detectTapGestures {
updateSelectedIndexIfNeeded(it.y)
}
}
.pointerInput(Unit) {
detectVerticalDragGestures { change, _ ->
updateSelectedIndexIfNeeded(change.position.y)
}
}
) {
headers.forEachIndexed { i, header ->
Text(
header,
modifier = Modifier
.onGloballyPositioned {
offsets[i] = it.boundsInParent().center.y
},
color = Color.White
)
}
}
CodePudding user response:
You can define the val itemName: Int
in the parent Clothes
class and override it in you other subclasses. If you do that, you then do not need to use a when
expression if you just want to access the itemName
property.
The parent class can be a sealed interface instead of a sealed class. That way it is a bit more flexible and a bit less verbose when overriding its properties. Also : Clothes()
then becomes just : Clothes
sealed interface Clothes {
val itemName: Int
data class FixedSizeClothing(override val itemName: Int, val sizePlaceholder: Int): Clothes
data class MultiSizeClothing(override val itemName: Int, val sizePlaceholders: List<Int>): Clothes
}
And the line where you create the headers becomes
val headers = clothingItems.map { stringResource(it.itemName).first().uppercase() }.toSet().toList()
The remember {}
does not make much sense because if your data changes so can the set of initial letters. You also cannot use the stringResouce()
function inside remember {}
, because stringResouce()
has to be used inside a @Composable function.
There is a different way of obtaining Resources and then using resources.getString(...)
if you would like to retrieve resource strings inside a remember {}
block. But in this case the remember {}
block does not make sense due to the data potentially changing. The optimization to cache initial letters would have to be done in a different way.