Refactored units list composables

This commit is contained in:
Sad Ellie 2022-06-12 13:38:56 +03:00
parent bc55e23e04
commit 2bfacd9e42
4 changed files with 152 additions and 75 deletions

View File

@ -1,5 +1,6 @@
package com.sadellie.unitto.data.units package com.sadellie.unitto.data.units
import androidx.annotation.StringRes
import com.sadellie.unitto.R import com.sadellie.unitto.R
val ALL_UNIT_GROUPS: List<UnitGroup> by lazy { val ALL_UNIT_GROUPS: List<UnitGroup> by lazy {
@ -10,7 +11,10 @@ val ALL_UNIT_GROUPS: List<UnitGroup> by lazy {
* As not all measurements can be converted between into other, we separate them into groups. * As not all measurements can be converted between into other, we separate them into groups.
* Within one groups all measurements can be converted * Within one groups all measurements can be converted
*/ */
enum class UnitGroup(val res: Int, val canNegate: Boolean = false) { enum class UnitGroup(
@StringRes val res: Int,
val canNegate: Boolean = false
) {
LENGTH(res = R.string.length), LENGTH(res = R.string.length),
CURRENCY(res = R.string.currency), CURRENCY(res = R.string.currency),
MASS(res = R.string.mass), MASS(res = R.string.mass),

View File

@ -84,17 +84,17 @@ fun SecondScreen(
) )
UnitsList( UnitsList(
groupedUnits = unitsList, groupedUnits = unitsList,
changeAction = { viewModel.changeUnitFrom(it); focusManager.clearFocus(true); navigateUp() }, selectAction = { viewModel.changeUnitFrom(it); focusManager.clearFocus(true); navigateUp() },
favoriteAction = { viewModel.favoriteUnit(it) }, favoriteAction = { viewModel.favoriteUnit(it) },
currentUnit = viewModel.unitFrom, currentUnit = viewModel.unitFrom,
) )
} else { } else {
UnitsList( UnitsList(
groupedUnits = unitsList, groupedUnits = unitsList,
changeAction = { viewModel.changeUnitTo(it); focusManager.clearFocus(true); navigateUp() }, selectAction = { viewModel.changeUnitTo(it); focusManager.clearFocus(true); navigateUp() },
favoriteAction = { viewModel.favoriteUnit(it) }, favoriteAction = { viewModel.favoriteUnit(it) },
currentUnit = viewModel.unitTo, currentUnit = viewModel.unitTo,
inputValue = viewModel.mainUIState.inputValue.toBigDecimal(), input = viewModel.mainUIState.inputValue.toBigDecimal(),
unitFrom = viewModel.unitFrom unitFrom = viewModel.unitFrom
) )
} }

View File

@ -24,7 +24,6 @@ import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
@ -40,34 +39,29 @@ import com.sadellie.unitto.R
import com.sadellie.unitto.data.units.AbstractUnit import com.sadellie.unitto.data.units.AbstractUnit
/** /**
* Represents one list item. Once clicked will navigate up * Represents one list item. Once clicked will navigate up.
* *
* @param modifier Modifier that will be applied to a box that surrounds row. * @param unit The unit itself.
* @param changeAction Function to change current unit. Called when choosing unit * @param isSelected Whether this unit is selected or not (current pair of unit).
* @param favoriteAction Function to mark unit as favorite. It's a toggle * @param selectAction Function to change current unit. Called when choosing unit.
* @param item The unit itself * @param favoriteAction Function to mark unit as favorite. It's a toggle.
* @param isSelected Whether this unit is selected or not (current pair of unit) * @param shortNameLabel String on the second line.
* @param convertValue Used for right side units. Shows conversion from unit on the left
*/ */
@Composable @Composable
fun UnitListItem( private fun BasicUnitListItem(
modifier: Modifier, unit: AbstractUnit,
changeAction: (AbstractUnit) -> Unit,
favoriteAction: (AbstractUnit) -> Unit,
item: AbstractUnit,
isSelected: Boolean, isSelected: Boolean,
convertValue: (AbstractUnit) -> String selectAction: (AbstractUnit) -> Unit,
favoriteAction: (AbstractUnit) -> Unit,
shortNameLabel: String
) { ) {
var isFavorite: Boolean by rememberSaveable { mutableStateOf(item.isFavorite) } var isFavorite: Boolean by rememberSaveable { mutableStateOf(unit.isFavorite) }
var convertedValue: String by remember { mutableStateOf("") }
Box( Box(
modifier = modifier modifier = Modifier
.clickable( .clickable(
interactionSource = remember { MutableInteractionSource() }, interactionSource = remember { MutableInteractionSource() },
indication = rememberRipple(), // You can also change the color and radius of the ripple indication = rememberRipple(), // You can also change the color and radius of the ripple
onClick = { onClick = { selectAction(unit) }
changeAction(item)
}
) )
.padding(horizontal = 12.dp) .padding(horizontal = 12.dp)
) { ) {
@ -87,19 +81,19 @@ fun UnitListItem(
.weight(1f) .weight(1f)
) { ) {
Text( Text(
text = stringResource(id = item.displayName), text = stringResource(id = unit.displayName),
maxLines = 1, maxLines = 1,
overflow = TextOverflow.Ellipsis overflow = TextOverflow.Ellipsis
) )
Text( Text(
text = convertedValue + stringResource(id = item.shortName), text = shortNameLabel,
style = MaterialTheme.typography.bodySmall, style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant, color = MaterialTheme.colorScheme.onSurfaceVariant,
maxLines = 1, maxLines = 1,
overflow = TextOverflow.Ellipsis overflow = TextOverflow.Ellipsis
) )
} }
IconButton(onClick = { favoriteAction(item); isFavorite = !isFavorite }) { IconButton(onClick = { favoriteAction(unit); isFavorite = !isFavorite }) {
AnimatedContent( AnimatedContent(
targetState = isFavorite, targetState = isFavorite,
transitionSpec = { transitionSpec = {
@ -107,15 +101,57 @@ fun UnitListItem(
} }
) { ) {
Icon( Icon(
if (item.isFavorite) Icons.Filled.Favorite else Icons.Filled.FavoriteBorder, if (unit.isFavorite) Icons.Filled.Favorite else Icons.Filled.FavoriteBorder,
contentDescription = stringResource(id = R.string.favorite_button_description) contentDescription = stringResource(id = R.string.favorite_button_description)
) )
} }
} }
} }
} }
LaunchedEffect(Unit) {
// Converting value
convertedValue = convertValue(item)
}
} }
/**
* Represents one list item. Once clicked will navigate up. Has without conversion.
*
* @param unit The unit itself.
* @param isSelected Whether this unit is selected or not (current pair of unit).
* @param selectAction Function to change current unit. Called when choosing unit.
* @param favoriteAction Function to mark unit as favorite. It's a toggle.
*/
@Composable
fun UnitListItem(
unit: AbstractUnit,
isSelected: Boolean,
selectAction: (AbstractUnit) -> Unit,
favoriteAction: (AbstractUnit) -> Unit,
) = BasicUnitListItem(
unit = unit,
isSelected = isSelected,
selectAction = selectAction,
favoriteAction = favoriteAction,
stringResource(id = unit.shortName)
)
/**
* Represents one list item. Once clicked will navigate up. Has with conversion.
*
* @param unit The unit itself.
* @param isSelected Whether this unit is selected or not (current pair of unit).
* @param selectAction Function to change current unit. Called when choosing unit.
* @param favoriteAction Function to mark unit as favorite. It's a toggle.
* @param convertValue Function to call that will convert this unit.
*/
@Composable
fun UnitListItem(
unit: AbstractUnit,
isSelected: Boolean,
selectAction: (AbstractUnit) -> Unit,
favoriteAction: (AbstractUnit) -> Unit,
convertValue: (AbstractUnit) -> String
) = BasicUnitListItem(
unit = unit,
isSelected = isSelected,
selectAction = selectAction,
favoriteAction = favoriteAction,
shortNameLabel = "${convertValue(unit)} ${stringResource(id = unit.shortName)}"
)

