|
|
@ -1,6 +1,7 @@ |
|
|
|
package com.whitefish.app.ui.home.exercise |
|
|
|
|
|
|
|
import androidx.compose.foundation.Canvas |
|
|
|
import androidx.compose.foundation.Image |
|
|
|
import androidx.compose.foundation.background |
|
|
|
import androidx.compose.foundation.layout.* |
|
|
|
import androidx.compose.foundation.lazy.LazyColumn |
|
|
@ -17,10 +18,24 @@ import androidx.compose.ui.graphics.Brush |
|
|
|
import androidx.compose.ui.graphics.Color |
|
|
|
import androidx.compose.ui.graphics.StrokeCap |
|
|
|
import androidx.compose.ui.graphics.drawscope.Stroke |
|
|
|
import androidx.compose.ui.layout.ContentScale |
|
|
|
import androidx.compose.ui.text.font.FontWeight |
|
|
|
import androidx.compose.ui.text.style.TextAlign |
|
|
|
import androidx.compose.ui.unit.dp |
|
|
|
import androidx.compose.ui.unit.sp |
|
|
|
import org.jetbrains.compose.resources.DrawableResource |
|
|
|
import org.jetbrains.compose.resources.painterResource |
|
|
|
import org.jetbrains.compose.ui.tooling.preview.Preview |
|
|
|
import ringappkmp.composeapp.generated.resources.Res |
|
|
|
import ringappkmp.composeapp.generated.resources.bg |
|
|
|
import ringappkmp.composeapp.generated.resources.bg_exercise_history |
|
|
|
import ringappkmp.composeapp.generated.resources.bg_exercise_target |
|
|
|
import ringappkmp.composeapp.generated.resources.bg_score |
|
|
|
import ringappkmp.composeapp.generated.resources.ic_exercise_riding |
|
|
|
import ringappkmp.composeapp.generated.resources.ic_exercise_running |
|
|
|
import ringappkmp.composeapp.generated.resources.ic_exercise_swimming |
|
|
|
import ringappkmp.composeapp.generated.resources.ic_free |
|
|
|
import ringappkmp.composeapp.generated.resources.ic_running |
|
|
|
|
|
|
|
@OptIn(ExperimentalMaterial3Api::class) |
|
|
|
@Composable |
|
|
@ -96,22 +111,22 @@ fun ExerciseTypeCards() { |
|
|
|
item { |
|
|
|
ExerciseTypeCard( |
|
|
|
title = "跑步", |
|
|
|
gradient = listOf(Color(0xFF4FC3F7), Color(0xFF29B6F6)), |
|
|
|
modifier = Modifier.size(160.dp, 120.dp) |
|
|
|
modifier = Modifier.size(154.dp, 154.dp), |
|
|
|
background = Res.drawable.ic_exercise_running |
|
|
|
) |
|
|
|
} |
|
|
|
item { |
|
|
|
ExerciseTypeCard( |
|
|
|
title = "骑行", |
|
|
|
gradient = listOf(Color(0xFFFFB74D), Color(0xFFFF9800)), |
|
|
|
modifier = Modifier.size(160.dp, 120.dp) |
|
|
|
background = Res.drawable.ic_exercise_riding, |
|
|
|
modifier = Modifier.size(154.dp, 154.dp) |
|
|
|
) |
|
|
|
} |
|
|
|
item { |
|
|
|
ExerciseTypeCard( |
|
|
|
title = "游泳", |
|
|
|
gradient = listOf(Color(0xFF4DD0E1), Color(0xFF00BCD4)), |
|
|
|
modifier = Modifier.size(160.dp, 120.dp) |
|
|
|
background = Res.drawable.ic_exercise_swimming, |
|
|
|
modifier = Modifier.size(154.dp, 154.dp) |
|
|
|
) |
|
|
|
} |
|
|
|
} |
|
|
@ -120,7 +135,7 @@ fun ExerciseTypeCards() { |
|
|
|
@Composable |
|
|
|
fun ExerciseTypeCard( |
|
|
|
title: String, |
|
|
|
gradient: List<Color>, |
|
|
|
background: DrawableResource, |
|
|
|
modifier: Modifier = Modifier |
|
|
|
) { |
|
|
|
Card( |
|
|
@ -132,17 +147,24 @@ fun ExerciseTypeCard( |
|
|
|
modifier = Modifier |
|
|
|
.fillMaxSize() |
|
|
|
.background( |
|
|
|
Brush.linearGradient(gradient), |
|
|
|
Color.Transparent, |
|
|
|
RoundedCornerShape(16.dp) |
|
|
|
), |
|
|
|
contentAlignment = Alignment.Center |
|
|
|
contentAlignment = Alignment.TopStart |
|
|
|
) { |
|
|
|
Text( |
|
|
|
text = title, |
|
|
|
fontSize = 18.sp, |
|
|
|
color = Color.White, |
|
|
|
fontWeight = FontWeight.Bold |
|
|
|
Image( |
|
|
|
painterResource(background), |
|
|
|
contentDescription = null, |
|
|
|
modifier = Modifier.fillMaxSize() |
|
|
|
) |
|
|
|
Row(modifier = Modifier.padding(top = 20.dp, start = 20.dp)) { |
|
|
|
Text( |
|
|
|
text = title, |
|
|
|
fontSize = 24.sp, |
|
|
|
color = Color.White, |
|
|
|
fontWeight = FontWeight.Bold, |
|
|
|
) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
@ -158,7 +180,11 @@ fun AddExerciseButton() { |
|
|
|
shape = RoundedCornerShape(16.dp), |
|
|
|
elevation = CardDefaults.cardElevation(10.dp) |
|
|
|
) { |
|
|
|
Row(modifier = Modifier.fillMaxSize(),verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.Center) { |
|
|
|
Row( |
|
|
|
modifier = Modifier.fillMaxSize(), |
|
|
|
verticalAlignment = Alignment.CenterVertically, |
|
|
|
horizontalArrangement = Arrangement.Center |
|
|
|
) { |
|
|
|
Text( |
|
|
|
text = "+ 添加运动记录", |
|
|
|
fontSize = 16.sp, |
|
|
@ -173,58 +199,72 @@ fun AddExerciseButton() { |
|
|
|
fun ExerciseRecordsSection() { |
|
|
|
Card( |
|
|
|
modifier = Modifier.fillMaxWidth(), |
|
|
|
shape = RoundedCornerShape(16.dp), |
|
|
|
colors = CardDefaults.cardColors(Color.White.copy(alpha = 0.9f)) |
|
|
|
shape = RoundedCornerShape(24.dp), |
|
|
|
colors = CardDefaults.cardColors(containerColor = Color.Transparent) |
|
|
|
) { |
|
|
|
Column( |
|
|
|
modifier = Modifier.padding(16.dp) |
|
|
|
) { |
|
|
|
Row( |
|
|
|
modifier = Modifier.fillMaxWidth(), |
|
|
|
horizontalArrangement = Arrangement.SpaceBetween, |
|
|
|
verticalAlignment = Alignment.CenterVertically |
|
|
|
Box(modifier = Modifier.fillMaxSize()){ |
|
|
|
Image( |
|
|
|
painterResource(Res.drawable.bg_exercise_history), |
|
|
|
contentDescription = null, |
|
|
|
modifier = Modifier.fillMaxSize(), |
|
|
|
contentScale = ContentScale.Crop |
|
|
|
) |
|
|
|
Column( |
|
|
|
modifier = Modifier.padding(14.dp) |
|
|
|
) { |
|
|
|
Text( |
|
|
|
text = "运动记录", |
|
|
|
fontSize = 16.sp, |
|
|
|
fontWeight = FontWeight.Bold, |
|
|
|
color = Color(0xFF666666) |
|
|
|
Row( |
|
|
|
modifier = Modifier.fillMaxWidth(), |
|
|
|
horizontalArrangement = Arrangement.SpaceBetween, |
|
|
|
verticalAlignment = Alignment.CenterVertically |
|
|
|
) { |
|
|
|
Text( |
|
|
|
text = "运动记录", |
|
|
|
fontSize = 16.sp, |
|
|
|
fontWeight = FontWeight.Bold, |
|
|
|
color = Color(0xffffffff) |
|
|
|
) |
|
|
|
Text( |
|
|
|
text = "查看更多", |
|
|
|
fontSize = 14.sp, |
|
|
|
color = Color(0xffffffff) |
|
|
|
) |
|
|
|
} |
|
|
|
|
|
|
|
Spacer(modifier = Modifier.height(16.dp)) |
|
|
|
|
|
|
|
// 户外跑步记录 |
|
|
|
ExerciseRecordItem( |
|
|
|
iconColor = Color(0xFF2196F3), |
|
|
|
iconRes = Res.drawable.ic_running, |
|
|
|
title = "户外跑步", |
|
|
|
distance = "2.85公里", |
|
|
|
time = "00:20:36", |
|
|
|
pace = "7'59\"公里" |
|
|
|
) |
|
|
|
Text( |
|
|
|
text = "查看更多", |
|
|
|
fontSize = 14.sp, |
|
|
|
color = Color(0xFF9C27B0) |
|
|
|
|
|
|
|
Row(modifier = Modifier.height(16.dp).padding(start = 65.dp), verticalAlignment = Alignment.CenterVertically){ |
|
|
|
Box(modifier = Modifier.background(Color.White).height(1.dp).fillMaxWidth()) |
|
|
|
} |
|
|
|
|
|
|
|
// 自由训练记录 |
|
|
|
ExerciseRecordItem( |
|
|
|
iconColor = Color(0xFF4CAF50), |
|
|
|
title = "自由训练", |
|
|
|
distance = "161千卡", |
|
|
|
time = "00:57:06", |
|
|
|
pace = "104次/分钟", |
|
|
|
iconRes = Res.drawable.ic_free |
|
|
|
) |
|
|
|
} |
|
|
|
|
|
|
|
Spacer(modifier = Modifier.height(16.dp)) |
|
|
|
|
|
|
|
// 户外跑步记录 |
|
|
|
ExerciseRecordItem( |
|
|
|
iconColor = Color(0xFF2196F3), |
|
|
|
title = "户外跑步", |
|
|
|
distance = "2.85公里", |
|
|
|
time = "00:20:36", |
|
|
|
pace = "7'59\"公里" |
|
|
|
) |
|
|
|
|
|
|
|
Spacer(modifier = Modifier.height(12.dp)) |
|
|
|
|
|
|
|
// 自由训练记录 |
|
|
|
ExerciseRecordItem( |
|
|
|
iconColor = Color(0xFF4CAF50), |
|
|
|
title = "自由训练", |
|
|
|
distance = "161千卡", |
|
|
|
time = "00:57:06", |
|
|
|
pace = "104次/分钟" |
|
|
|
) |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
@Composable |
|
|
|
fun ExerciseRecordItem( |
|
|
|
iconColor: Color, |
|
|
|
iconRes: DrawableResource, |
|
|
|
title: String, |
|
|
|
distance: String, |
|
|
|
time: String, |
|
|
@ -237,16 +277,11 @@ fun ExerciseRecordItem( |
|
|
|
// 运动图标占位 |
|
|
|
Box( |
|
|
|
modifier = Modifier |
|
|
|
.size(40.dp) |
|
|
|
.size(50.dp) |
|
|
|
.background(iconColor, CircleShape), |
|
|
|
contentAlignment = Alignment.Center |
|
|
|
) { |
|
|
|
Text( |
|
|
|
text = title.first().toString(), |
|
|
|
color = Color.White, |
|
|
|
fontSize = 16.sp, |
|
|
|
fontWeight = FontWeight.Bold |
|
|
|
) |
|
|
|
Image(painterResource(iconRes),contentDescription = null) |
|
|
|
} |
|
|
|
|
|
|
|
Spacer(modifier = Modifier.width(12.dp)) |
|
|
@ -254,28 +289,28 @@ fun ExerciseRecordItem( |
|
|
|
Column(modifier = Modifier.weight(1f)) { |
|
|
|
Text( |
|
|
|
text = title, |
|
|
|
fontSize = 16.sp, |
|
|
|
fontSize = 12.sp, |
|
|
|
fontWeight = FontWeight.Medium, |
|
|
|
color = Color(0xFF333333) |
|
|
|
color = Color(0xEFffffff), |
|
|
|
) |
|
|
|
Spacer(modifier = Modifier.height(4.dp)) |
|
|
|
Row { |
|
|
|
Row(modifier = Modifier.fillMaxWidth(),horizontalArrangement = Arrangement.SpaceBetween) { |
|
|
|
Text( |
|
|
|
text = distance, |
|
|
|
fontSize = 14.sp, |
|
|
|
color = Color(0xFF666666) |
|
|
|
fontSize = 12.sp, |
|
|
|
color = Color(0xFFffffff) |
|
|
|
) |
|
|
|
Spacer(modifier = Modifier.width(16.dp)) |
|
|
|
Text( |
|
|
|
text = time, |
|
|
|
fontSize = 14.sp, |
|
|
|
color = Color(0xFF666666) |
|
|
|
fontSize = 12.sp, |
|
|
|
color = Color(0xFFffffff) |
|
|
|
) |
|
|
|
Spacer(modifier = Modifier.width(16.dp)) |
|
|
|
Text( |
|
|
|
text = pace, |
|
|
|
fontSize = 14.sp, |
|
|
|
color = Color(0xFF666666) |
|
|
|
fontSize = 12.sp, |
|
|
|
color = Color(0xFFffffff) |
|
|
|
) |
|
|
|
} |
|
|
|
} |
|
|
@ -286,59 +321,62 @@ fun ExerciseRecordItem( |
|
|
|
fun ComprehensiveScoreSection() { |
|
|
|
Card( |
|
|
|
modifier = Modifier.fillMaxWidth(), |
|
|
|
shape = RoundedCornerShape(16.dp), |
|
|
|
colors = CardDefaults.cardColors(Color.White.copy(alpha = 0.9f)) |
|
|
|
shape = RoundedCornerShape(24.dp), |
|
|
|
colors = CardDefaults.cardColors(Color.Transparent) |
|
|
|
) { |
|
|
|
Column( |
|
|
|
modifier = Modifier.padding(16.dp) |
|
|
|
) { |
|
|
|
Text( |
|
|
|
text = "综合评分", |
|
|
|
fontSize = 16.sp, |
|
|
|
fontWeight = FontWeight.Bold, |
|
|
|
color = Color(0xFF333333) |
|
|
|
) |
|
|
|
|
|
|
|
Spacer(modifier = Modifier.height(20.dp)) |
|
|
|
|
|
|
|
// 圆形评分图表 |
|
|
|
Box( |
|
|
|
modifier = Modifier |
|
|
|
.fillMaxWidth() |
|
|
|
.height(200.dp), |
|
|
|
contentAlignment = Alignment.Center |
|
|
|
Box(modifier = Modifier.fillMaxSize()){ |
|
|
|
Image(painterResource(Res.drawable.bg_score),contentDescription = null, modifier = Modifier.fillMaxSize(), contentScale = ContentScale.Crop) |
|
|
|
Column( |
|
|
|
modifier = Modifier.padding(16.dp) |
|
|
|
) { |
|
|
|
CircularScoreChart(score = 75) |
|
|
|
} |
|
|
|
Text( |
|
|
|
text = "综合评分", |
|
|
|
fontSize = 16.sp, |
|
|
|
fontWeight = FontWeight.Bold, |
|
|
|
color = Color(0xFF333333) |
|
|
|
) |
|
|
|
|
|
|
|
Spacer(modifier = Modifier.height(20.dp)) |
|
|
|
Spacer(modifier = Modifier.height(20.dp)) |
|
|
|
|
|
|
|
// 评估指标 |
|
|
|
Column(verticalArrangement = Arrangement.spacedBy(12.dp)) { |
|
|
|
ScoreIndicator( |
|
|
|
label = "活动小时数得分", |
|
|
|
value = "佳", |
|
|
|
progress = 0.8f, |
|
|
|
color = Color(0xFFFF6B6B) |
|
|
|
) |
|
|
|
ScoreIndicator( |
|
|
|
label = "有氧活动评估", |
|
|
|
value = "佳", |
|
|
|
progress = 0.85f, |
|
|
|
color = Color(0xFF4ECDC4) |
|
|
|
) |
|
|
|
ScoreIndicator( |
|
|
|
label = "运动强度审评估", |
|
|
|
value = "优", |
|
|
|
progress = 0.95f, |
|
|
|
color = Color(0xFF45B7D1) |
|
|
|
) |
|
|
|
ScoreIndicator( |
|
|
|
label = "运动强度效率评估", |
|
|
|
value = "优", |
|
|
|
progress = 0.9f, |
|
|
|
color = Color(0xFF96CEB4) |
|
|
|
) |
|
|
|
// 圆形评分图表 |
|
|
|
Box( |
|
|
|
modifier = Modifier |
|
|
|
.fillMaxWidth() |
|
|
|
.height(140.dp), |
|
|
|
contentAlignment = Alignment.Center |
|
|
|
) { |
|
|
|
CircularScoreChart(score = 75) |
|
|
|
} |
|
|
|
|
|
|
|
Spacer(modifier = Modifier.height(20.dp)) |
|
|
|
|
|
|
|
// 评估指标 |
|
|
|
Column(verticalArrangement = Arrangement.spacedBy(12.dp)) { |
|
|
|
ScoreIndicator( |
|
|
|
label = "活动小时数得分", |
|
|
|
value = "佳", |
|
|
|
progress = 0.8f, |
|
|
|
color = Color(0xFFFF6B6B) |
|
|
|
) |
|
|
|
ScoreIndicator( |
|
|
|
label = "有氧活动评估", |
|
|
|
value = "佳", |
|
|
|
progress = 0.85f, |
|
|
|
color = Color(0xFF4ECDC4) |
|
|
|
) |
|
|
|
ScoreIndicator( |
|
|
|
label = "运动强度审评估", |
|
|
|
value = "优", |
|
|
|
progress = 0.95f, |
|
|
|
color = Color(0xFF45B7D1) |
|
|
|
) |
|
|
|
ScoreIndicator( |
|
|
|
label = "运动强度效率评估", |
|
|
|
value = "优", |
|
|
|
progress = 0.9f, |
|
|
|
color = Color(0xFF96CEB4) |
|
|
|
) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
@ -346,25 +384,28 @@ fun ComprehensiveScoreSection() { |
|
|
|
|
|
|
|
@Composable |
|
|
|
fun CircularScoreChart(score: Int) { |
|
|
|
Box(contentAlignment = Alignment.Center) { |
|
|
|
Canvas(modifier = Modifier.size(120.dp)) { |
|
|
|
Box(contentAlignment = Alignment.BottomCenter) { |
|
|
|
Canvas(modifier = Modifier.size(250.dp, 125.dp)) { |
|
|
|
val strokeWidth = 12.dp.toPx() |
|
|
|
val radius = (size.minDimension - strokeWidth) / 2 |
|
|
|
val center = Offset(size.width / 2, size.height / 2) |
|
|
|
val radius = (size.width - strokeWidth) / 2 |
|
|
|
val center = Offset(size.width / 2, size.height) |
|
|
|
|
|
|
|
// 背景圆 |
|
|
|
drawCircle( |
|
|
|
// 背景半圆弧 |
|
|
|
drawArc( |
|
|
|
color = Color(0xFFE0E0E0), |
|
|
|
radius = radius, |
|
|
|
center = center, |
|
|
|
style = Stroke(strokeWidth) |
|
|
|
startAngle = 180f, |
|
|
|
sweepAngle = 180f, |
|
|
|
useCenter = false, |
|
|
|
style = Stroke(strokeWidth), |
|
|
|
topLeft = Offset(center.x - radius, center.y - radius), |
|
|
|
size = Size(radius * 2, radius * 2) |
|
|
|
) |
|
|
|
|
|
|
|
// 进度圆弧 |
|
|
|
val sweepAngle = (score / 100f) * 360f |
|
|
|
// 进度半圆弧 |
|
|
|
val sweepAngle = (score / 100f) * 180f |
|
|
|
drawArc( |
|
|
|
color = Color(0xFF5E35B1), |
|
|
|
startAngle = -90f, |
|
|
|
startAngle = 180f, |
|
|
|
sweepAngle = sweepAngle, |
|
|
|
useCenter = false, |
|
|
|
style = Stroke(strokeWidth, cap = StrokeCap.Round), |
|
|
@ -373,7 +414,10 @@ fun CircularScoreChart(score: Int) { |
|
|
|
) |
|
|
|
} |
|
|
|
|
|
|
|
Column(horizontalAlignment = Alignment.CenterHorizontally) { |
|
|
|
Column( |
|
|
|
horizontalAlignment = Alignment.CenterHorizontally, |
|
|
|
modifier = Modifier.padding(bottom = 8.dp) |
|
|
|
) { |
|
|
|
Text( |
|
|
|
text = score.toString(), |
|
|
|
fontSize = 36.sp, |
|
|
@ -437,8 +481,8 @@ fun ScoreIndicator( |
|
|
|
fun InsightSection() { |
|
|
|
Card( |
|
|
|
modifier = Modifier.fillMaxWidth(), |
|
|
|
shape = RoundedCornerShape(16.dp), |
|
|
|
colors = CardDefaults.cardColors(Color(0xFF5E35B1)) |
|
|
|
shape = RoundedCornerShape(24.dp), |
|
|
|
colors = CardDefaults.cardColors(Color(0x80352764)) |
|
|
|
) { |
|
|
|
Column( |
|
|
|
modifier = Modifier.padding(16.dp) |
|
|
@ -460,4 +504,10 @@ fun InsightSection() { |
|
|
|
) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
@Preview |
|
|
|
@Composable |
|
|
|
fun ExerciseScreenPreview() { |
|
|
|
ExerciseScreen() |
|
|
|
} |