Merge pull request 'dev' (#1) from dev into main
Reviewed-on: https://gitea.rayc.top/rayc-root/WeightTrack/pulls/1main
commit
351d056d1c
|
|
@ -1,10 +1,12 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
|
<component name="GradleMigrationSettings" migrationVersion="1" />
|
||||||
<component name="GradleSettings">
|
<component name="GradleSettings">
|
||||||
<option name="linkedExternalProjectsSettings">
|
<option name="linkedExternalProjectsSettings">
|
||||||
<GradleProjectSettings>
|
<GradleProjectSettings>
|
||||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||||
<option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" />
|
<option name="gradleHome" value="/usr/local/Cellar/gradle/8.4/libexec" />
|
||||||
|
<option name="gradleJvm" value="17" />
|
||||||
<option name="modules">
|
<option name="modules">
|
||||||
<set>
|
<set>
|
||||||
<option value="$PROJECT_DIR$" />
|
<option value="$PROJECT_DIR$" />
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<profile version="1.0">
|
||||||
|
<option name="myName" value="Project Default" />
|
||||||
|
<inspection_tool class="PreviewAnnotationInFunctionWithParameters" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="PreviewApiLevelMustBeValid" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="PreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="PreviewFontScaleMustBeGreaterThanZero" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="PreviewMultipleParameterProviders" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="PreviewMustBeTopLevelFunction" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="PreviewNeedsComposableAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="PreviewNotSupportedInUnitTestFiles" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="PreviewPickerAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
</profile>
|
||||||
|
</component>
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="17" project-jdk-type="JavaSDK">
|
||||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectType">
|
<component name="ProjectType">
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
plugins {
|
plugins {
|
||||||
id("com.android.application")
|
id("com.android.application")
|
||||||
id("org.jetbrains.kotlin.android")
|
id("org.jetbrains.kotlin.android")
|
||||||
|
id("io.objectbox")
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
|
|
@ -67,5 +68,17 @@ dependencies {
|
||||||
debugImplementation("androidx.compose.ui:ui-tooling")
|
debugImplementation("androidx.compose.ui:ui-tooling")
|
||||||
debugImplementation("androidx.compose.ui:ui-test-manifest")
|
debugImplementation("androidx.compose.ui:ui-test-manifest")
|
||||||
|
|
||||||
|
// google fonts 感觉有些字体用不了
|
||||||
implementation("androidx.compose.ui:ui-text-google-fonts:1.6.1")
|
implementation("androidx.compose.ui:ui-text-google-fonts:1.6.1")
|
||||||
|
|
||||||
|
// implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.6.1")
|
||||||
|
// implementation("androidx.compose.runtime:runtime-livedata:1.7.0")
|
||||||
|
// implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1")
|
||||||
|
|
||||||
|
// view model
|
||||||
|
|
||||||
|
implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.6.1")
|
||||||
|
|
||||||
|
implementation("com.google.accompanist:accompanist-systemuicontroller:0.27.0")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -3,12 +3,12 @@
|
||||||
xmlns:tools="http://schemas.android.com/tools">
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
<application
|
<application
|
||||||
|
android:name=".WeightTrackApp"
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||||
android:fullBackupContent="@xml/backup_rules"
|
android:fullBackupContent="@xml/backup_rules"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@drawable/weight"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/Theme.WeightTrack"
|
android:theme="@style/Theme.WeightTrack"
|
||||||
tools:targetApi="31">
|
tools:targetApi="31">
|
||||||
|
|
|
||||||
|
|
@ -1,30 +1,51 @@
|
||||||
package com.eacenic.weighttrack
|
package com.eacenic.weighttrack
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.util.Log
|
||||||
|
import android.widget.Toast
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
|
import androidx.annotation.RequiresApi
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxHeight
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.layout.wrapContentHeight
|
import androidx.compose.foundation.layout.wrapContentHeight
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.Edit
|
||||||
|
import androidx.compose.material.icons.outlined.Edit
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
|
import androidx.compose.material3.ButtonDefaults
|
||||||
|
import androidx.compose.material3.DatePicker
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.IconButton
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.ModalBottomSheet
|
import androidx.compose.material3.ModalBottomSheet
|
||||||
|
import androidx.compose.material3.OutlinedTextField
|
||||||
import androidx.compose.material3.Surface
|
import androidx.compose.material3.Surface
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.rememberDatePickerState
|
||||||
import androidx.compose.material3.rememberModalBottomSheetState
|
import androidx.compose.material3.rememberModalBottomSheetState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
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.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
|
|
@ -32,18 +53,27 @@ import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.unit.TextUnit
|
import androidx.compose.ui.graphics.painter.Painter
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import com.eacenic.weighttrack.ui.theme.WeightTrackTheme
|
import com.eacenic.weighttrack.ui.theme.WeightTrackTheme
|
||||||
|
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import java.time.Instant
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
import java.time.ZoneId
|
||||||
|
import java.time.ZoneOffset
|
||||||
|
import java.time.format.DateTimeFormatter
|
||||||
|
|
||||||
class MainActivity : ComponentActivity() {
|
class MainActivity : ComponentActivity() {
|
||||||
|
@RequiresApi(Build.VERSION_CODES.O)
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
setContent {
|
setContent {
|
||||||
WeightTrackTheme {
|
WeightTrackTheme {
|
||||||
// A surface container using the 'background' color from the theme
|
|
||||||
Surface(
|
Surface(
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
color = MaterialTheme.colorScheme.background
|
color = MaterialTheme.colorScheme.background
|
||||||
|
|
@ -55,77 +85,276 @@ class MainActivity : ComponentActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@RequiresApi(Build.VERSION_CODES.O)
|
||||||
@Composable
|
private val dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
||||||
fun WeightTrackLayout() {
|
|
||||||
|
|
||||||
val scope = rememberCoroutineScope()
|
@SuppressLint("StateFlowValueCalledInComposition")
|
||||||
|
@RequiresApi(Build.VERSION_CODES.O)
|
||||||
|
@Composable
|
||||||
|
fun WeightTrackLayout(
|
||||||
|
weightTrackViewModel: WeightTrackViewModel = viewModel()
|
||||||
|
) {
|
||||||
|
|
||||||
|
val systemUIController = rememberSystemUiController()
|
||||||
|
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
systemUIController.setStatusBarColor(Color(0xFF424242))
|
||||||
|
}
|
||||||
|
|
||||||
|
val weightRecords by weightTrackViewModel.historyData.collectAsState()
|
||||||
|
val newestRecord by weightTrackViewModel.newestRecord.collectAsState()
|
||||||
|
|
||||||
var openBottomSheet by rememberSaveable { mutableStateOf(false) }
|
var openBottomSheet by rememberSaveable { mutableStateOf(false) }
|
||||||
var bottomSheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
|
var curRecord by rememberSaveable { mutableStateOf<WeightRecord?>(null) }
|
||||||
|
|
||||||
|
weightTrackViewModel.loadWeightRecords()
|
||||||
|
|
||||||
Surface(
|
Surface(
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
color = Color.Gray
|
color = Color.White
|
||||||
) {
|
) {
|
||||||
Column {
|
Column(
|
||||||
Row {
|
modifier = Modifier.padding(horizontal = 20.dp)
|
||||||
CurrentWeight()
|
) {
|
||||||
}
|
CurrentWeight(newestRecord, Modifier.padding(top = 20.dp))
|
||||||
Button(onClick = {
|
LazyColumn(
|
||||||
|
modifier = Modifier.fillMaxHeight(0.9f)
|
||||||
|
) {
|
||||||
|
this.items(weightRecords) { it ->
|
||||||
|
// Text(text = "${it.weight}")
|
||||||
|
WeightRecordItem(
|
||||||
|
weightRecord = it,
|
||||||
|
onClickEdit = { record ->
|
||||||
|
curRecord = record
|
||||||
openBottomSheet = true
|
openBottomSheet = true
|
||||||
}) {
|
|
||||||
Text(text = "添加")
|
|
||||||
}
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EditButton(onClick = { openBottomSheet = true })
|
||||||
}
|
}
|
||||||
|
|
||||||
if (openBottomSheet) {
|
if (openBottomSheet) {
|
||||||
ModalBottomSheet(
|
BottomSheetEditor(
|
||||||
onDismissRequest = { openBottomSheet = false },
|
onDismiss = {
|
||||||
sheetState = bottomSheetState
|
|
||||||
) {
|
|
||||||
Row {
|
|
||||||
Button(
|
|
||||||
onClick = {
|
|
||||||
scope.launch { bottomSheetState.hide() }.invokeOnCompletion {
|
|
||||||
if (!bottomSheetState.isVisible) {
|
|
||||||
openBottomSheet = false
|
openBottomSheet = false
|
||||||
|
curRecord = null
|
||||||
|
},
|
||||||
|
curRecord = curRecord
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
Text(text = "关闭")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.O)
|
||||||
|
@Composable
|
||||||
|
fun WeightRecordItem(
|
||||||
|
weightRecord: WeightRecord,
|
||||||
|
onClickEdit: (curRecord: WeightRecord) -> Unit
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.wrapContentHeight()
|
||||||
|
.padding(vertical = 5.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = "${weightRecord.weight}",
|
||||||
|
fontFamily = doodleShadowFontFamily,
|
||||||
|
fontSize = 36.sp,
|
||||||
|
modifier = Modifier.weight(1f)
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = dateTimeFormatter.format(weightRecord.date),
|
||||||
|
fontFamily = doodleShadowFontFamily,
|
||||||
|
fontSize = 18.sp,
|
||||||
|
modifier = Modifier.padding(end = 20.dp)
|
||||||
|
)
|
||||||
|
IconButton(
|
||||||
|
onClick = { onClickEdit(weightRecord) },
|
||||||
|
modifier = Modifier.padding(horizontal = 5.dp)
|
||||||
|
) {
|
||||||
|
Icon(Icons.Filled.Edit, contentDescription = "edit")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun CurrentWeight() {
|
fun CurrentWeight(
|
||||||
|
curRecord: WeightRecord? = null,
|
||||||
|
modifier: Modifier = Modifier
|
||||||
|
) {
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = modifier
|
||||||
.wrapContentHeight()
|
.wrapContentHeight()
|
||||||
.padding(20.dp)
|
|
||||||
.fillMaxWidth(),
|
.fillMaxWidth(),
|
||||||
horizontalArrangement = Arrangement.Center
|
horizontalArrangement = Arrangement.Center
|
||||||
) {
|
) {
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth(0.9f)
|
.fillMaxWidth()
|
||||||
.height(120.dp)
|
.height(120.dp)
|
||||||
.clip(RoundedCornerShape(10.dp))
|
.clip(RoundedCornerShape(10.dp))
|
||||||
.background(Color.Cyan)
|
.background(Color(0xFF424242))
|
||||||
|
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = "62.6",
|
text = "${curRecord?.weight ?: "No Data"}",
|
||||||
modifier = Modifier.align(Alignment.Center),
|
modifier = Modifier.align(Alignment.Center),
|
||||||
fontSize = 80.sp,
|
fontSize = 80.sp,
|
||||||
|
color = Color.White,
|
||||||
fontFamily = doodleShadowFontFamily
|
fontFamily = doodleShadowFontFamily
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun EditButton(
|
||||||
|
onClick: () -> Unit
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.Center,
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.wrapContentHeight()
|
||||||
|
) {
|
||||||
|
Button(
|
||||||
|
modifier = Modifier
|
||||||
|
.height(48.dp)
|
||||||
|
.fillMaxWidth(),
|
||||||
|
shape = RoundedCornerShape(5.dp),
|
||||||
|
colors = ButtonDefaults.buttonColors(Color(0xFF424242)),
|
||||||
|
onClick = onClick,
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = "ADD",
|
||||||
|
color = Color.White,
|
||||||
|
fontSize = 36.sp,
|
||||||
|
fontFamily = doodleShadowFontFamily
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.O)
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
fun BottomSheetEditor(
|
||||||
|
onDismiss: () -> Unit,
|
||||||
|
weightTrackViewModel: WeightTrackViewModel = viewModel(),
|
||||||
|
curRecord: WeightRecord? = null
|
||||||
|
) {
|
||||||
|
val context = LocalContext.current
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
|
|
||||||
|
val bottomSheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
|
||||||
|
|
||||||
|
var text by remember { mutableStateOf("${curRecord?.weight ?: ""}") }
|
||||||
|
var clickable = text.isNotEmpty()
|
||||||
|
|
||||||
|
|
||||||
|
val datePickerState = rememberDatePickerState(
|
||||||
|
initialSelectedDateMillis = curRecord?.date?.toInstant(ZoneOffset.UTC)?.toEpochMilli()
|
||||||
|
)
|
||||||
|
|
||||||
|
ModalBottomSheet(
|
||||||
|
onDismissRequest = onDismiss,
|
||||||
|
sheetState = bottomSheetState
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.wrapContentHeight()
|
||||||
|
.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = 20.dp)
|
||||||
|
) {
|
||||||
|
OutlinedTextField(
|
||||||
|
value = text,
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
onValueChange = {
|
||||||
|
val newVal = it.toFloatOrNull()
|
||||||
|
if ((it.isNotEmpty() && newVal == null) || (newVal != null && newVal > 1000f)) {
|
||||||
|
Toast.makeText(
|
||||||
|
context,
|
||||||
|
"只能输入数字,且要求数字小于1000",
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
).show()
|
||||||
|
return@OutlinedTextField
|
||||||
|
}
|
||||||
|
if (it.length > 6) {
|
||||||
|
Toast.makeText(
|
||||||
|
context,
|
||||||
|
"数字长度不能超过6位",
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
).show()
|
||||||
|
return@OutlinedTextField
|
||||||
|
}
|
||||||
|
text = it
|
||||||
|
clickable = text.isNotEmpty()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Spacer(
|
||||||
|
modifier = Modifier
|
||||||
|
.height(20.dp)
|
||||||
|
.fillMaxWidth()
|
||||||
|
)
|
||||||
|
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
DatePicker(
|
||||||
|
state = datePickerState,
|
||||||
|
modifier = Modifier.padding(16.dp),
|
||||||
|
title = { Text(text = "称重日期") },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Row(
|
||||||
|
horizontalArrangement = Arrangement.Center,
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
Button(
|
||||||
|
onClick = {
|
||||||
|
scope.launch { bottomSheetState.hide() }.invokeOnCompletion {
|
||||||
|
if (!bottomSheetState.isVisible) {
|
||||||
|
onDismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Text(text = "CANCEL", fontSize = 18.sp, fontFamily = doodleShadowFontFamily)
|
||||||
|
}
|
||||||
|
Spacer(modifier = Modifier.size(60.dp))
|
||||||
|
Button(
|
||||||
|
onClick = {
|
||||||
|
val newDate = LocalDateTime.ofInstant(
|
||||||
|
Instant.ofEpochMilli(datePickerState.selectedDateMillis!!),
|
||||||
|
ZoneId.systemDefault()
|
||||||
|
)
|
||||||
|
val newRecord = curRecord?.copy(weight = text.toFloat(), date = newDate)
|
||||||
|
?: WeightRecord(weight = text.toFloat(), date = newDate)
|
||||||
|
weightTrackViewModel.addWeightRecord(newRecord)
|
||||||
|
text = ""
|
||||||
|
onDismiss()
|
||||||
|
},
|
||||||
|
enabled = clickable
|
||||||
|
) {
|
||||||
|
Text(text = "CONFIRM", fontSize = 18.sp, fontFamily = doodleShadowFontFamily)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.size(40.dp))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
package com.eacenic.weighttrack
|
||||||
|
|
||||||
|
import android.os.Build
|
||||||
|
import androidx.annotation.RequiresApi
|
||||||
|
import io.objectbox.annotation.Convert
|
||||||
|
import io.objectbox.annotation.Entity
|
||||||
|
import io.objectbox.annotation.Id
|
||||||
|
import io.objectbox.converter.PropertyConverter
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
import java.time.format.DateTimeFormatter
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
data class WeightRecord @RequiresApi(Build.VERSION_CODES.O) constructor(
|
||||||
|
@Id
|
||||||
|
var id: Long = 0,
|
||||||
|
var weight: Float,
|
||||||
|
@Convert(converter = WeightRecordConverter::class, dbType = String::class)
|
||||||
|
var date: LocalDateTime? = null,
|
||||||
|
var isDeleted: Boolean = false
|
||||||
|
)
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.O)
|
||||||
|
val ymdHmsFormatter: DateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
|
||||||
|
|
||||||
|
class WeightRecordConverter: PropertyConverter<LocalDateTime?, String?> {
|
||||||
|
@RequiresApi(Build.VERSION_CODES.O)
|
||||||
|
override fun convertToEntityProperty(databaseValue: String?): LocalDateTime {
|
||||||
|
return LocalDateTime.parse(databaseValue, ymdHmsFormatter)
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.O)
|
||||||
|
override fun convertToDatabaseValue(entityProperty: LocalDateTime?): String {
|
||||||
|
return entityProperty?.format(ymdHmsFormatter) ?: ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
package com.eacenic.weighttrack
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
|
||||||
|
class WeightTrackApp: Application() {
|
||||||
|
|
||||||
|
override fun onCreate() {
|
||||||
|
super.onCreate()
|
||||||
|
WeightTrackObjectBox.init(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
package com.eacenic.weighttrack
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import io.objectbox.BoxStore
|
||||||
|
|
||||||
|
object WeightTrackObjectBox {
|
||||||
|
|
||||||
|
lateinit var store: BoxStore
|
||||||
|
private set
|
||||||
|
|
||||||
|
fun init(context: Context) {
|
||||||
|
store = MyObjectBox.builder()
|
||||||
|
.androidContext(context.applicationContext)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
package com.eacenic.weighttrack
|
||||||
|
|
||||||
|
import android.os.Build
|
||||||
|
import androidx.annotation.RequiresApi
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import io.objectbox.kotlin.boxFor
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
|
const val TAG = "WeightTrackViewModel"
|
||||||
|
class WeightTrackViewModel: ViewModel() {
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.O)
|
||||||
|
private var _historyData = MutableStateFlow<List<WeightRecord>>(mutableListOf<WeightRecord>())
|
||||||
|
@RequiresApi(Build.VERSION_CODES.O)
|
||||||
|
val historyData: StateFlow<List<WeightRecord>> = _historyData
|
||||||
|
|
||||||
|
private var _curRecord = MutableStateFlow<WeightRecord?>(null)
|
||||||
|
val newestRecord = _curRecord
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.O)
|
||||||
|
fun addWeightRecord(newRecord: WeightRecord) {
|
||||||
|
saveWeightRecord(newRecord)
|
||||||
|
loadWeightRecords()
|
||||||
|
}
|
||||||
|
|
||||||
|
private val weightBox = WeightTrackObjectBox.store.boxFor(WeightRecord::class)
|
||||||
|
|
||||||
|
private fun saveWeightRecord(weightRecord: WeightRecord) {
|
||||||
|
weightBox.put(weightRecord)
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.O)
|
||||||
|
fun loadWeightRecords() {
|
||||||
|
val storedData = weightBox.all
|
||||||
|
storedData.sortByDescending { it.date }
|
||||||
|
_historyData.value = storedData
|
||||||
|
if (storedData.isNotEmpty()) {
|
||||||
|
_curRecord.value = storedData[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="297dp"
|
||||||
|
android:height="210dp"
|
||||||
|
android:viewportWidth="297"
|
||||||
|
android:viewportHeight="210">
|
||||||
|
<path
|
||||||
|
android:pathData="M21.15,51.45L21.15,51.45A24.41,14.3 90,0 1,35.44 75.85L35.44,133.96A24.41,14.3 90,0 1,21.15 158.37L21.15,158.37A24.41,14.3 90,0 1,6.85 133.96L6.85,75.85A24.41,14.3 90,0 1,21.15 51.45z"
|
||||||
|
android:strokeWidth="0.59"
|
||||||
|
android:fillColor="#000000"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M115.64,88.33l63.41,0l0,33.15l-63.41,0z"
|
||||||
|
android:strokeWidth="0.49"
|
||||||
|
android:fillColor="#000000"/>
|
||||||
|
<path
|
||||||
|
android:pathData="m76.68,15.39c-22.04,0 -39.78,18.23 -39.78,40.87l0,97.3c0,22.64 17.74,40.87 39.78,40.87 22.04,0 39.78,-18.23 39.78,-40.87L116.46,56.26c0,-22.64 -17.74,-40.87 -39.78,-40.87zM76.68,52.69c7.35,0 13.26,10.63 13.26,23.84l0,56.76c0,13.21 -5.92,23.84 -13.26,23.84 -7.35,0 -13.26,-10.63 -13.26,-23.84L63.42,76.53c0,-13.21 5.91,-23.84 13.26,-23.84z"
|
||||||
|
android:strokeWidth="1.28"
|
||||||
|
android:fillColor="#666666"/>
|
||||||
|
<path
|
||||||
|
android:pathData="m218.41,15.39c-22.04,0 -39.78,18.23 -39.78,40.87l0,97.3c0,22.64 17.74,40.87 39.78,40.87 22.04,0 39.78,-18.23 39.78,-40.87L258.2,56.26c0,-22.64 -17.74,-40.87 -39.78,-40.87zM218.41,52.69c7.35,0 13.26,10.63 13.26,23.84l0,56.76c0,13.21 -5.92,23.84 -13.26,23.84 -7.35,0 -13.26,-10.63 -13.26,-23.84L205.15,76.53c0,-13.21 5.91,-23.84 13.26,-23.84z"
|
||||||
|
android:strokeWidth="1.28"
|
||||||
|
android:fillColor="#666666"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M274.77,51.45L274.77,51.45A24.41,14.3 90,0 1,289.07 75.85L289.07,133.96A24.41,14.3 90,0 1,274.77 158.37L274.77,158.37A24.41,14.3 90,0 1,260.48 133.96L260.48,75.85A24.41,14.3 90,0 1,274.77 51.45z"
|
||||||
|
android:strokeWidth="0.59"
|
||||||
|
android:fillColor="#000000"/>
|
||||||
|
</vector>
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 115 KiB |
|
|
@ -1,4 +1,13 @@
|
||||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||||
|
buildscript {
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
dependencies {
|
||||||
|
classpath("io.objectbox:objectbox-gradle-plugin:3.8.0")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id("com.android.application") version "8.2.2" apply false
|
id("com.android.application") version "8.2.2" apply false
|
||||||
id("org.jetbrains.kotlin.android") version "1.9.0" apply false
|
id("org.jetbrains.kotlin.android") version "1.9.0" apply false
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue