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
import androidx.annotation.StringRes
import com.sadellie.unitto.R
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.
* 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),
CURRENCY(res = R.string.currency),
MASS(res = R.string.mass),

View File

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

View File

@ -24,7 +24,6 @@ import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@ -40,34 +39,29 @@ import com.sadellie.unitto.R
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 changeAction Function to change current unit. Called when choosing unit
* @param favoriteAction Function to mark unit as favorite. It's a toggle
* @param item The unit itself
* @param isSelected Whether this unit is selected or not (current pair of unit)
* @param convertValue Used for right side units. Shows conversion from unit on the left
* @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 shortNameLabel String on the second line.
*/
@Composable
fun UnitListItem(
modifier: Modifier,
changeAction: (AbstractUnit) -> Unit,
favoriteAction: (AbstractUnit) -> Unit,
item: AbstractUnit,
private fun BasicUnitListItem(
unit: AbstractUnit,
isSelected: Boolean,
convertValue: (AbstractUnit) -> String
selectAction: (AbstractUnit) -> Unit,
favoriteAction: (AbstractUnit) -> Unit,
shortNameLabel: String
) {
var isFavorite: Boolean by rememberSaveable { mutableStateOf(item.isFavorite) }
var convertedValue: String by remember { mutableStateOf("") }
var isFavorite: Boolean by rememberSaveable { mutableStateOf(unit.isFavorite) }
Box(
modifier = modifier
modifier = Modifier
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = rememberRipple(), // You can also change the color and radius of the ripple
onClick = {
changeAction(item)
}
onClick = { selectAction(unit) }
)
.padding(horizontal = 12.dp)
) {
@ -87,19 +81,19 @@ fun UnitListItem(
.weight(1f)
) {
Text(
text = stringResource(id = item.displayName),
text = stringResource(id = unit.displayName),
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
Text(
text = convertedValue + stringResource(id = item.shortName),
text = shortNameLabel,
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
}
IconButton(onClick = { favoriteAction(item); isFavorite = !isFavorite }) {
IconButton(onClick = { favoriteAction(unit); isFavorite = !isFavorite }) {
AnimatedContent(
targetState = isFavorite,
transitionSpec = {
@ -107,15 +101,57 @@ fun UnitListItem(
}
) {
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)
)
}
}
}
}
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 changeAction Action to perform when clicking on a 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 unitFrom Current unit on the left. Used for right side screen
* @param currentUnit Currently selected unit. Will be visually distinguishable
* @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.
*/
@Composable
fun UnitsList(
groupedUnits: Map<UnitGroup, List<AbstractUnit>>,
changeAction: (AbstractUnit) -> Unit,
selectAction: (AbstractUnit) -> Unit,
favoriteAction: (AbstractUnit) -> Unit,
inputValue: BigDecimal = BigDecimal.ONE,
unitFrom: AbstractUnit? = null,
currentUnit: AbstractUnit,
) {
LazyColumn(
@ -42,41 +38,82 @@ fun UnitsList(
content = {
if (groupedUnits.isEmpty()) {
item { SearchPlaceholder() }
} else {
groupedUnits.forEach { (unitGroup, listOfUnits) ->
stickyHeader {
Text(
modifier = Modifier
.background(MaterialTheme.colorScheme.background)
.fillMaxWidth()
.padding(vertical = 12.dp, horizontal = 8.dp),
text = stringResource(id = unitGroup.res),
style = MaterialTheme.typography.titleMedium,
color = MaterialTheme.colorScheme.primary
)
return@LazyColumn
}
groupedUnits.forEach { (unitGroup, listOfUnits) ->
stickyHeader { Header(text = stringResource(id = unitGroup.res)) }
items(items = listOfUnits, key = { it.unitId }) { unit ->
UnitListItem(
modifier = Modifier,
changeAction = changeAction,
favoriteAction = favoriteAction,
item = unit,
unit = unit,
isSelected = currentUnit == unit,
convertValue = {
if (unitFrom != null) {
Formatter.format(
unitFrom
.convert(it, inputValue, 3)
.toPlainString()
).plus(" ")
} else {
""
}
}
selectAction = selectAction,
favoriteAction = favoriteAction
)
}
}
}
}
)
}
/**
* 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
)
}