View File

@ -19,22 +19,18 @@ import java.math.BigDecimal
/** /**
* Component with grouped units * Component with grouped units.
* *
* @param groupedUnits Grouped [AbstractUnit]s to be listed * @param groupedUnits Grouped [AbstractUnit]s to be listed.
* @param changeAction Action to perform when clicking on a list item * @param selectAction Action to perform when clicking on a list item.
* @param favoriteAction Action to perform when clicking "favorite" button on list item * @param favoriteAction Action to perform when clicking "favorite" button on list item.
* @param inputValue Current input value. Used for right side screen * @param currentUnit Currently selected unit. Will be visually distinguishable.
* @param unitFrom Current unit on the left. Used for right side screen
* @param currentUnit Currently selected unit. Will be visually distinguishable
*/ */
@Composable @Composable
fun UnitsList( fun UnitsList(
groupedUnits: Map<UnitGroup, List<AbstractUnit>>, groupedUnits: Map<UnitGroup, List<AbstractUnit>>,
changeAction: (AbstractUnit) -> Unit, selectAction: (AbstractUnit) -> Unit,
favoriteAction: (AbstractUnit) -> Unit, favoriteAction: (AbstractUnit) -> Unit,
inputValue: BigDecimal = BigDecimal.ONE,
unitFrom: AbstractUnit? = null,
currentUnit: AbstractUnit, currentUnit: AbstractUnit,
) { ) {
LazyColumn( LazyColumn(
@ -42,41 +38,82 @@ fun UnitsList(
content = { content = {
if (groupedUnits.isEmpty()) { if (groupedUnits.isEmpty()) {
item { SearchPlaceholder() } item { SearchPlaceholder() }
} else { return@LazyColumn
groupedUnits.forEach { (unitGroup, listOfUnits) -> }
stickyHeader { groupedUnits.forEach { (unitGroup, listOfUnits) ->
Text( stickyHeader { Header(text = stringResource(id = unitGroup.res)) }
modifier = Modifier items(items = listOfUnits, key = { it.unitId }) { unit ->
.background(MaterialTheme.colorScheme.background) UnitListItem(
.fillMaxWidth() unit = unit,
.padding(vertical = 12.dp, horizontal = 8.dp), isSelected = currentUnit == unit,
text = stringResource(id = unitGroup.res), selectAction = selectAction,
style = MaterialTheme.typography.titleMedium, favoriteAction = favoriteAction
color = MaterialTheme.colorScheme.primary )
)
}
items(items = listOfUnits, key = { it.unitId }) { unit ->
UnitListItem(
modifier = Modifier,
changeAction = changeAction,
favoriteAction = favoriteAction,
item = unit,
isSelected = currentUnit == unit,
convertValue = {
if (unitFrom != null) {
Formatter.format(
unitFrom
.convert(it, inputValue, 3)
.toPlainString()
).plus(" ")
} else {
""
}
}
)
}
} }
} }
} }
) )
} }
/**
* Component with grouped units/
*
* @param groupedUnits Grouped [AbstractUnit]s to be listed.
* @param selectAction Action to perform when clicking on a list item.
* @param favoriteAction Action to perform when clicking "favorite" button on list item.
* @param currentUnit Currently selected unit. Will be visually distinguishable.
* @param unitFrom Unit to convert from. Used for conversion in short name field.
* @param input Current input value. Used for conversion in short name field.
*/
@Composable
fun UnitsList(
groupedUnits: Map<UnitGroup, List<AbstractUnit>>,
selectAction: (AbstractUnit) -> Unit,
favoriteAction: (AbstractUnit) -> Unit,
currentUnit: AbstractUnit,
unitFrom: AbstractUnit,
input: BigDecimal
) {
LazyColumn(
modifier = Modifier.fillMaxSize(),
content = {
if (groupedUnits.isEmpty()) {
item { SearchPlaceholder() }
return@LazyColumn
}
groupedUnits.forEach { (unitGroup, listOfUnits) ->
stickyHeader { Header(text = stringResource(id = unitGroup.res)) }
items(items = listOfUnits, key = { it.unitId }) { unit ->
UnitListItem(
unit = unit,
isSelected = currentUnit == unit,
selectAction = selectAction,
favoriteAction = favoriteAction,
convertValue = {
Formatter.format(unitFrom.convert(unit, input, 3).toPlainString())
}
)
}
}
}
)
}
/**
* Unit group header.
*
* @param text Unit group name.
*/
@Composable
private fun Header(text: String) {
Text(
modifier = Modifier
.background(MaterialTheme.colorScheme.background)
.fillMaxWidth()
.padding(vertical = 12.dp, horizontal = 8.dp),
text = text,
style = MaterialTheme.typography.titleMedium,
color = MaterialTheme.colorScheme.primary
)
}