Increase use of space in detail view

This commit is contained in:
Yuri Schimke 2021-10-17 19:07:17 +01:00 committed by John O'Reilly
parent 644e547cb9
commit c866bde5fa
5 changed files with 118 additions and 120 deletions

View file

@ -8,7 +8,7 @@ buildscript {
dependencies {
// keeping this here to allow AS to automatically update
classpath("com.android.tools.build:gradle:7.0.2")
classpath("com.android.tools.build:gradle:7.0.3")
with(Deps.Gradle) {
classpath(kotlin)

View file

@ -1,80 +0,0 @@
package androidx.wear.compose.samples.shared
import androidx.compose.runtime.Stable
import androidx.compose.ui.Modifier
import androidx.compose.ui.composed
import androidx.compose.ui.layout.LayoutModifier
import androidx.compose.ui.layout.Measurable
import androidx.compose.ui.layout.MeasureResult
import androidx.compose.ui.layout.MeasureScope
import androidx.compose.ui.platform.InspectorInfo
import androidx.compose.ui.platform.InspectorValueInfo
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.debugInspectorInfo
import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.constrainHeight
import androidx.compose.ui.unit.constrainWidth
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.offset
import kotlin.math.sqrt
@Stable
fun Modifier.fillMaxRectangle() = composed {
val isRound = LocalContext.current.resources.configuration.isScreenRound
var inset: Dp = 0.dp
if (isRound) {
val screenHeightDp = LocalContext.current.resources.configuration.screenHeightDp
val screenWidthDp = LocalContext.current.resources.configuration.smallestScreenWidthDp
val maxSquareEdge = (sqrt(((screenHeightDp * screenWidthDp) / 2).toDouble()))
inset = Dp(((screenHeightDp - maxSquareEdge) / 2).toFloat())
}
this.then(RectangleInsetModifier(
inset = inset,
inspectorInfo = debugInspectorInfo {
name = "fillMaxRectangle"
})
)
}
private class RectangleInsetModifier(
val inset: Dp = 0.dp,
inspectorInfo: InspectorInfo.() -> Unit
) : LayoutModifier, InspectorValueInfo(inspectorInfo) {
init {
require(
(inset.value >= 0f || inset == Dp.Unspecified)
) {
"Inset must be non-negative"
}
}
override fun MeasureScope.measure(
measurable: Measurable,
constraints: Constraints
): MeasureResult {
val totalOffsetInPx = inset.roundToPx() * 2
val placeable = measurable.measure(constraints.offset(-totalOffsetInPx, -totalOffsetInPx))
val width = constraints.constrainWidth(placeable.width + totalOffsetInPx)
val height = constraints.constrainHeight(placeable.height + totalOffsetInPx)
return layout(width, height) {
placeable.place(inset.roundToPx(), inset.roundToPx())
}
}
override fun hashCode(): Int {
var result = inset.hashCode()
result = 31 * result + inset.hashCode()
return result
}
override fun equals(other: Any?): Boolean {
val otherModifier = other as? RectangleInsetModifier ?: return false
return inset == otherModifier.inset
}
}

View file

@ -5,18 +5,14 @@ import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.runtime.CompositionLocalProvider
import androidx.navigation.NavType
import androidx.navigation.navArgument
import androidx.wear.compose.material.ExperimentalWearMaterialApi
import androidx.wear.compose.navigation.SwipeDismissableNavHost
import androidx.wear.compose.navigation.composable
import androidx.wear.compose.navigation.rememberSwipeDismissableNavController
import coil.ImageLoader
import coil.compose.LocalImageLoader
import com.surrus.common.repository.PeopleInSpaceRepositoryInterface
import org.koin.android.ext.android.inject
sealed class Screen(val route: String) {
object PersonList : Screen("personList")
object PersonDetails : Screen("personDetails")

View file

@ -1,18 +1,38 @@
package com.surrus.peopleinspace
import android.content.res.Configuration
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.CutCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.wear.compose.material.ExperimentalWearMaterialApi
import androidx.wear.compose.material.MaterialTheme
import androidx.wear.compose.material.PositionIndicator
import androidx.wear.compose.material.Scaffold
import androidx.wear.compose.material.Text
import androidx.wear.compose.samples.shared.fillMaxRectangle
import coil.compose.rememberImagePainter
import androidx.wear.compose.material.Vignette
import androidx.wear.compose.material.VignettePosition
import com.surrus.common.remote.Assignment
import org.koin.androidx.compose.getViewModel
@Composable
@ -28,41 +48,99 @@ fun PersonDetailsScreen(personName: String) {
}
}
PersonDetailsScreen(person)
}
@OptIn(ExperimentalWearMaterialApi::class)
@Composable
private fun PersonDetailsScreen(person: Assignment?) {
val scrollState = rememberScrollState()
person?.let { person ->
Box(modifier = Modifier
.fillMaxSize()
.fillMaxRectangle()) {
MaterialTheme {
Scaffold(
vignette = {
if (person != null) {
Vignette(vignettePosition = VignettePosition.Bottom)
}
},
positionIndicator = { PositionIndicator(scrollState = scrollState) }
) {
Column(
modifier = Modifier
.padding(8.dp)
.fillMaxWidth()
.fillMaxSize()
.padding(horizontal = if (LocalConfiguration.current.isScreenRound) 18.dp else 8.dp)
.verticalScroll(scrollState),
horizontalAlignment = Alignment.CenterHorizontally
) {
Spacer(modifier = Modifier.size(if (LocalConfiguration.current.isScreenRound) 32.dp else 12.dp))
Text(
person.name,
style = MaterialTheme.typography.body2,
textAlign = TextAlign.Center
Image(
painter = rememberAstronautPainter(person),
modifier = Modifier
.size(120.dp)
.clip(CutCornerShape(30.dp)),
contentDescription = person?.name,
)
Spacer(modifier = Modifier.size(12.dp))
val imageUrl = person.personImageUrl ?: ""
if (imageUrl.isNotEmpty()) {
Image(
painter = rememberImagePainter(imageUrl),
modifier = Modifier.size(120.dp), contentDescription = person.name
Text(
person?.name ?: "Astronaut not found.",
style = MaterialTheme.typography.title1,
textAlign = TextAlign.Center
)
val personBio = person?.personBio
if (personBio != null) {
Spacer(modifier = Modifier.size(12.dp))
Text(
personBio,
style = MaterialTheme.typography.body2,
textAlign = TextAlign.Justify
)
}
Spacer(modifier = Modifier.size(24.dp))
val bio = person.personBio ?: ""
Text(bio, style = MaterialTheme.typography.body1, textAlign = TextAlign.Center)
Spacer(modifier = Modifier.size(if (LocalConfiguration.current.isScreenRound) 48.dp else 12.dp))
}
}
}
}
@Preview(
widthDp = 300,
heightDp = 300,
apiLevel = 26,
uiMode = Configuration.UI_MODE_TYPE_WATCH,
backgroundColor = 0x000000,
showBackground = true
)
@Composable
fun PersonDetailsScreenPreview() {
val person = remember {
Assignment(
"Apollo 11",
"Neil Armstrong",
"https://www.biography.com/.image/ar_1:1%2Cc_fill%2Ccs_srgb%2Cfl_progressive%2Cq_auto:good%2Cw_1200/MTc5OTk0MjgyMzk5MTE0MzYy/gettyimages-150832381.jpg",
"Mark Thomas Vande Hei (born November 10, 1966) is a retired United States Army officer and NASA astronaut who served as a flight Engineer for Expedition 53 and 54 on the International Space Station."
)
}
Box(modifier = Modifier.background(Color.Black)) {
PersonDetailsScreen(person)
}
}
@Preview(
widthDp = 300,
heightDp = 300,
apiLevel = 26,
uiMode = Configuration.UI_MODE_TYPE_WATCH,
backgroundColor = 0x000000,
showBackground = true
)
@Composable
fun PersonDetailsScreenNotFoundPreview() {
Box(modifier = Modifier.background(Color.Black)) {
PersonDetailsScreen(null)
}
}

View file

@ -34,7 +34,6 @@ import androidx.wear.compose.material.Text
import coil.annotation.ExperimentalCoilApi
import coil.compose.rememberImagePainter
import com.surrus.common.remote.Assignment
import com.surrus.common.repository.PeopleInSpaceRepositoryInterface
import org.koin.androidx.compose.getViewModel
const val PersonListTag = "PersonList"
@ -117,16 +116,7 @@ fun PersonView(person: Assignment, personSelected: (person: Assignment) -> Unit)
verticalAlignment = Alignment.CenterVertically,
) {
Image(
painter = rememberImagePainter(person.personImageUrl) {
// Use the generic astronaut SVG for missing or error (404?).
fallback(R.drawable.ic_american_astronaut)
error(R.drawable.ic_american_astronaut)
if (LocalInspectionMode.current) {
// Show error image instead of blank in @Preview
placeholder(R.drawable.ic_american_astronaut)
}
},
painter = rememberAstronautPainter(person),
modifier = Modifier
.size(50.dp)
.clip(MaterialTheme.shapes.medium),
@ -147,6 +137,20 @@ fun PersonView(person: Assignment, personSelected: (person: Assignment) -> Unit)
}
}
@OptIn(ExperimentalCoilApi::class)
@Composable
fun rememberAstronautPainter(person: Assignment?) =
rememberImagePainter(person?.personImageUrl) {
// Use the generic astronaut SVG for missing or error (404?).
fallback(R.drawable.ic_american_astronaut)
error(R.drawable.ic_american_astronaut)
if (LocalInspectionMode.current) {
// Show error image instead of blank in @Preview
placeholder(R.drawable.ic_american_astronaut)
}
}
@Preview(
widthDp = 300,
heightDp = 80,