diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 07ea30b..ce8df0f 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,10 +1,12 @@ + = mutableListOf( - StakeInfo("awdawd","awdawd","awdawda",1231.0,1231.0,0.0,"awdawd","awdawd","awdawd","否","awdawd","awdawda","awda"), - StakeInfo("awdawd","awdawd","awdawda",1231.0,1231.0,0.0,"awdawd","awdawd","awdawd","否","awdawd","awdawda","awda"), - StakeInfo("awdawd","awdawd","awdawda",1231.0,1231.0,0.0,"awdawd","awdawd","awdawd","否","awdawd","awdawda","awda"), - StakeInfo("awdawd","awdawd","awdawda",1231.0,1231.0,0.0,"awdawd","awdawd","awdawd","否","awdawd","awdawda","awda"), - StakeInfo("awdawd","awdawd","awdawda",1231.0,1231.0,0.0,"awdawd","awdawd","awdawd","否","awdawd","awdawda","awda"),) - var isFirst=true - @RequiresApi(Build.VERSION_CODES.R) - @Composable - fun MyApp(){ - val navController = rememberNavController() -// val viewModel:SimpleViewModel> by lazy { SimpleViewModel() } - val viewModel:SimpleViewModel> = viewModel() -// viewModel.setData(outerItems) -// viewModel.setData(mutableListOf()) - NavHost(navController = navController, startDestination = "home") { - composable("home") { - HomePager(navController,viewModel) - } - composable("info") { - StakeInfoViewPager().MainView(navController,viewModel) - } - composable("info/{data}") { backStackEntry -> - val json = backStackEntry.arguments?.getString("data") - val stakeInfo=Gson().fromJson(json,StakeInfo::class.java) - StakeInfoViewPager().MainView(navController,viewModel=viewModel,stakeInfo) - } - composable("files") { - FilesScreen(navController,viewModel) - } - } - } - @RequiresApi(Build.VERSION_CODES.R) - @Composable - fun HomePager(navController: NavController,viewModel: SimpleViewModel>){ - Test1229Theme() { - var isLoading by remember { mutableStateOf(true) } - val context = LocalContext.current - val lifecycleOwner = LocalLifecycleOwner.current - val (isPermissionGranted, onPermissionChange) = remember { mutableStateOf(Environment.isExternalStorageManager()) } - val permissionDialog = remember { mutableStateOf(false) } - DisposableEffect(Unit) { - val observer = object : LifecycleEventObserver { - @RequiresApi(Build.VERSION_CODES.R) - override fun onStateChanged( - source: LifecycleOwner, - event: Lifecycle.Event - ) { - Log.d("MainActivity", "onStateChanged: $event") - when (event) { - Lifecycle.Event.ON_RESUME -> { - onPermissionChange(Environment.isExternalStorageManager()) - } - - else -> Unit - } - } - } - lifecycleOwner.lifecycle.addObserver(observer) - onDispose { - lifecycleOwner.lifecycle.removeObserver(observer) + Test1229Theme { + Surface(modifier = Modifier.fillMaxSize()) { + SharedNavHost() } } - LaunchedEffect(isPermissionGranted) { - if (isPermissionGranted&&!isFirst) { - context.toast("权限已授权") - } - } - if(isPermissionGranted){ - permissionDialog.value=false - LaunchedEffect(Unit) { - val dataTmp=viewModel.stateFlow.value.data - if (dataTmp==null){ - Log.d("MainActivity","LaunchedEffect $isLoading $dataTmp") - val directory=Constant.SDCARD+"/"+Constant.projectName+"/backup" - val data = ExcelUtils().parseExcel("$directory/main.xls") - viewModel.setData(data) - } - isLoading = false - } - if (isLoading){ - Column( - modifier = Modifier.fillMaxSize(), - horizontalAlignment = Alignment.CenterHorizontally, // 水平居中 - verticalArrangement = Arrangement.Center // 垂直居中 - ){ - CircularProgressIndicator( - modifier = Modifier.width(64.dp), - color = MaterialTheme.colorScheme.secondary, - trackColor = MaterialTheme.colorScheme.surfaceVariant, - ) - Spacer(Modifier.fillMaxWidth().height(26.dp)) - Text( - text = "数据加载中....", - ) - } - }else{ - Scaffold( - modifier = Modifier.fillMaxSize(), - topBar = { - if (context is MainActivity) { - // 当前是 MainActivity,可以访问 MainActivity 的成员 - val activity: MainActivity = context - // 做你需要的操作 - TopBar(activity) - } - - }, - floatingActionButton = { - FloatingButtonS(navController,viewModel) - } - ) { innerPadding -> - mainPager(navController,viewModel,Modifier.padding(innerPadding)) - } - } - - - }else{ - permissionDialog.value=true - } + val state by viewModel.state.collectAsState() PermissionDialog( - visible = permissionDialog.value, - onConfirm = { - val intent = Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION) - isFirst=false - context.startActivity(intent) - }, - onDismissRequest = { - context.toast("权限取消授权") - finish() - } + visible = state.permissionDialogVisible, + onPermissionChange = viewModel::onPermissionChange ) } } diff --git a/app/src/main/java/com/zywl/test1229/MainViewModel.kt b/app/src/main/java/com/zywl/test1229/MainViewModel.kt new file mode 100644 index 0000000..e47d983 --- /dev/null +++ b/app/src/main/java/com/zywl/test1229/MainViewModel.kt @@ -0,0 +1,42 @@ +package com.zywl.test1229 + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.zywl.test1229.data.PermissionState +import com.zywl.test1229.di.PermissionStateFlow +import com.zywl.test1229.domain.CheckPermissionUseCase +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.update +import javax.inject.Inject + +@HiltViewModel +class MainViewModel @Inject constructor( + @PermissionStateFlow + private val permissionState: MutableStateFlow, + private val checkPermissionUseCase: CheckPermissionUseCase +) : ViewModel() { + private val _state = MutableStateFlow(MainViewState()) + val state = _state.asStateFlow() + + init { + permissionState.onEach { + when (it) { + PermissionState.None -> checkPermissionUseCase() + PermissionState.Granted -> _state.update { it.copy(permissionDialogVisible = false) } + PermissionState.Denied -> _state.update { it.copy(permissionDialogVisible = true) } + } + }.launchIn(viewModelScope) + } + + fun onPermissionChange(value: Boolean) { + checkPermissionUseCase() + } +} + +data class MainViewState( + val permissionDialogVisible: Boolean = false +) \ No newline at end of file diff --git a/app/src/main/java/com/zywl/test1229/bean/Constant.kt b/app/src/main/java/com/zywl/test1229/bean/Constant.kt index ca01892..81c7c79 100644 --- a/app/src/main/java/com/zywl/test1229/bean/Constant.kt +++ b/app/src/main/java/com/zywl/test1229/bean/Constant.kt @@ -2,13 +2,11 @@ package com.zywl.test1229.bean import android.os.Environment -class Constant { - companion object{ - val stakeTypeArray = listOf("里程桩","测试桩","转角桩","警示桩") - val depthArray = listOf("超长无法探测") - val BuriedTechnologyArray = listOf("定向钻穿河","定向钻穿河路","直管平铺","平铺弯头") - val terrainArray = listOf("铺装路面","绿化带","山坡","荒地") - var SDCARD: String = Environment.getExternalStorageDirectory().absolutePath - var projectName="Test1229" - } +object Constant { + val stakeTypeArray = listOf("里程桩","测试桩","转角桩","警示桩") + val depthArray = listOf("超长无法探测") + val BuriedTechnologyArray = listOf("定向钻穿河","定向钻穿河路","直管平铺","平铺弯头") + val terrainArray = listOf("铺装路面","绿化带","山坡","荒地") + var SDCARD: String = Environment.getExternalStorageDirectory().absolutePath + var projectName="Test1229" } \ No newline at end of file diff --git a/app/src/main/java/com/zywl/test1229/bean/StakeInfo.kt b/app/src/main/java/com/zywl/test1229/bean/StakeInfo.kt index 4cd9c43..1366f21 100644 --- a/app/src/main/java/com/zywl/test1229/bean/StakeInfo.kt +++ b/app/src/main/java/com/zywl/test1229/bean/StakeInfo.kt @@ -1,21 +1,41 @@ package com.zywl.test1229.bean import android.os.Parcelable -import kotlinx.android.parcel.Parcelize +import kotlinx.parcelize.Parcelize +import kotlinx.serialization.Serializable +@Serializable @Parcelize data class StakeInfo( - var tempNo:String, - var pipeLine:String, - var stakeType:String, - var latitude:Double, - var longitude:Double, - var z:Double, - var pipeDepth:String, - var mileage:String, - var buryTech:String, - var isInPoint:String, - var terrain:String, - var imageName:String, - var collectDate:String, -):Parcelable + val tempNo: String, + val pipeLine: String, + val stakeType: String, + val latitude: Double, + val longitude: Double, + val z: Double, + val pipeDepth: String, + val mileage: String, + val buryTech: String, + val isInPoint: String, + val terrain: String, + val imageName: String, + val collectDate: String, +) : Parcelable { + companion object { + val Empty = StakeInfo( + "", + "", + "", + 0.0, + 0.0, + 0.0, + "", + "", + "", + "", + "", + "", + "" + ) + } +} diff --git a/app/src/main/java/com/zywl/test1229/data/PermissionState.kt b/app/src/main/java/com/zywl/test1229/data/PermissionState.kt new file mode 100644 index 0000000..38b5214 --- /dev/null +++ b/app/src/main/java/com/zywl/test1229/data/PermissionState.kt @@ -0,0 +1,5 @@ +package com.zywl.test1229.data + +enum class PermissionState { + None, Granted, Denied +} \ No newline at end of file diff --git a/app/src/main/java/com/zywl/test1229/di/SharedModule.kt b/app/src/main/java/com/zywl/test1229/di/SharedModule.kt new file mode 100644 index 0000000..a97ed1c --- /dev/null +++ b/app/src/main/java/com/zywl/test1229/di/SharedModule.kt @@ -0,0 +1,25 @@ +package com.zywl.test1229.di + +import com.zywl.test1229.data.PermissionState +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import kotlinx.coroutines.flow.MutableStateFlow +import javax.inject.Qualifier +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +object SharedModule { + @Provides + @PermissionStateFlow + @Singleton + fun providePermissionState(): MutableStateFlow { + return MutableStateFlow(PermissionState.None) + } +} + +@Qualifier +@Retention(AnnotationRetention.BINARY) +annotation class PermissionStateFlow \ No newline at end of file diff --git a/app/src/main/java/com/zywl/test1229/domain/CheckPermissionUseCase.kt b/app/src/main/java/com/zywl/test1229/domain/CheckPermissionUseCase.kt new file mode 100644 index 0000000..c1e7f6a --- /dev/null +++ b/app/src/main/java/com/zywl/test1229/domain/CheckPermissionUseCase.kt @@ -0,0 +1,23 @@ +package com.zywl.test1229.domain + +import android.os.Build +import android.os.Environment +import com.zywl.test1229.data.PermissionState +import com.zywl.test1229.di.PermissionStateFlow +import kotlinx.coroutines.flow.MutableStateFlow +import javax.inject.Inject + +class CheckPermissionUseCase @Inject constructor( + @PermissionStateFlow + private val permissionState: MutableStateFlow +) { + operator fun invoke(): Boolean { + val granted = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + Environment.isExternalStorageManager() + } else { + TODO() + } + permissionState.value = if (granted) PermissionState.Granted else PermissionState.Denied + return granted + } +} \ No newline at end of file diff --git a/app/src/main/java/com/zywl/test1229/ktx/Any.kt b/app/src/main/java/com/zywl/test1229/ktx/Any.kt new file mode 100644 index 0000000..c8def8a --- /dev/null +++ b/app/src/main/java/com/zywl/test1229/ktx/Any.kt @@ -0,0 +1,3 @@ +package com.zywl.test1229.ktx + +val Any.TAG: String get() = this::class.java.simpleName \ No newline at end of file diff --git a/app/src/main/java/com/zywl/test1229/ui/SharedNavHost.kt b/app/src/main/java/com/zywl/test1229/ui/SharedNavHost.kt new file mode 100644 index 0000000..e7a4d69 --- /dev/null +++ b/app/src/main/java/com/zywl/test1229/ui/SharedNavHost.kt @@ -0,0 +1,24 @@ +package com.zywl.test1229.ui + +import androidx.compose.runtime.Composable +import androidx.navigation.compose.NavHost +import androidx.navigation.compose.rememberNavController +import com.zywl.test1229.ui.files.files +import com.zywl.test1229.ui.home.HomeRoute +import com.zywl.test1229.ui.home.home +import com.zywl.test1229.ui.info.info + +@Composable +fun SharedNavHost( + startDestination: Any = HomeRoute +) { + val navController = rememberNavController() + NavHost( + navController = navController, + startDestination = startDestination + ) { + home(navController) + info(navController) + files(navController) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/zywl/test1229/ui/common/DropdownMenu.kt b/app/src/main/java/com/zywl/test1229/ui/common/DropdownMenu.kt new file mode 100644 index 0000000..4b4ed3d --- /dev/null +++ b/app/src/main/java/com/zywl/test1229/ui/common/DropdownMenu.kt @@ -0,0 +1,129 @@ +package com.zywl.test1229.ui.common + +import androidx.compose.material3.DropdownMenuItem +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.ExposedDropdownMenuBox +import androidx.compose.material3.ExposedDropdownMenuDefaults +import androidx.compose.material3.Text +import androidx.compose.material3.TextField +import androidx.compose.material3.TextFieldDefaults +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun EditableExposedDropdownMenuSample( + options: List, + label: String, + content: String, + onPipeLineChanged: (String) -> Unit, modifier: Modifier = Modifier +) { + var expanded by remember { mutableStateOf(false) } + ExposedDropdownMenuBox( + //是否列出列表 + expanded = expanded, + onExpandedChange = { + expanded = !expanded + }, + ) { + TextField( + value = content, + onValueChange = { + onPipeLineChanged(it) + }, + label = { Text(label) }, + trailingIcon = { + ExposedDropdownMenuDefaults.TrailingIcon( + expanded = expanded + ) + }, + colors = TextFieldDefaults.colors( + focusedContainerColor = Color.Transparent, + unfocusedContainerColor = Color.Transparent + ), + modifier = modifier.menuAnchor() + ) + val filteringOptions = + options.filter { it.contains(content, ignoreCase = true) } + if (filteringOptions.isNotEmpty()) { + ExposedDropdownMenu( + expanded = expanded, + onDismissRequest = { + expanded = false + } + ) { + filteringOptions.forEach { selectionOption -> + DropdownMenuItem( + text = { + Text(text = selectionOption) + }, + onClick = { + onPipeLineChanged(selectionOption) + expanded = false + } + ) + } + } + } + } +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun ExposedDropdownMenuSample( + label: String, + content: String, + onPipeLineChanged: (String) -> Unit, modifier: Modifier = Modifier +) { + val options = listOf("否", "是") + var expanded by remember { mutableStateOf(false) } + ExposedDropdownMenuBox( + expanded = expanded, + onExpandedChange = { + expanded = !expanded + } + ) { + TextField( + readOnly = true, + value = content, + onValueChange = { }, + label = { Text(label) }, + trailingIcon = { + ExposedDropdownMenuDefaults.TrailingIcon( + //这里使用了 ExposedDropdownMenuDefaults.TrailingIcon 组件作为右侧图标。 + //这个图标会根据 expanded 的状态变化,通常用于表示下拉菜单的打开或关闭状态 + expanded = expanded + ) + }, + colors = TextFieldDefaults.colors( + focusedContainerColor = Color.Transparent, + unfocusedContainerColor = Color.Transparent + ), + modifier = modifier.menuAnchor() + ) + + ExposedDropdownMenu( + expanded = expanded, + onDismissRequest = { + expanded = false + }, + ) { + options.forEach { selectionOption -> + DropdownMenuItem( + text = { + Text(selectionOption) + }, + onClick = { + onPipeLineChanged(selectionOption) + expanded = false + } + ) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/zywl/test1229/ui/common/EditableTime.kt b/app/src/main/java/com/zywl/test1229/ui/common/EditableTime.kt new file mode 100644 index 0000000..0536cf4 --- /dev/null +++ b/app/src/main/java/com/zywl/test1229/ui/common/EditableTime.kt @@ -0,0 +1,79 @@ +package com.zywl.test1229.ui.common + +import android.util.Log +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.material3.DatePicker +import androidx.compose.material3.DatePickerDialog +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.material3.rememberDatePickerState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.sp +import java.text.SimpleDateFormat +import java.util.Date +import java.util.Locale + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun EditableTime( + label: String, + date: String, + onPipeLineChanged: (String) -> Unit, modifier: Modifier = Modifier +) { + var content = date + if (content.isEmpty()) { + content = "**-**-**" + } + + Row(modifier = modifier, horizontalArrangement = Arrangement.SpaceBetween) { + var showTimePicker by remember { mutableStateOf(false) } + Text(text = label) + Text(text = content, + style = TextStyle( + fontSize = 14.sp, + fontWeight = FontWeight.Bold + ), + modifier = Modifier.clickable { + showTimePicker = true + } + ) + val datePickerState = rememberDatePickerState() + if (showTimePicker) { + DatePickerDialog( + onDismissRequest = { showTimePicker = false; }, + confirmButton = { + TextButton(onClick = { + val formattedDate = datePickerState.selectedDateMillis?.let { + val date = Date(it) + val format = + SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()) // 设置日期格式 + format.format(date) + } ?: content + onPipeLineChanged(formattedDate) + Log.d("DatePickerDialog", formattedDate) + showTimePicker = false; + }) { + Text("OK") + } + }, + dismissButton = { + TextButton(onClick = { showTimePicker = false; }) { + Text("Cancel") + } + } + ) { + DatePicker(state = datePickerState) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/zywl/test1229/ui/common/FloatingActionButton.kt b/app/src/main/java/com/zywl/test1229/ui/common/FloatingActionButton.kt new file mode 100644 index 0000000..7461ae0 --- /dev/null +++ b/app/src/main/java/com/zywl/test1229/ui/common/FloatingActionButton.kt @@ -0,0 +1,83 @@ +package com.zywl.test1229.ui.common + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.fadeIn +import androidx.compose.animation.slideInVertically +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material3.FloatingActionButton +import androidx.compose.material3.FloatingActionButtonDefaults +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp + +@Composable +fun FloatingActionButton( + onImport: () -> Unit, + onExport: () -> Unit, + onCreate: () -> Unit, + modifier: Modifier = Modifier +) { + var isShow by remember { mutableStateOf(false) } + + Column( + modifier = modifier.padding(end = 16.dp, bottom = 8.dp) + ) { + AnimatedVisibility( + visible = isShow, + enter = fadeIn() + slideInVertically(initialOffsetY = { it }) // Animation for appearing + ) { + Column( + verticalArrangement = Arrangement.spacedBy(8.dp), + modifier = Modifier.padding(bottom = 8.dp) + ) { + FloatingActionButton( + elevation = FloatingActionButtonDefaults.elevation(0.dp), + shape = CircleShape, + onClick = onImport, + modifier = Modifier.size(66.dp), + ) { + Text("导入", color = Color.White) + } + FloatingActionButton( + elevation = FloatingActionButtonDefaults.elevation(0.dp), + shape = CircleShape, + onClick = onExport, + modifier = Modifier.size(66.dp), + ) { + Text("导出", color = Color.White) + } + FloatingActionButton( + elevation = FloatingActionButtonDefaults.elevation(0.dp), + shape = CircleShape, + onClick = onCreate, + modifier = Modifier.size(66.dp), + ) { + Text("新增", color = Color.White) + } + } + } + FloatingActionButton( + elevation = FloatingActionButtonDefaults.elevation(0.dp), + shape = CircleShape, + onClick = { + isShow = !isShow + }, + modifier = Modifier.size(66.dp), + ) { + var str = if (isShow) "-" else "+" + Text(str, color = Color.White, style = TextStyle(fontSize = 26.sp)) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/zywl/test1229/ui/common/PermissionDialog.kt b/app/src/main/java/com/zywl/test1229/ui/common/PermissionDialog.kt new file mode 100644 index 0000000..357f5f0 --- /dev/null +++ b/app/src/main/java/com/zywl/test1229/ui/common/PermissionDialog.kt @@ -0,0 +1,92 @@ +package com.zywl.test1229.ui.common + +import android.app.Activity +import android.content.Intent +import android.os.Build +import android.os.Environment +import android.provider.Settings +import android.util.Log +import androidx.annotation.RequiresApi +import androidx.compose.material3.AlertDialog +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect +import androidx.compose.ui.platform.LocalContext +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleEventObserver +import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.compose.LocalLifecycleOwner +import com.zywl.test1229.ktx.toast + +@Composable +fun PermissionDialog( + visible: Boolean, + onPermissionChange: (Boolean) -> Unit +) { + val context = LocalContext.current + val lifecycleOwner = LocalLifecycleOwner.current + DisposableEffect(Unit) { + val observer = object : LifecycleEventObserver { + @RequiresApi(Build.VERSION_CODES.R) + override fun onStateChanged( + source: LifecycleOwner, + event: Lifecycle.Event + ) { + Log.d("MainActivity", "onStateChanged: $event") + when (event) { + Lifecycle.Event.ON_RESUME -> { + onPermissionChange(Environment.isExternalStorageManager()) + } + + else -> Unit + } + } + } + lifecycleOwner.lifecycle.addObserver(observer) + onDispose { + lifecycleOwner.lifecycle.removeObserver(observer) + } + } + PermissionDialog( + visible = visible, + onConfirm = { + val intent = Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION) + context.startActivity(intent) + }, + onDismissRequest = { + context.toast("权限取消授权") + (context as? Activity)?.finish() + } + ) +} + +@Composable +fun PermissionDialog( + visible: Boolean, + onConfirm: () -> Unit, + onDismissRequest: () -> Unit, +) { + if (visible) AlertDialog( + title = { + Text(text = "管理所有文件") + }, text = { + Text(text = "授予app管理存储设备上的所有文件的权限") + }, + confirmButton = { + TextButton( + onClick = { + onConfirm() + } + ) { + Text(text = "确定") + } + }, dismissButton = { + TextButton( + onClick = onDismissRequest + ) { + Text(text = "取消") + } + }, onDismissRequest = onDismissRequest + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/zywl/test1229/ui/common/PreviewSurface.kt b/app/src/main/java/com/zywl/test1229/ui/common/PreviewSurface.kt new file mode 100644 index 0000000..0d8735e --- /dev/null +++ b/app/src/main/java/com/zywl/test1229/ui/common/PreviewSurface.kt @@ -0,0 +1,19 @@ +package com.zywl.test1229.ui.common + +import androidx.compose.material3.Surface +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import com.zywl.test1229.ui.theme.Test1229Theme + +@Composable +fun PreviewSurface( + modifier: Modifier = Modifier, + content: @Composable () -> Unit +) { + Test1229Theme { + Surface( + modifier = modifier, + content = content + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/zywl/test1229/ui/common/Previews.kt b/app/src/main/java/com/zywl/test1229/ui/common/Previews.kt new file mode 100644 index 0000000..cc543a7 --- /dev/null +++ b/app/src/main/java/com/zywl/test1229/ui/common/Previews.kt @@ -0,0 +1,9 @@ +package com.zywl.test1229.ui.common + +import android.content.res.Configuration.UI_MODE_NIGHT_NO +import android.content.res.Configuration.UI_MODE_NIGHT_YES +import androidx.compose.ui.tooling.preview.Preview + +@Preview(uiMode = UI_MODE_NIGHT_YES) +@Preview(uiMode = UI_MODE_NIGHT_NO) +annotation class Previews diff --git a/app/src/main/java/com/zywl/test1229/ui/common/ProgressDialog.kt b/app/src/main/java/com/zywl/test1229/ui/common/ProgressDialog.kt new file mode 100644 index 0000000..c21d2b7 --- /dev/null +++ b/app/src/main/java/com/zywl/test1229/ui/common/ProgressDialog.kt @@ -0,0 +1,34 @@ +package com.zywl.test1229.ui.common + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.width +import androidx.compose.material3.AlertDialog +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp + +@Composable +fun ProgressDialog(visible: Boolean) { + if (visible) AlertDialog( + onDismissRequest = { }, + title = { + Text(text = "正在处理...") + }, + text = { + Column(horizontalAlignment = Alignment.CenterHorizontally) { + CircularProgressIndicator( + modifier = Modifier.width(64.dp), + color = MaterialTheme.colorScheme.secondary, + trackColor = MaterialTheme.colorScheme.surfaceVariant, + ) + } + }, + confirmButton = { + + } + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/zywl/test1229/ui/common/StakeInfoView.kt b/app/src/main/java/com/zywl/test1229/ui/common/StakeInfoView.kt new file mode 100644 index 0000000..61ddc50 --- /dev/null +++ b/app/src/main/java/com/zywl/test1229/ui/common/StakeInfoView.kt @@ -0,0 +1,144 @@ +package com.zywl.test1229.ui.common + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Button +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.zywl.test1229.bean.Constant +import com.zywl.test1229.bean.StakeInfo + +@Composable +fun StakeInfoView( + isShow: Boolean, + stakeInfo: StakeInfo, + onPipeLineChanged: (StakeInfo) -> Unit, + onDeleteItem: () -> Unit +) { + if (!isShow) { + return + } + Surface( + shape = RoundedCornerShape(8.dp), + shadowElevation = 10.dp, + ) { + Column { + EditableTextField( + "管线", + stakeInfo.pipeLine, + modifier = Modifier.fillMaxWidth(), + onPipeLineChanged = { + onPipeLineChanged(stakeInfo.copy(pipeLine = it)) + } + ) + EditableExposedDropdownMenuSample( + Constant.stakeTypeArray, + "桩类型", + stakeInfo.stakeType, + modifier = Modifier.fillMaxWidth(), + onPipeLineChanged = { + onPipeLineChanged(stakeInfo.copy(stakeType = it)) + } + ) + EditableExposedDropdownMenuSample( + Constant.depthArray, + "管中埋深(米)", + stakeInfo.pipeDepth, + modifier = Modifier.fillMaxWidth(), + onPipeLineChanged = { + onPipeLineChanged(stakeInfo.copy(pipeDepth = it)) + } + ) + EditableTextField( + "里程", + stakeInfo.mileage, + modifier = Modifier.fillMaxWidth(), + onPipeLineChanged = { + onPipeLineChanged(stakeInfo.copy(mileage = it)) + } + ) + EditableExposedDropdownMenuSample( + Constant.BuriedTechnologyArray, + "埋设工艺", + stakeInfo.buryTech, + modifier = Modifier.fillMaxWidth(), + onPipeLineChanged = { + onPipeLineChanged(stakeInfo.copy(buryTech = it)) + } + ) + ExposedDropdownMenuSample( + "是否出入土点", + stakeInfo.isInPoint, + modifier = Modifier.fillMaxWidth(), + onPipeLineChanged = { + onPipeLineChanged(stakeInfo.copy(isInPoint = it)) + } + ) + EditableExposedDropdownMenuSample( + Constant.terrainArray, + "地形", + stakeInfo.terrain, + modifier = Modifier.fillMaxWidth(), + onPipeLineChanged = { + onPipeLineChanged(stakeInfo.copy(terrain = it)) + } + ) + Row { + EditableTextFieldInt("经度", "${stakeInfo.latitude}", modifier = Modifier + .fillMaxWidth() + .weight(1f), onPipeLineChanged = { + onPipeLineChanged(stakeInfo.copy(latitude = it)) + }) + EditableTextFieldInt( + "维度", + "${stakeInfo.longitude}", + onPipeLineChanged = { + onPipeLineChanged(stakeInfo.copy(longitude = it)) + }, + modifier = Modifier + .fillMaxWidth() + .weight(1f), + ) + } + EditableTextFieldInt( + "Z(85高程,桩根部)", + "${stakeInfo.z}", + onPipeLineChanged = { + onPipeLineChanged(stakeInfo.copy(z = it)) + }, modifier = Modifier + .fillMaxWidth() + + ) + EditableTime( + "采集时间", + stakeInfo.collectDate, + modifier = Modifier + .fillMaxWidth() + .padding(start = 6.dp, end = 6.dp, top = 18.dp, bottom = 12.dp), + onPipeLineChanged = { + onPipeLineChanged(stakeInfo.copy(collectDate = it)) + } + ) + Row( + modifier = Modifier.fillMaxSize(), + horizontalArrangement = Arrangement.Center + ) { + Button( + onClick = { + onDeleteItem() + } + ) { + Text(text = "删除") + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/zywl/test1229/ui/common/TextField.kt b/app/src/main/java/com/zywl/test1229/ui/common/TextField.kt new file mode 100644 index 0000000..7661a66 --- /dev/null +++ b/app/src/main/java/com/zywl/test1229/ui/common/TextField.kt @@ -0,0 +1,65 @@ +package com.zywl.test1229.ui.common + +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material3.Text +import androidx.compose.material3.TextField +import androidx.compose.material3.TextFieldDefaults +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.input.ImeAction +import androidx.compose.ui.text.input.KeyboardType + +@Composable +fun EditableTextField( + label: String, + content: String, + onPipeLineChanged: (String) -> Unit, modifier: Modifier = Modifier +) { + TextField( + modifier = modifier, + value = content, + onValueChange = { + onPipeLineChanged(it) + }, + singleLine = true, + label = { + Text(label) + }, + colors = TextFieldDefaults.colors( + focusedContainerColor = Color.Transparent, + unfocusedContainerColor = Color.Transparent + ) + ) +} + +@Composable +fun EditableTextFieldInt( + label: String, + content: String, + onPipeLineChanged: (Double) -> Unit, + modifier: Modifier = Modifier +) { + TextField( + modifier = modifier, + value = content, + onValueChange = { + val newValue = it.toDoubleOrNull() // 将输入的字符串转换为 Double + if (newValue != null) { + onPipeLineChanged(newValue) // 如果转换成功,调用 onPipeLineChanged + } + }, + singleLine = true, + label = { + Text(label) + }, + keyboardOptions = KeyboardOptions.Default.copy( + keyboardType = KeyboardType.Number, // 设置为数字键盘 + imeAction = ImeAction.Done + ), + colors = TextFieldDefaults.colors( + focusedContainerColor = Color.Transparent, + unfocusedContainerColor = Color.Transparent + ) + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/zywl/test1229/ui/files/FilesRoute.kt b/app/src/main/java/com/zywl/test1229/ui/files/FilesRoute.kt new file mode 100644 index 0000000..e487593 --- /dev/null +++ b/app/src/main/java/com/zywl/test1229/ui/files/FilesRoute.kt @@ -0,0 +1,28 @@ +package com.zywl.test1229.ui.files + +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavHostController +import androidx.navigation.compose.composable +import kotlinx.serialization.Serializable + +fun NavGraphBuilder.files(navController: NavHostController) = composable { + val viewModel: FilesViewModel = hiltViewModel() + FilesScreen( + fileState = viewModel.fileState, + onFileSelected = { + navController.previousBackStackEntry?.savedStateHandle?.set(FileSavedStateKey, it) + navController.navigateUp() + }, + onCancel = navController::navigateUp + ) +} + +fun NavHostController.navigateToFiles() { + navigate(FilesRoute) +} + +const val FileSavedStateKey = "file" + +@Serializable +data object FilesRoute \ No newline at end of file diff --git a/app/src/main/java/com/zywl/test1229/view/FilesScreen.kt b/app/src/main/java/com/zywl/test1229/ui/files/FilesScreen.kt similarity index 85% rename from app/src/main/java/com/zywl/test1229/view/FilesScreen.kt rename to app/src/main/java/com/zywl/test1229/ui/files/FilesScreen.kt index f03d346..c91b27d 100644 --- a/app/src/main/java/com/zywl/test1229/view/FilesScreen.kt +++ b/app/src/main/java/com/zywl/test1229/ui/files/FilesScreen.kt @@ -1,304 +1,298 @@ -package com.zywl.test1229.view - -import android.annotation.SuppressLint -import android.content.Context -import android.content.Intent -import android.os.Environment -import android.util.Log -import androidx.activity.compose.BackHandler -import androidx.compose.foundation.Image -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.items -import androidx.compose.foundation.lazy.rememberLazyListState -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Done -import androidx.compose.material.icons.rounded.ArrowBack -import androidx.compose.material.icons.rounded.Clear -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton -import androidx.compose.material3.ListItem -import androidx.compose.material3.Scaffold -import androidx.compose.material3.Text -import androidx.compose.material3.TopAppBar -import androidx.compose.material3.TopAppBarDefaults -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 -import androidx.compose.runtime.setValue -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.input.nestedscroll.nestedScroll -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.unit.dp -import androidx.core.content.FileProvider -import androidx.navigation.NavController -import com.zywl.test1229.utils.ExcelUtils -import com.zywl.test1229.R -import com.zywl.test1229.bean.StakeInfo -import com.zywl.test1229.data.SimpleViewModel -import java.io.File -import java.nio.file.Files - -@SuppressLint("UnrememberedGetBackStackEntry") -@OptIn(ExperimentalMaterial3Api::class) -@Composable -fun FilesScreen(navController:NavController,viewModel: SimpleViewModel>){ - val context = LocalContext.current - val fileState = remember { FileState() } - val action = remember { mutableStateOf(FileAction.None) } - - //设置copy或者select里的parentfile路径 - LaunchedEffect(fileState.current) { - fileState.onContentChange() - } - //按下返回健且条件满足时 - BackHandler(fileState.current != fileState.root) { - fileState.goToParent() - } - val listState = rememberLazyListState() - val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior() - var isLoading by remember { mutableStateOf(false) } - var filePath by remember { mutableStateOf(null) } - LaunchedEffect(filePath) { - filePath?.let { - isLoading = true - var excelUtils= ExcelUtils() - var excelData=excelUtils.parseExcel(filePath!!) - isLoading = false - viewModel.setData(excelData); - navController.popBackStack() - } - } - Scaffold( - topBar = { - TopAppBar( - title = { - Text(text = action.value.topBarTitle, maxLines = 1) - }, navigationIcon = { - action.value.navigation?.let { - IconButton( - onClick = { - it.second.invoke(action.value) - } - ) { - Icon(imageVector = it.first, contentDescription = null) - } - } - }, actions = { - action.value.actions.forEach { item -> - IconButton( - onClick = item.second - ) { - Icon(imageVector = item.first, contentDescription = null) - } - } - }, scrollBehavior = scrollBehavior - ) - } - -// floatingActionButton = { -// AnimatedVisibility( - //当下拉到底时不显示 -// visible = listState.canScrollForward, -// enter = slideInVertically(initialOffsetY = { it }) + fadeIn(), -// exit = slideOutVertically(targetOffsetY = { it }) + fadeOut() -// ) { -// FloatingActionButton( -// onClick = { -// -// } -// ) { -// Icon(imageVector = Icons.Rounded.Add, contentDescription = null) -// } -// } -// } - ,modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection) - ){ innerPadding -> - LazyColumn( - state = listState, - contentPadding = innerPadding - ) { - if (fileState.parent != null) item { - ListItem( - headlineContent = { - Text(text = "..") - }, modifier = Modifier.clickable { - fileState.goToParent() - }, leadingContent = { - Icon(imageVector = Icons.Rounded.ArrowBack, contentDescription = null) - } - ) - } - items(fileState.items) { item -> - ListItem( - headlineContent = { - Text(text = item.name) - }, leadingContent = { - if (item.isDirectory) { - Image( - painter = painterResource(R.mipmap.directory), - contentDescription = "Vector Image", - modifier = Modifier.size(30.dp) // 控制图片的大小 - ) - } - if (item.isFile) { - Image( - painter = painterResource(R.mipmap.file), - contentDescription = "Vector Image", - modifier = Modifier.size(30.dp) // 控制图片的大小 - ) - } - }, -// trailingContent = { -// val expanded = remember { mutableStateOf(false) } -// IconButton( -// onClick = { -// expanded.value = true -// } -// ) { -// Icon(imageVector = Icons.Rounded.MoreVert, contentDescription = null) -// } -// } - modifier = Modifier.clickable { - if (item.isDirectory) fileState.current = item.path - if (item.isFile) { - var fileActionCopy: FileAction.Select = FileAction.Select( - source = item, - onPasteResult = { str -> - filePath=str - //返回数据 -// action.value = FileAction.None -// navController.previousBackStackEntry?.savedStateHandle?.set("resultKey", str) -// navController.popBackStack() -// isLoading = true -// -// var excelUtils=ExcelUtils() -// var excelData=excelUtils.parseExcel(str) -// -// isLoading = false -// viewModel.setData(excelData); -// navController.popBackStack() - - }, onCancel = { - action.value = FileAction.None - } - ) - action.value = fileActionCopy - } - } - ) - } - } - } - ProgressDialogView(isLoading) -} -fun Context.openExternal(file: File) { - if (file.isDirectory) return - val contentType = Files.probeContentType(file.toPath()) - val uri = FileProvider.getUriForFile(this, "$packageName.provider", file) - val intent = Intent().apply { - action = Intent.ACTION_VIEW - addCategory(Intent.CATEGORY_DEFAULT) - addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) - setDataAndType(uri, contentType) - } - startActivity(intent) -} -class FileState { - var items by mutableStateOf(emptyList()) - var root: String by mutableStateOf(Environment.getExternalStorageDirectory().path) - var current: String by mutableStateOf(root) - var parent: String? by mutableStateOf(null) - val listener = mutableListOf<(String) -> Unit>() - - fun onContentChange() { - listener.forEach { - it(current) - } - val file = File(current) -// val files = (file.listFiles() ?: arrayOf()) -// .groupBy { it.isDirectory } -// .flatMap { -// it.value.sortedBy { it.name } -// } - val files = (file.listFiles() ?: arrayOf()) - .sortedBy { it.name } // 按文件名排序 - .flatMap { - // 先获取所有文件夹,再获取所有后缀为 .xls 的文件 - when { - it.isDirectory -> listOf(it) // 如果是目录,添加到列表中 - it.extension.equals("xls", ignoreCase = true) -> listOf(it) // 如果是 .xls 文件,添加到列表中 - else -> emptyList() // 否则不做任何处理 - } - } - parent = if (current == root) null else file.parentFile?.path - items = files - } - - fun goToParent() { - parent?.let { current = it } - } -} -sealed interface FileAction { - val topBarTitle: String - val navigation: Pair Unit>? - val actions: List Unit>> - - data object None : FileAction { - override val topBarTitle: String = "文件选择" - override val navigation: Pair Unit>? = null - override val actions: List Unit>> = emptyList() - } - - class Select( - val source: File, - val onPasteResult: (String) -> Unit, - val onCancel: FileAction.() -> Unit - ):FileAction{ - fun sure() { - Log.d("MainActivity","${source.path} ${source.name}") - onPasteResult(source.path); - } - override val topBarTitle: String = "已经选中 ${source.name} " - override val navigation: Pair Unit>? = - Icons.Rounded.Clear to onCancel - override val actions: List Unit>> = listOf( - Icons.Default.Done to ::sure - ) - } - - class Copy( - val source: File, - val onPasteResult: (Boolean, String) -> Unit, - val onCancel: FileAction.() -> Unit - ) : FileAction { - private var targetDir: File = source.parentFile - fun onTargetDirChange(it: String) { - targetDir = File(it) - } - - fun paste() { - val filename = source.name - val target = File(targetDir, filename) - if (target.exists()) { - onPasteResult(false, "文件已存在") - } else { - source.copyRecursively(target) - onPasteResult(true, "复制成功") - } - } - - override val topBarTitle: String = "复制 ${source.name} 到.." - override val navigation: Pair Unit>? = - Icons.Rounded.Clear to onCancel - override val actions: List Unit>> = listOf( - Icons.Default.Done to ::paste - ) - } +package com.zywl.test1229.ui.files + +import android.annotation.SuppressLint +import android.content.Context +import android.content.Intent +import android.os.Environment +import android.util.Log +import androidx.activity.compose.BackHandler +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.rounded.ArrowBack +import androidx.compose.material.icons.filled.Done +import androidx.compose.material.icons.rounded.ArrowBack +import androidx.compose.material.icons.rounded.Clear +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.ListItem +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.material3.TopAppBarDefaults +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 +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.input.nestedscroll.nestedScroll +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.unit.dp +import androidx.core.content.FileProvider +import com.zywl.test1229.R +import java.io.File +import java.nio.file.Files + +@SuppressLint("UnrememberedGetBackStackEntry") +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun FilesScreen( + fileState: FileState, + onFileSelected: (String) -> Unit, + onCancel: () -> Unit +) { + val action = remember { mutableStateOf(FileAction.None(onBack = { onCancel() })) } + + //设置copy或者select里的parentfile路径 + LaunchedEffect(fileState.current) { + fileState.onContentChange() + } + //按下返回健且条件满足时 + BackHandler(fileState.current != fileState.root) { + fileState.goToParent() + } + val listState = rememberLazyListState() + val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior() + + Scaffold( + topBar = { + TopAppBar( + title = { + Text(text = action.value.topBarTitle, maxLines = 1) + }, navigationIcon = { + action.value.navigation?.let { + IconButton( + onClick = { + it.second.invoke(action.value) + } + ) { + Icon(imageVector = it.first, contentDescription = null) + } + } + }, actions = { + action.value.actions.forEach { item -> + IconButton( + onClick = item.second + ) { + Icon(imageVector = item.first, contentDescription = null) + } + } + }, scrollBehavior = scrollBehavior + ) + } + +// floatingActionButton = { +// AnimatedVisibility( + //当下拉到底时不显示 +// visible = listState.canScrollForward, +// enter = slideInVertically(initialOffsetY = { it }) + fadeIn(), +// exit = slideOutVertically(targetOffsetY = { it }) + fadeOut() +// ) { +// FloatingActionButton( +// onClick = { +// +// } +// ) { +// Icon(imageVector = Icons.Rounded.Add, contentDescription = null) +// } +// } +// } + , modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection) + ) { innerPadding -> + LazyColumn( + state = listState, + contentPadding = innerPadding + ) { + if (fileState.parent != null) item { + ListItem( + headlineContent = { + Text(text = "..") + }, modifier = Modifier.clickable { + fileState.goToParent() + }, leadingContent = { + Icon(imageVector = Icons.Rounded.ArrowBack, contentDescription = null) + } + ) + } + items(fileState.items) { item -> + ListItem( + headlineContent = { + Text(text = item.name) + }, leadingContent = { + if (item.isDirectory) { + Icon( + painter = painterResource(R.mipmap.directory), + contentDescription = "Vector Image", + modifier = Modifier.size(30.dp), // 控制图片的大小 + ) + } + if (item.isFile) { + Icon( + painter = painterResource(R.mipmap.file), + contentDescription = "Vector Image", + modifier = Modifier.size(30.dp) // 控制图片的大小 + ) + } + }, +// trailingContent = { +// val expanded = remember { mutableStateOf(false) } +// IconButton( +// onClick = { +// expanded.value = true +// } +// ) { +// Icon(imageVector = Icons.Rounded.MoreVert, contentDescription = null) +// } +// } + modifier = Modifier.clickable { + if (item.isDirectory) fileState.current = item.path + if (item.isFile) { + var fileActionCopy: FileAction.Select = FileAction.Select( + source = item, + onPasteResult = { str -> + //filePath = str + onFileSelected(str) + //返回数据 +// action.value = FileAction.None +// navController.previousBackStackEntry?.savedStateHandle?.set("resultKey", str) +// navController.popBackStack() +// isLoading = true +// +// var excelUtils=ExcelUtils() +// var excelData=excelUtils.parseExcel(str) +// +// isLoading = false +// viewModel.setData(excelData); +// navController.popBackStack() + + }, onCancel = { + action.value = FileAction.None(onBack = { onCancel() }) + } + ) + action.value = fileActionCopy + } + } + ) + } + } + } +} + +fun Context.openExternal(file: File) { + if (file.isDirectory) return + val contentType = Files.probeContentType(file.toPath()) + val uri = FileProvider.getUriForFile(this, "$packageName.provider", file) + val intent = Intent().apply { + action = Intent.ACTION_VIEW + addCategory(Intent.CATEGORY_DEFAULT) + addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + setDataAndType(uri, contentType) + } + startActivity(intent) +} + +class FileState { + var items by mutableStateOf(emptyList()) + var root: String by mutableStateOf(Environment.getExternalStorageDirectory().path) + var current: String by mutableStateOf(root) + var parent: String? by mutableStateOf(null) + val listener = mutableListOf<(String) -> Unit>() + + fun onContentChange() { + listener.forEach { + it(current) + } + val file = File(current) +// val files = (file.listFiles() ?: arrayOf()) +// .groupBy { it.isDirectory } +// .flatMap { +// it.value.sortedBy { it.name } +// } + val files = (file.listFiles() ?: arrayOf()) + .sortedBy { it.name } // 按文件名排序 + .flatMap { + // 先获取所有文件夹,再获取所有后缀为 .xls 的文件 + when { + it.isDirectory -> listOf(it) // 如果是目录,添加到列表中 + it.extension.equals( + "xls", + ignoreCase = true + ) -> listOf(it) // 如果是 .xls 文件,添加到列表中 + else -> emptyList() // 否则不做任何处理 + } + } + parent = if (current == root) null else file.parentFile?.path + items = files + } + + fun goToParent() { + parent?.let { current = it } + } +} + +sealed interface FileAction { + val topBarTitle: String + val navigation: Pair Unit>? + val actions: List Unit>> + + data class None(val onBack: FileAction.() -> Unit = {}) : FileAction { + override val topBarTitle: String = "文件选择" + override val navigation: Pair Unit>? = + Icons.AutoMirrored.Rounded.ArrowBack to onBack + override val actions: List Unit>> = emptyList() + } + + class Select( + val source: File, + val onPasteResult: (String) -> Unit, + val onCancel: FileAction.() -> Unit + ) : FileAction { + fun sure() { + Log.d("MainActivity", "${source.path} ${source.name}") + onPasteResult(source.path); + } + + override val topBarTitle: String = "已经选中 ${source.name} " + override val navigation: Pair Unit>? = + Icons.Rounded.Clear to onCancel + override val actions: List Unit>> = listOf( + Icons.Default.Done to ::sure + ) + } + + class Copy( + val source: File, + val onPasteResult: (Boolean, String) -> Unit, + val onCancel: FileAction.() -> Unit + ) : FileAction { + private var targetDir: File = source.parentFile + fun onTargetDirChange(it: String) { + targetDir = File(it) + } + + fun paste() { + val filename = source.name + val target = File(targetDir, filename) + if (target.exists()) { + onPasteResult(false, "文件已存在") + } else { + source.copyRecursively(target) + onPasteResult(true, "复制成功") + } + } + + override val topBarTitle: String = "复制 ${source.name} 到.." + override val navigation: Pair Unit>? = + Icons.Rounded.Clear to onCancel + override val actions: List Unit>> = listOf( + Icons.Default.Done to ::paste + ) + } } \ No newline at end of file diff --git a/app/src/main/java/com/zywl/test1229/ui/files/FilesViewModel.kt b/app/src/main/java/com/zywl/test1229/ui/files/FilesViewModel.kt new file mode 100644 index 0000000..7e0b6c9 --- /dev/null +++ b/app/src/main/java/com/zywl/test1229/ui/files/FilesViewModel.kt @@ -0,0 +1,10 @@ +package com.zywl.test1229.ui.files + +import androidx.lifecycle.ViewModel +import dagger.hilt.android.lifecycle.HiltViewModel +import javax.inject.Inject + +@HiltViewModel +class FilesViewModel @Inject constructor() : ViewModel() { + val fileState = FileState() +} \ No newline at end of file diff --git a/app/src/main/java/com/zywl/test1229/ui/home/HomeRoute.kt b/app/src/main/java/com/zywl/test1229/ui/home/HomeRoute.kt new file mode 100644 index 0000000..ba277ec --- /dev/null +++ b/app/src/main/java/com/zywl/test1229/ui/home/HomeRoute.kt @@ -0,0 +1,63 @@ +package com.zywl.test1229.ui.home + +import android.app.Activity +import android.util.Log +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableLongStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.platform.LocalContext +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavHostController +import androidx.navigation.compose.composable +import com.zywl.test1229.bean.StakeInfo +import com.zywl.test1229.ktx.TAG +import com.zywl.test1229.ktx.toast +import com.zywl.test1229.ui.files.FileSavedStateKey +import com.zywl.test1229.ui.files.navigateToFiles +import com.zywl.test1229.ui.info.StakeInfoSavedStateKey +import com.zywl.test1229.ui.info.navigateToInfo +import kotlinx.serialization.Serializable + +fun NavGraphBuilder.home(navController: NavHostController) = composable { + val file = it.savedStateHandle.get(FileSavedStateKey) + val stakeInfo = it.savedStateHandle.get(StakeInfoSavedStateKey) + val context = LocalContext.current + val viewModel: HomeViewModel = hiltViewModel() + val state by viewModel.state.collectAsState() + var lastBackPressedTime: Long by remember { mutableLongStateOf(0) } + val backPressInterval: Long = 2000 + val actions = remember(viewModel.actions) { + viewModel.actions.copy( + onFinishedActivity = { + val currentTime = System.currentTimeMillis() + if (currentTime - lastBackPressedTime < backPressInterval) { + (context as? Activity)?.finish() + } else { + context.toast("再按一次退出") + lastBackPressedTime = currentTime + } + }, navigateToInfo = { + navController.navigateToInfo(it ?: StakeInfo.Empty) + }, navigateToFiles = navController::navigateToFiles + ) + } + + LaunchedEffect(file) { + file?.let(actions.onFileSelected) + it.savedStateHandle[FileSavedStateKey] = null + } + + LaunchedEffect(stakeInfo) { + stakeInfo?.let { actions.onPipeLineChanged(state.editingIndex, it) } + it.savedStateHandle[StakeInfoSavedStateKey] = null + } + + HomeScreen(state = state, actions = actions) +} + +@Serializable +data object HomeRoute \ No newline at end of file diff --git a/app/src/main/java/com/zywl/test1229/ui/home/HomeScreen.kt b/app/src/main/java/com/zywl/test1229/ui/home/HomeScreen.kt new file mode 100644 index 0000000..a46a4a9 --- /dev/null +++ b/app/src/main/java/com/zywl/test1229/ui/home/HomeScreen.kt @@ -0,0 +1,185 @@ +package com.zywl.test1229.ui.home + +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.imePadding +import androidx.compose.foundation.layout.navigationBarsPadding +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.itemsIndexed +import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.ArrowBack +import androidx.compose.material3.Button +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.material3.TopAppBarDefaults +import androidx.compose.material3.VerticalDivider +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.input.nestedscroll.nestedScroll +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.TextStyle +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 com.zywl.test1229.R +import com.zywl.test1229.bean.StakeInfo +import com.zywl.test1229.ui.common.FloatingActionButton +import com.zywl.test1229.ui.common.PreviewSurface +import com.zywl.test1229.ui.common.Previews +import com.zywl.test1229.ui.common.ProgressDialog +import com.zywl.test1229.ui.common.StakeInfoView + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun HomeScreen( + state: HomeViewState, + actions: HomeActions +) { + val listState = rememberLazyListState() + val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior() + Scaffold( + topBar = { + TopAppBar( + title = { + Text(text = "桩管理") + }, + navigationIcon = { + IconButton(onClick = actions.onFinishedActivity) { + Icon( + imageVector = Icons.AutoMirrored.Filled.ArrowBack, + contentDescription = "Back" + ) + } + }, scrollBehavior = scrollBehavior + ) + }, floatingActionButton = { + FloatingActionButton( + onImport = actions.navigateToFiles, + onExport = actions.onExport, + onCreate = { + actions.onEditingIndexChange(-1) + actions.navigateToInfo(null) + } + ) + }, modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection) + ) { innerPadding -> + Box( + modifier = Modifier.fillMaxSize() + ) { + LazyColumn( + state = listState, + contentPadding = innerPadding, + verticalArrangement = Arrangement.spacedBy(8.dp), + modifier = Modifier.imePadding() + ) { + itemsIndexed(state.items) { index, item -> + var expanded by remember { mutableStateOf(false) } + + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center, + modifier = Modifier + .background( + color = MaterialTheme.colorScheme.surfaceContainerLow + ) + .padding(8.dp) + ) { + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .fillMaxWidth() + .clickable { + actions.onEditingIndexChange(index) + actions.navigateToInfo(item) + }, + ) { + Text( + text = "${index + 1}", + style = TextStyle( + fontSize = 16.sp, + fontWeight = FontWeight.Bold, + ), modifier = Modifier.padding(start = 6.dp) + ) + Text( + text = "测绘临时编号:${item.tempNo}", + style = TextStyle( + fontSize = 14.sp, + fontWeight = FontWeight.Bold + ), textAlign = TextAlign.Center, + modifier = Modifier.weight(1f) + ) + IconButton( + onClick = { + expanded = !expanded + } + ) { + Icon( + painter = painterResource((if (!expanded) R.mipmap.downdraw else R.mipmap.updraw)), + contentDescription = null, + modifier = Modifier.size(12.dp) + ) + } + } + StakeInfoView( + isShow = expanded, + stakeInfo = item, + onPipeLineChanged = { + actions.onPipeLineChanged(index, it) + }, + onDeleteItem = { + actions.onDeleteItem(index) + } + ) + VerticalDivider() + } + } + item { + Spacer(modifier = Modifier.height(96.dp)) + } + } + Button( + onClick = { /* Handle Click */ }, + modifier = Modifier + .align(Alignment.BottomStart) // 设置它在Box中的右下角 + .navigationBarsPadding() + ) { + Text("设置点的线") + } + } + } + ProgressDialog(visible = state.exporting) +} + + +@Composable +@Previews +fun HomeScreenPreview() { + val state = HomeViewState( + items = List(10) { StakeInfo.Empty } + ) + PreviewSurface { + HomeScreen(state = state, actions = HomeActions()) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/zywl/test1229/ui/home/HomeViewModel.kt b/app/src/main/java/com/zywl/test1229/ui/home/HomeViewModel.kt new file mode 100644 index 0000000..61ff2e9 --- /dev/null +++ b/app/src/main/java/com/zywl/test1229/ui/home/HomeViewModel.kt @@ -0,0 +1,125 @@ +package com.zywl.test1229.ui.home + +import android.content.Context +import android.util.Log +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.zywl.test1229.bean.Constant +import com.zywl.test1229.bean.StakeInfo +import com.zywl.test1229.data.PermissionState +import com.zywl.test1229.di.PermissionStateFlow +import com.zywl.test1229.ktx.TAG +import com.zywl.test1229.ktx.toast +import com.zywl.test1229.utils.ExcelUtils +import dagger.hilt.android.lifecycle.HiltViewModel +import dagger.hilt.android.qualifiers.ApplicationContext +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.isActive +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import java.io.File +import javax.inject.Inject + +@HiltViewModel +class HomeViewModel @Inject constructor( + @ApplicationContext + private val context: Context, + @PermissionStateFlow + private val permissionState: MutableStateFlow +) : ViewModel() { + private val _state = MutableStateFlow(HomeViewState()) + val state = _state.asStateFlow() + val actions = HomeActions( + onFileSelected = ::onFileSelected, + onPipeLineChanged = ::onPipeLineChanged, + onExport = ::onExport, + onDeleteItem = ::onDeleteItem, + onEditingIndexChange = ::onEditingIndexChange + ) + + init { + viewModelScope.launch(Dispatchers.Default) { + while (isActive) { + delay(10_000) + val items = _state.value.items + if (items.isEmpty()) continue + ExcelUtils.produceExcel(context, items, true) + } + } + permissionState.onEach { + if (it == PermissionState.Granted) { + val file = File(File(Constant.SDCARD, Constant.projectName), "backup/main.xls") + onFileSelected(file.path) + } + }.launchIn(viewModelScope) + } + + private fun onFileSelected(file: String) { + viewModelScope.launch(Dispatchers.IO) { + val stakeInfoList = ExcelUtils.parseExcel(file) + Log.d(TAG, "onFileSelected: $stakeInfoList") + _state.update { it.copy(items = it.items + stakeInfoList) } + } + } + + private fun onPipeLineChanged(index: Int, value: StakeInfo) { + Log.d(TAG, "onPipeLineChanged: $index, $value") + _state.update { + val stakeInfoList = it.items.toMutableList() + if (index >= 0) { + stakeInfoList[index] = value + } else { + stakeInfoList.add(value) + } + it.copy(items = stakeInfoList) + } + } + + private fun onExport() { + val items = _state.value.items + if (items.isEmpty()) return context.toast("导出失败,没有数据") + viewModelScope.launch(Dispatchers.IO) { + _state.update { it.copy(exporting = true) } + ExcelUtils.produceExcel(context, items) + _state.update { it.copy(exporting = false) } + withContext(Dispatchers.Main) { + context.toast("导出成功") + } + } + } + + private fun onDeleteItem(index: Int) { + _state.update { + val stakeInfoList = it.items.toMutableList() + stakeInfoList.removeAt(index) + it.copy(items = stakeInfoList) + } + } + + private fun onEditingIndexChange(index: Int) { + _state.update { it.copy(editingIndex = index) } + } +} + +data class HomeViewState( + val items: List = emptyList(), + val exporting: Boolean = false, + val editingIndex: Int = -1, +) + +data class HomeActions( + val navigateToInfo: (StakeInfo?) -> Unit = {}, + val onFinishedActivity: () -> Unit = {}, + val onPipeLineChanged: (index: Int, StakeInfo) -> Unit = { _, _ -> }, + val onExport: () -> Unit = {}, + val navigateToFiles: () -> Unit = {}, + val onFileSelected: (String) -> Unit = {}, + val onDeleteItem: (index: Int) -> Unit = {}, + val onEditingIndexChange: (Int) -> Unit = {} +) \ No newline at end of file diff --git a/app/src/main/java/com/zywl/test1229/ui/info/InfoRoute.kt b/app/src/main/java/com/zywl/test1229/ui/info/InfoRoute.kt new file mode 100644 index 0000000..a93c7de --- /dev/null +++ b/app/src/main/java/com/zywl/test1229/ui/info/InfoRoute.kt @@ -0,0 +1,40 @@ +package com.zywl.test1229.ui.info + +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavHostController +import androidx.navigation.compose.composable +import com.zywl.test1229.bean.StakeInfo +import kotlinx.serialization.Serializable + +fun NavGraphBuilder.info(navController: NavHostController) = composable { + val viewModel: InfoViewModel = hiltViewModel() + val state by viewModel.state.collectAsState() + val actions = remember(viewModel.actions) { + viewModel.actions.copy( + onDone = { + navController.previousBackStackEntry?.savedStateHandle?.set( + StakeInfoSavedStateKey, + state.info + ) + navController.navigateUp() + }, + onCancel = navController::navigateUp, + ) + } + InfoScreen(state = state, actions = actions) +} + +fun NavHostController.navigateToInfo(info: StakeInfo) { + navigate(info) +} + +const val StakeInfoSavedStateKey = "stake_info" + +@Serializable +data class InfoRoute( + val info: StakeInfo +) \ No newline at end of file diff --git a/app/src/main/java/com/zywl/test1229/ui/info/InfoScreen.kt b/app/src/main/java/com/zywl/test1229/ui/info/InfoScreen.kt new file mode 100644 index 0000000..b652a88 --- /dev/null +++ b/app/src/main/java/com/zywl/test1229/ui/info/InfoScreen.kt @@ -0,0 +1,179 @@ +package com.zywl.test1229.ui.info + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.imePadding +import androidx.compose.foundation.layout.navigationBarsPadding +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.statusBarsPadding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.Button +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.zywl.test1229.bean.Constant +import com.zywl.test1229.ui.common.EditableExposedDropdownMenuSample +import com.zywl.test1229.ui.common.EditableTextField +import com.zywl.test1229.ui.common.EditableTextFieldInt +import com.zywl.test1229.ui.common.EditableTime +import com.zywl.test1229.ui.common.ExposedDropdownMenuSample +import com.zywl.test1229.ui.common.PreviewSurface +import com.zywl.test1229.ui.common.Previews + + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun InfoScreen( + state: InfoViewState, + actions: InfoActions +) { + val scrollState = rememberScrollState() + val data = state.info + Column( + modifier = Modifier + .fillMaxSize() + .navigationBarsPadding() + .imePadding() + .statusBarsPadding() + .padding(8.dp) + .background(color = MaterialTheme.colorScheme.surfaceContainer) + .verticalScroll(scrollState) + ) { + EditableTextField( + "桩号", + data.tempNo, + modifier = Modifier.fillMaxWidth(), + onPipeLineChanged = { + actions.onInfoChange(data.copy(tempNo = it)) + } + ) + EditableTextField( + "管线", + data.pipeLine, + modifier = Modifier.fillMaxWidth(), + onPipeLineChanged = { + actions.onInfoChange(data.copy(pipeLine = it)) + } + ) + EditableExposedDropdownMenuSample( + Constant.stakeTypeArray, + "桩类型", + data.stakeType, + modifier = Modifier.fillMaxWidth(), + onPipeLineChanged = { + actions.onInfoChange(data.copy(stakeType = it)) + } + ) + EditableExposedDropdownMenuSample( + Constant.depthArray, + "管中埋深(米)", + data.pipeDepth, + modifier = Modifier.fillMaxWidth(), + onPipeLineChanged = { + actions.onInfoChange(data.copy(pipeDepth = it)) + } + ) + EditableTextField( + "里程", + data.mileage, + modifier = Modifier.fillMaxWidth(), + onPipeLineChanged = { + actions.onInfoChange(data.copy(mileage = it)) + } + ) + EditableExposedDropdownMenuSample( + Constant.BuriedTechnologyArray, + "埋设工艺", + data.buryTech, + modifier = Modifier.fillMaxWidth(), + onPipeLineChanged = { + actions.onInfoChange(data.copy(buryTech = it)) + } + ) + ExposedDropdownMenuSample( + "是否出入土点", + data.isInPoint, + modifier = Modifier.fillMaxWidth(), + onPipeLineChanged = { + actions.onInfoChange(data.copy(isInPoint = it)) + } + ) + EditableExposedDropdownMenuSample( + Constant.terrainArray, + "地形", + data.terrain, + modifier = Modifier.fillMaxWidth(), + onPipeLineChanged = { + actions.onInfoChange(data.copy(terrain = it)) + } + ) + Row { + EditableTextFieldInt( + label = "经度", + content = (data.latitude).toString(), + modifier = Modifier + .fillMaxWidth() + .weight(1f), onPipeLineChanged = { + actions.onInfoChange(data.copy(latitude = it)) + } + ) + EditableTextFieldInt( + label = "维度", + content = (data.longitude).toString(), + modifier = Modifier + .fillMaxWidth() + .weight(1f), onPipeLineChanged = { + actions.onInfoChange(data.copy(longitude = it)) + } + ) + } + EditableTime( + label = "采集时间", + date = data.collectDate, + modifier = Modifier + .fillMaxWidth() + .padding(start = 6.dp, end = 6.dp, top = 18.dp, bottom = 12.dp), + onPipeLineChanged = { + actions.onInfoChange(data.copy(collectDate = it)) + } + ) + Row( + horizontalArrangement = Arrangement.Center, + modifier = Modifier.fillMaxWidth() + ) { + Button( + modifier = Modifier, + onClick = actions.onDone + ) { + Text(text = "确定") + } + Spacer(modifier = Modifier.width(16.dp)) + Button( + modifier = Modifier, + onClick = actions.onCancel + ) { + Text(text = "取消") + } + } + Spacer(modifier = Modifier.height(8.dp)) + } +} + +@Composable +@Previews +fun InfoScreenPreview() { + PreviewSurface { + InfoScreen(state = InfoViewState(), actions = InfoActions()) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/zywl/test1229/ui/info/InfoViewModel.kt b/app/src/main/java/com/zywl/test1229/ui/info/InfoViewModel.kt new file mode 100644 index 0000000..cc721e6 --- /dev/null +++ b/app/src/main/java/com/zywl/test1229/ui/info/InfoViewModel.kt @@ -0,0 +1,37 @@ +package com.zywl.test1229.ui.info + +import androidx.lifecycle.SavedStateHandle +import androidx.lifecycle.ViewModel +import androidx.navigation.toRoute +import com.zywl.test1229.bean.StakeInfo +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update +import javax.inject.Inject + +@HiltViewModel +class InfoViewModel @Inject constructor( + private val savedStateHandle: SavedStateHandle +) : ViewModel() { + private val info = savedStateHandle.toRoute() + private val _state = MutableStateFlow(InfoViewState(info = info)) + val state = _state.asStateFlow() + val actions = InfoActions( + onInfoChange = ::onInfoChange + ) + + private fun onInfoChange(value: StakeInfo) { + _state.update { it.copy(info = value) } + } +} + +data class InfoViewState( + val info: StakeInfo = StakeInfo.Empty +) + +data class InfoActions( + val onInfoChange: (StakeInfo) -> Unit = {}, + val onDone: () -> Unit = {}, + val onCancel: () -> Unit = {}, +) \ No newline at end of file diff --git a/app/src/main/java/com/zywl/test1229/utils/ExcelUtils.kt b/app/src/main/java/com/zywl/test1229/utils/ExcelUtils.kt index 39b0dde..7e95ea5 100644 --- a/app/src/main/java/com/zywl/test1229/utils/ExcelUtils.kt +++ b/app/src/main/java/com/zywl/test1229/utils/ExcelUtils.kt @@ -14,38 +14,38 @@ import java.io.FileInputStream import java.time.LocalDate -class ExcelUtils { - fun parseExcel(path:String):MutableList{ - val list:MutableList = mutableListOf() - var file=File(path) +object ExcelUtils { + fun parseExcel(path: String): MutableList { + val list: MutableList = mutableListOf() + var file = File(path) if (!file.exists()) return list - var fileByte=FileInputStream(file) - val workbook=HSSFWorkbook(fileByte) - val sheet=workbook.getSheetAt(0); + var fileByte = FileInputStream(file) + val workbook = HSSFWorkbook(fileByte) + val sheet = workbook.getSheetAt(0); for (rowIndex in 1 until sheet.physicalNumberOfRows) { val row = sheet.getRow(rowIndex) - var index3=0.0 - var index4=0.0 - var index5=0.0 + var index3 = 0.0 + var index4 = 0.0 + var index5 = 0.0 try { - index3=getCellData(row.getCell(9)).toDouble(); + index3 = getCellData(row.getCell(9)).toDouble(); } catch (e: NumberFormatException) { - index3=0.0 + index3 = 0.0 } try { - index4=getCellData(row.getCell(10)).toDouble(); + index4 = getCellData(row.getCell(10)).toDouble(); } catch (e: NumberFormatException) { - index4=0.0 + index4 = 0.0 } try { - index5=getCellData(row.getCell(10)).toDouble(); + index5 = getCellData(row.getCell(10)).toDouble(); } catch (e: NumberFormatException) { - index5=0.0 + index5 = 0.0 } // 跳过空行 if (row == null) continue - var stakeInfo=StakeInfo( + var stakeInfo = StakeInfo( getCellData(row.getCell(1)), getCellData(row.getCell(0)), getCellData(row.getCell(2)), @@ -64,7 +64,8 @@ class ExcelUtils { } return list; } - fun getCellData(cell:Cell):String{ + + fun getCellData(cell: Cell): String { val cellValue = when (cell.cellType) { CellType.STRING -> cell.stringCellValue CellType.NUMERIC -> cell.numericCellValue.toString() @@ -73,7 +74,8 @@ class ExcelUtils { } return cellValue } - fun produceExcel(context: Context?,array:List,isbackUp: Boolean=false){ + + fun produceExcel(context: Context?, array: List, isbackUp: Boolean = false) { val primeExlPFixedSizeWidth = JsonUils.parseJSONListDouble( JsonUils.loadJSONFromAssets("pile_data.json", context), "primeExlPFixedSizeWidth" @@ -84,8 +86,8 @@ class ExcelUtils { ) // softexportPrimeDataP(m_excelFiledNameP,m_pointListGroup); - var listMain= mutableListOf>() - for (index in array){ + var listMain = mutableListOf>() + for (index in array) { var list = mutableListOf() list.add(index.pipeLine) list.add(index.tempNo) @@ -108,30 +110,42 @@ class ExcelUtils { JsonUils.loadJSONFromAssets("pile_data.json", context), "primeExlP" ) - var directory="" - var date=LocalDate.now() - if (!isbackUp){ - var pointTitleInfo=Tools.getDataClassFieldNames(PointInfo()) - var lineTitleInfo=Tools.getDataClassFieldNames(LineInfo()) + var directory = "" + var date = LocalDate.now() + if (!isbackUp) { + var pointTitleInfo = Tools.getDataClassFieldNames(PointInfo()) + var lineTitleInfo = Tools.getDataClassFieldNames(LineInfo()) var pointInfos = Tools.getDataClassFieldNamesAndValueForMap(collectPoints(array)) - var points=Tools.getDataClassFieldNamesAndValuesForList(pointTitleInfo,pointInfos) - directory=Constant.SDCARD+"/"+Constant.projectName+"/tables" + var points = + Tools.getDataClassFieldNamesAndValuesForList(pointTitleInfo, pointInfos) + directory = Constant.SDCARD + "/" + Constant.projectName + "/tables" FileUtils.mkDir(directory) - var fileNum = FileUtils.getFileCountInDirectory(directory, ".xls")/2+1 + var fileNum = FileUtils.getFileCountInDirectory(directory, ".xls") / 2 + 1 ExcelUtilsOfPoi.initExcelPrime( "$directory/桩表_${date}_${fileNum}.xls", parseJSONprimeExlP, "桩表", primeExlPFixedSizeWidth, primeExlFixedHeight ) - ExcelUtilsOfPoi.initExcel("$directory/桩表PC_${date}_${fileNum}.xls",pointTitleInfo,lineTitleInfo,"P_ALL", "L_All") + ExcelUtilsOfPoi.initExcel( + "$directory/桩表PC_${date}_${fileNum}.xls", + pointTitleInfo, + lineTitleInfo, + "P_ALL", + "L_All" + ) ExcelUtilsOfPoi.writeObjListToExcelPrime( listMain, "$directory/桩表_${date}_${fileNum}.xls", primeExlFixedHeight, primeExlPFixedSizeWidth ) - ExcelUtilsOfPoi.writeObjListToExcel(0,points,"$directory/桩表PC_${date}_${fileNum}.xls") - }else{ - directory=Constant.SDCARD+"/"+Constant.projectName+"/backup" - FileUtils.mkDir(directory) + ExcelUtilsOfPoi.writeObjListToExcel( + 0, + points, + "$directory/桩表PC_${date}_${fileNum}.xls" + ) + } else { + directory = Constant.SDCARD + "/" + Constant.projectName + "/backup" + // FileUtils.mkDir(directory) + File(directory).mkdirs() ExcelUtilsOfPoi.initExcelPrime( "$directory/main.xls", parseJSONprimeExlP, "桩表", primeExlPFixedSizeWidth, primeExlFixedHeight @@ -141,48 +155,50 @@ class ExcelUtils { primeExlFixedHeight, primeExlPFixedSizeWidth ) } - Log.d("MainActivity","produceExcel") + Log.d("MainActivity", "produceExcel") } - fun collectPoints(data:List):List{ - var list:MutableList = mutableListOf() + + fun collectPoints(data: List): List { + var list: MutableList = mutableListOf() var point = PointInfo() - var index=1; - data.forEach { item-> - point.buryTech=item.buryTech - point.collectDate=item.collectDate - point.exp_Date=item.collectDate - point.exp_Num="RQa$index" - point.id="RQa$index" - point.isInPoint=item.isInPoint - point.latitude=item.latitude.toString() - point.longitude=item.longitude.toString() - point.mileage=item.mileage - point.pipeDepth=item.pipeDepth - point.pipeLine=item.pipeLine - point.serialNum=index.toString() - point.situation="T-正常" - point.stakeType=item.stakeType - point.state="新测" - point.subsid=item.stakeType - point.symbol="探测点" - point.symbolExpression="RQ-探测点" - point.symbolID="472" - point.symbolSizeX="4.0" - point.symbolSizeY="4.0" - point.tempNo=item.tempNo - point.terrain=item.terrain - point.code="RQ" - point.pipeLine="燃气-RQ" - point.rangeExpression="3.5" - point.shortCode="RQ" - point.sysId=index.toString() - point.type="Type_None" + var index = 1; + data.forEach { item -> + point.buryTech = item.buryTech + point.collectDate = item.collectDate + point.exp_Date = item.collectDate + point.exp_Num = "RQa$index" + point.id = "RQa$index" + point.isInPoint = item.isInPoint + point.latitude = item.latitude.toString() + point.longitude = item.longitude.toString() + point.mileage = item.mileage + point.pipeDepth = item.pipeDepth + point.pipeLine = item.pipeLine + point.serialNum = index.toString() + point.situation = "T-正常" + point.stakeType = item.stakeType + point.state = "新测" + point.subsid = item.stakeType + point.symbol = "探测点" + point.symbolExpression = "RQ-探测点" + point.symbolID = "472" + point.symbolSizeX = "4.0" + point.symbolSizeY = "4.0" + point.tempNo = item.tempNo + point.terrain = item.terrain + point.code = "RQ" + point.pipeLine = "燃气-RQ" + point.rangeExpression = "3.5" + point.shortCode = "RQ" + point.sysId = index.toString() + point.type = "Type_None" list.add(point) } return list } - fun collectLines(data:List):List{ - var list:MutableList = mutableListOf() + + fun collectLines(data: List): List { + var list: MutableList = mutableListOf() return list } } \ No newline at end of file diff --git a/app/src/main/java/com/zywl/test1229/view/DropdownMenu.kt b/app/src/main/java/com/zywl/test1229/view/DropdownMenu.kt deleted file mode 100644 index 3f82cc2..0000000 --- a/app/src/main/java/com/zywl/test1229/view/DropdownMenu.kt +++ /dev/null @@ -1,257 +0,0 @@ - -package com.zywl.test1229.view -import android.util.Log -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.text.KeyboardOptions - -import androidx.compose.material3.DatePicker -import androidx.compose.material3.DatePickerDialog -import androidx.compose.material3.DropdownMenuItem -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.ExposedDropdownMenuBox -import androidx.compose.material3.ExposedDropdownMenuDefaults -import androidx.compose.material3.Text -import androidx.compose.material3.TextButton -import androidx.compose.material3.TextField -import androidx.compose.material3.TextFieldDefaults -import androidx.compose.material3.rememberDatePickerState -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 -import androidx.compose.runtime.setValue - - -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.text.input.ImeAction -import androidx.compose.ui.text.input.KeyboardType - -import androidx.compose.ui.unit.sp -import com.zywl.test1229.utils.ExcelUtils -import com.zywl.test1229.bean.StakeInfo -import com.zywl.test1229.data.SimpleViewModel -import kotlinx.coroutines.delay -import java.text.SimpleDateFormat -import java.util.Date -import java.util.Locale - - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -fun EditableExposedDropdownMenuSample(options:List, - label:String, - content:String, - onPipeLineChanged: (String) -> Unit,modifier: Modifier=Modifier) { - var expanded by remember { mutableStateOf(false) } - ExposedDropdownMenuBox( - //是否列出列表 - expanded = expanded, - onExpandedChange = { - expanded = !expanded - }, - ) { - TextField( - value = content, - onValueChange = { - onPipeLineChanged(it) - }, - label = { Text(label) }, - trailingIcon = { - ExposedDropdownMenuDefaults.TrailingIcon( - expanded = expanded - ) - }, - colors = TextFieldDefaults.colors(focusedContainerColor = Color.Transparent, unfocusedContainerColor = Color.Transparent ), - modifier = modifier.menuAnchor() - ) - val filteringOptions = - options.filter { it.contains(content, ignoreCase = true) } - if (filteringOptions.isNotEmpty()) { - ExposedDropdownMenu( - expanded = expanded, - onDismissRequest = { - expanded = false - } - ) { - filteringOptions.forEach { selectionOption -> - DropdownMenuItem( - text = { - Text(text = selectionOption) - }, - onClick = { - onPipeLineChanged(selectionOption) - expanded = false - } - ) - } - } - } - } -} -@OptIn(ExperimentalMaterial3Api::class) -@Composable -fun ExposedDropdownMenuSample(label:String, - content:String, - onPipeLineChanged: (String) -> Unit,modifier: Modifier=Modifier) { - val options = listOf("否", "是") - var expanded by remember { mutableStateOf(false) } - ExposedDropdownMenuBox( - expanded = expanded, - onExpandedChange = { - expanded = !expanded - } - ) { - TextField( - readOnly = true, - value = content, - onValueChange = { }, - label = { Text(label) }, - trailingIcon = { - ExposedDropdownMenuDefaults.TrailingIcon( - //这里使用了 ExposedDropdownMenuDefaults.TrailingIcon 组件作为右侧图标。 - //这个图标会根据 expanded 的状态变化,通常用于表示下拉菜单的打开或关闭状态 - expanded = expanded - ) - }, - colors = TextFieldDefaults.colors(focusedContainerColor = Color.Transparent, unfocusedContainerColor = Color.Transparent ), - modifier = modifier.menuAnchor() - ) - - ExposedDropdownMenu( - expanded = expanded, - onDismissRequest = { - expanded = false - }, - ) { - options.forEach { selectionOption -> - DropdownMenuItem( - text = { - Text(selectionOption) - }, - onClick = { - onPipeLineChanged(selectionOption) - expanded = false - } - ) - } - } - } -} -@Composable -fun EditableTextField(label:String, - content:String, - onPipeLineChanged: (String) -> Unit,modifier: Modifier=Modifier){ - TextField( - modifier = modifier, - value =content, - onValueChange = { - onPipeLineChanged(it) - }, - singleLine = true, - label = { - Text(label) - }, - colors = TextFieldDefaults.colors(focusedContainerColor = Color.Transparent, unfocusedContainerColor = Color.Transparent ) - ) -} -@Composable -fun EditableTextFieldInt(label:String, - content:String, - onPipeLineChanged: (Double) -> Unit,modifier: Modifier=Modifier){ - TextField( - modifier = modifier, - value =content, - onValueChange = { - val newValue = it.toDoubleOrNull() // 将输入的字符串转换为 Double - if (newValue != null) { - onPipeLineChanged(newValue) // 如果转换成功,调用 onPipeLineChanged - } - }, - singleLine = true, - label = { - Text(label) - }, - keyboardOptions = KeyboardOptions.Default.copy( - keyboardType = KeyboardType.Number, // 设置为数字键盘 - imeAction = ImeAction.Done - ), - colors = TextFieldDefaults.colors(focusedContainerColor = Color.Transparent, unfocusedContainerColor = Color.Transparent ) - ) -} -@OptIn(ExperimentalMaterial3Api::class) -@Composable -fun EditableTime(label:String, - date:String, - onPipeLineChanged: (String) -> Unit,modifier: Modifier=Modifier){ - var content=date - if(content.isEmpty()) { - content="**-**-**" - } - - Row(modifier = modifier, horizontalArrangement = Arrangement.SpaceBetween) { - var showTimePicker by remember { mutableStateOf(false) } - Text(text = label) - Text(text = content, - style = TextStyle( - fontSize = 14.sp, - fontWeight = FontWeight.Bold - ), - modifier = Modifier.clickable { - showTimePicker = true - } - ) - val datePickerState = rememberDatePickerState() - if (showTimePicker) { - DatePickerDialog( - onDismissRequest = { showTimePicker=false; }, - confirmButton = { - TextButton(onClick = { - val formattedDate = datePickerState.selectedDateMillis?.let { - val date = Date(it) - val format = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()) // 设置日期格式 - format.format(date) - } ?: content - onPipeLineChanged(formattedDate) - Log.d("DatePickerDialog",formattedDate) - showTimePicker=false; - }) { - Text("OK") - } - }, - dismissButton = { - TextButton(onClick = {showTimePicker=false;}) { - Text("Cancel") - } - } - ) { - DatePicker(state = datePickerState) - } - } - } -} -@Composable -fun PeriodicTaskForDataSave(viewModel: SimpleViewModel>){ - var context = LocalContext.current - LaunchedEffect(key1 = Unit) { - while (true){ - delay(10_000) - var data=viewModel.stateFlow.value.data - Log.d("MainActivity","PeriodicTaskForDataSave") - if (data!=null){ - if (data.isNotEmpty()) - ExcelUtils().produceExcel(context,data,true) - else - Log.d("MainActivity","PeriodicTaskForDataSave is Empty") - }else{ - Log.d("MainActivity","PeriodicTaskForDataSave is null") - } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/zywl/test1229/view/HomePager.kt b/app/src/main/java/com/zywl/test1229/view/HomePager.kt deleted file mode 100644 index 03ea3c4..0000000 --- a/app/src/main/java/com/zywl/test1229/view/HomePager.kt +++ /dev/null @@ -1,331 +0,0 @@ -package com.zywl.test1229.view - -import android.net.Uri -import android.util.Log -import android.widget.Toast -import androidx.compose.animation.AnimatedVisibility -import androidx.compose.animation.fadeIn -import androidx.compose.animation.slideInVertically -import androidx.compose.foundation.background -import androidx.compose.foundation.clickable -import androidx.compose.foundation.gestures.detectTapGestures -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.itemsIndexed -import androidx.compose.foundation.shape.CircleShape -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.ArrowBack -import androidx.compose.material3.Button -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.FloatingActionButton -import androidx.compose.material3.FloatingActionButtonDefaults -import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton -import androidx.compose.material3.Surface -import androidx.compose.material3.Text -import androidx.compose.material3.TopAppBar -import androidx.compose.material3.TopAppBarDefaults -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.SideEffect -import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.ImageBitmap -import androidx.compose.ui.input.pointer.pointerInput -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.imageResource -import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import androidx.navigation.NavController -import com.google.gson.Gson -import com.zywl.test1229.utils.ExcelUtils -import com.zywl.test1229.MainActivity -import com.zywl.test1229.R -import com.zywl.test1229.bean.Constant -import com.zywl.test1229.bean.StakeInfo -import com.zywl.test1229.data.SimpleViewModel -import kotlinx.coroutines.delay - - -@Composable -fun FloatingButtonS(navController: NavController,viewModel: SimpleViewModel>){ -// var viewModel= viewModel(); - var isShow by remember { mutableStateOf(false) } - var isShowDialog by remember { mutableStateOf(false) } - val context = LocalContext.current -// val result = navController.currentBackStackEntry?.savedStateHandle?.get("resultKey") -// SideEffect { -// Log.d("mainPager","stakeInfoView $result") -// } - Column(modifier = Modifier.padding(end = 16.dp,bottom = 8.dp)) { - AnimatedVisibility( - visible = isShow, - enter = fadeIn() + slideInVertically(initialOffsetY = { it }) // Animation for appearing - ) { - Column( - verticalArrangement = Arrangement.spacedBy(8.dp), - modifier = Modifier.padding(bottom = 8.dp) - ) { - FloatingActionButton( - elevation = FloatingActionButtonDefaults.elevation(0.dp), - shape = CircleShape, - onClick = { - navController.navigate("files") - }, - modifier = Modifier.size(66.dp), - ) { - Text("导入", color = Color.White) - } - FloatingActionButton( - elevation = FloatingActionButtonDefaults.elevation(0.dp), - shape = CircleShape, - onClick = { - isShowDialog=true - var excelUtils = ExcelUtils() - var data=viewModel.stateFlow.value.data - if (data!=null){ - if (data.isNotEmpty()){ - excelUtils.produceExcel(context,data) - Toast.makeText(context, "导出成功", Toast.LENGTH_SHORT).show() - } - else{ - Toast.makeText(context, "导出失败,没有数据", Toast.LENGTH_SHORT).show() - } - }else{ - Toast.makeText(context, "导出失败,没有数据", Toast.LENGTH_SHORT).show() - } - isShowDialog=false - }, - modifier = Modifier.size(66.dp), - ) { - Text("导出", color = Color.White) - } - - FloatingActionButton( - elevation = FloatingActionButtonDefaults.elevation(0.dp), - shape = CircleShape, - onClick = { - navController.navigate("info") - }, - modifier = Modifier.size(66.dp), - ) { - Text("新增", color = Color.White) - } - } - } - FloatingActionButton( - elevation = FloatingActionButtonDefaults.elevation(0.dp), - shape = CircleShape, - onClick = { - isShow=!isShow - }, - modifier = Modifier.size(66.dp), - containerColor = Color.Blue - ) { - var str=if (isShow) "-" else "+" - Text(str, color = Color.White, style = TextStyle(fontSize = 26.sp)) - } - } - ProgressDialogView(isShowDialog) -} - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -fun TopBar(activity: MainActivity){ - // 设置两次点击之间的间隔时间为 2 秒 - var showExitMessage by remember { mutableStateOf(false) } - - if (showExitMessage) { - Toast.makeText(LocalContext.current, "再按一次退出", Toast.LENGTH_SHORT).show() - LaunchedEffect(Unit) { - delay(activity.backPressInterval) - showExitMessage = false // 2 秒后隐藏提示 - } - } - TopAppBar( - title = { Text(text = "桩管理") }, - colors = TopAppBarDefaults.mediumTopAppBarColors( - containerColor = Color.Cyan - ), - navigationIcon = { - IconButton(onClick = { - val currentTime = System.currentTimeMillis() - if (currentTime - activity.lastBackPressedTime < activity.backPressInterval) { - // 如果两次点击的时间间隔小于规定的时间(2000ms),退出应用 - activity.finish() // 或者使用 `exitProcess(0)` 来退出应用 - } else { - // 否则显示提示信息并记录当前时间 - activity.lastBackPressedTime = currentTime - showExitMessage = true - } - - }) { - Icon(imageVector = Icons.Default.ArrowBack, contentDescription = "Back") - } - } - ) -} -var outerItems:MutableList = mutableListOf() -@Composable -fun mainPager(navController: NavController, viewModel: SimpleViewModel>, modifier: Modifier = Modifier){ - // 订阅 stateFlow 以获取数据 - val result = viewModel.stateFlow.collectAsState() - val dataList = result.value.data - PeriodicTaskForDataSave(viewModel) - SideEffect { - Log.d("sss","mainPager outerItems") - dataList?.let { - outerItems= dataList as MutableList - } - } - Box(modifier= modifier - .fillMaxSize() - .padding(start = 36.dp, end = 36.dp)){ - LazyColumn(modifier=Modifier.fillMaxSize()) { - itemsIndexed(dataList?:ArrayList()){ - index, item -> - var expanded by remember { mutableStateOf(false) } - var stakeInfo by remember { - mutableStateOf(item) - } - SideEffect { - outerItems[index] = stakeInfo - } - Column(modifier = Modifier - .fillParentMaxWidth() - .padding(top = 16.dp), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center) { - Row(modifier = Modifier - .fillMaxWidth() - .height(26.dp), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.SpaceBetween) { - Text(text = "${index+1}", style = TextStyle( - fontSize = 16.sp, - fontWeight = FontWeight.Bold, - ), modifier = Modifier.padding(start = 6.dp) - ) - Text(text = "测绘临时编号:${item.tempNo}",style = TextStyle( - fontSize = 14.sp, - fontWeight = FontWeight.Bold - ), modifier = Modifier - .padding(start = 26.dp) - .clickable { - navController.navigate("info/${Uri.encode(Gson().toJson(item))}") - } - ) - Icon(bitmap = ImageBitmap.imageResource( id = (if (!expanded) R.mipmap.downdraw else R.mipmap.updraw)), - contentDescription = null,modifier= Modifier - .size(16.dp) - .padding(end = 6.dp) - .clickable { - expanded = !expanded - } - ) - } - StakeInfoView(isShow = expanded, stakeInfo = stakeInfo, - onPipeLineChanged = { newStakeInfo-> - stakeInfo=newStakeInfo - }, - onDeleteItem = { - - } - ) - Box(modifier = Modifier.padding(top = 6.dp)) { - Spacer(modifier = Modifier - .fillMaxWidth() - .height(1.dp) - .background(Color.Blue)) - } - } - } - } - Button( - onClick = { /* Handle Click */ }, - modifier = Modifier - .align(Alignment.BottomStart) // 设置它在Box中的右下角 - .padding(bottom = 36.dp) - ) { - Text("设置点的线") - } - } -} -@Composable -fun StakeInfoView(isShow:Boolean, stakeInfo: StakeInfo, onPipeLineChanged:(StakeInfo)->Unit,onDeleteItem:()->Unit){ - if (!isShow){ - return - } - Surface( - shape = RoundedCornerShape(8.dp), - shadowElevation = 10.dp, - ){ - Column { - EditableTextField("管线",stakeInfo.pipeLine, modifier = Modifier.fillMaxWidth(), onPipeLineChanged = { - onPipeLineChanged(stakeInfo.copy(pipeLine = it)) - }) - EditableExposedDropdownMenuSample(Constant.stakeTypeArray,"桩类型",stakeInfo.stakeType,modifier = Modifier.fillMaxWidth(),onPipeLineChanged = { - onPipeLineChanged(stakeInfo.copy(stakeType = it)) - }) - EditableExposedDropdownMenuSample(Constant.depthArray,"管中埋深(米)",stakeInfo.pipeDepth,modifier = Modifier.fillMaxWidth(),onPipeLineChanged = { - onPipeLineChanged(stakeInfo.copy(pipeDepth = it)) - }) - EditableTextField("里程",stakeInfo.mileage, modifier = Modifier.fillMaxWidth(), onPipeLineChanged = { - onPipeLineChanged(stakeInfo.copy(mileage = it)) - }) - EditableExposedDropdownMenuSample(Constant.BuriedTechnologyArray,"埋设工艺",stakeInfo.buryTech,modifier = Modifier.fillMaxWidth(),onPipeLineChanged = { - onPipeLineChanged(stakeInfo.copy(buryTech = it)) - }) - ExposedDropdownMenuSample("是否出入土点",stakeInfo.isInPoint, modifier = Modifier.fillMaxWidth(), onPipeLineChanged = { - onPipeLineChanged(stakeInfo.copy(isInPoint = it)) - }) - EditableExposedDropdownMenuSample(Constant.terrainArray,"地形",stakeInfo.terrain,modifier = Modifier.fillMaxWidth(),onPipeLineChanged = { - onPipeLineChanged(stakeInfo.copy(terrain = it)) - }) - Row { - EditableTextFieldInt("经度","${stakeInfo.latitude}", modifier = Modifier - .fillMaxWidth() - .weight(1f), onPipeLineChanged = { - onPipeLineChanged(stakeInfo.copy(latitude = it)) - }) - EditableTextFieldInt("维度","${stakeInfo.longitude}", modifier = Modifier - .fillMaxWidth() - .weight(1f), onPipeLineChanged = { - onPipeLineChanged(stakeInfo.copy(longitude = it)) - }) - } - EditableTextFieldInt("Z(85高程,桩根部)","${stakeInfo.z}", modifier = Modifier - .fillMaxWidth(), onPipeLineChanged = { - onPipeLineChanged(stakeInfo.copy(z = it)) - }) - EditableTime("采集时间",stakeInfo.collectDate, modifier = Modifier - .fillMaxWidth() - .padding(start = 6.dp, end = 6.dp, top = 18.dp, bottom = 12.dp), onPipeLineChanged = { - onPipeLineChanged(stakeInfo.copy(collectDate = it)) - }) - Row (modifier = Modifier.fillMaxSize(),horizontalArrangement = Arrangement.Center){ - Button(onClick = { - onDeleteItem() - }) { - Text(text = "删除") - } - } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/zywl/test1229/view/SimpleDialog.kt b/app/src/main/java/com/zywl/test1229/view/SimpleDialog.kt deleted file mode 100644 index c3e025f..0000000 --- a/app/src/main/java/com/zywl/test1229/view/SimpleDialog.kt +++ /dev/null @@ -1,112 +0,0 @@ -package com.zywl.test1229.view - - -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.width -import androidx.compose.material3.AlertDialog -import androidx.compose.material3.CircularProgressIndicator -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.Icon -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text -import androidx.compose.material3.TextButton -import androidx.compose.material3.TextField -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.unit.dp -import java.awt.Color - - -@Composable -fun SimpleDialogView(showDialog : Boolean, - dialogTitle: String, - dialogText: String, - onDismissRequest: () -> Unit, - onConfirmation: () -> Unit){ - if (showDialog) { - AlertDialog( - onDismissRequest = { - onDismissRequest() - }, - title = { - Text(dialogTitle) - }, - text = { - Text(dialogText) - }, - confirmButton = { - TextButton( - onClick = { - onConfirmation() - } - ) { - Text("确定") - } - }, - dismissButton = { - TextButton( - onClick = { - onDismissRequest() - } - ) { - Text("取消") - } - } - ) - } -} -@Composable -fun PermissionDialog( - visible: Boolean, - onConfirm: () -> Unit, - onDismissRequest: () -> Unit, -) { - if (visible) AlertDialog( - title = { - Text(text = "管理所有文件") - }, text = { - Text(text = "授予app管理存储设备上的所有文件的权限") - }, - confirmButton = { - TextButton( - onClick = { - onConfirm() - onDismissRequest() - } - ) { - Text(text = "确定") - } - }, dismissButton = { - TextButton( - onClick = onDismissRequest - ) { - Text(text = "取消") - } - }, onDismissRequest = onDismissRequest - ) -} -@Composable -fun ProgressDialogView(visible: Boolean){ - if (visible) - AlertDialog( - onDismissRequest = { }, - title = { - Text(text = "正在处理...") - }, - text = { - Column(horizontalAlignment = Alignment.CenterHorizontally) { - CircularProgressIndicator( - modifier = Modifier.width(64.dp), - color = MaterialTheme.colorScheme.secondary, - trackColor = MaterialTheme.colorScheme.surfaceVariant, - ) - } - }, - confirmButton = { - - } - ) -} \ No newline at end of file diff --git a/app/src/main/java/com/zywl/test1229/view/StakeInfoViewPager.kt b/app/src/main/java/com/zywl/test1229/view/StakeInfoViewPager.kt deleted file mode 100644 index cd59b29..0000000 --- a/app/src/main/java/com/zywl/test1229/view/StakeInfoViewPager.kt +++ /dev/null @@ -1,154 +0,0 @@ -package com.zywl.test1229.view -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.verticalScroll -import androidx.compose.material3.Button -import androidx.compose.material3.Scaffold -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue -import androidx.compose.ui.Modifier -import androidx.compose.ui.unit.dp -import androidx.navigation.NavController -import com.zywl.test1229.bean.Constant -import com.zywl.test1229.bean.StakeInfo -import com.zywl.test1229.bean.User -import com.zywl.test1229.data.SimpleViewModel - -class StakeInfoViewPager { - @Composable - fun MainView(navController: NavController, - viewModel: SimpleViewModel>, - stakeInfo: StakeInfo? =null, - modifier: Modifier=Modifier){ - val result = viewModel.stateFlow.collectAsState() - val dataList = result.value.data as MutableList - val scrollState = rememberScrollState() - var index=-1 - - var dataTmp=StakeInfo("","","",0.0,0.0,0.0, - "","","","","","","") - var data by remember { mutableStateOf(dataTmp) } - stakeInfo?.let { - data=stakeInfo - dataList?.let{ - index= dataList.indexOf(stakeInfo) ?: -1 - } - } - - Scaffold( - modifier = Modifier.fillMaxSize().padding(start = 26.dp, end = 26.dp), - ) { innerPadding -> - Column(modifier = Modifier.fillMaxSize().padding(innerPadding).verticalScroll(scrollState)) { - EditableTextField( - "桩号", - data.tempNo, - modifier = Modifier.fillMaxWidth(), - onPipeLineChanged = { - data=data.copy(tempNo = it) - }) - EditableTextField( - "管线", - data.pipeLine, - modifier = Modifier.fillMaxWidth(), - onPipeLineChanged = { - data=data.copy(pipeLine = it) - }) - EditableExposedDropdownMenuSample( - Constant.stakeTypeArray, - "桩类型", - data.stakeType, - modifier = Modifier.fillMaxWidth(), - onPipeLineChanged = { - data=data.copy(stakeType = it) - }) - EditableExposedDropdownMenuSample( - Constant.depthArray, - "管中埋深(米)", - data.pipeDepth, - modifier = Modifier.fillMaxWidth(), - onPipeLineChanged = { - data=data.copy(pipeDepth = it) - }) - EditableTextField( - "里程", - data.mileage, - modifier = Modifier.fillMaxWidth(), - onPipeLineChanged = { - data=data.copy(mileage = it) - }) - EditableExposedDropdownMenuSample( - Constant.BuriedTechnologyArray, - "埋设工艺", - data.buryTech , - modifier = Modifier.fillMaxWidth(), - onPipeLineChanged = { - data=data.copy(buryTech = it) - }) - ExposedDropdownMenuSample( - "是否出入土点", - data.isInPoint, - modifier = Modifier.fillMaxWidth(), - onPipeLineChanged = { - data=data.copy(isInPoint = it) - }) - EditableExposedDropdownMenuSample( - Constant.terrainArray, - "地形", - data.terrain, - modifier = Modifier.fillMaxWidth(), - onPipeLineChanged = { - data=data.copy(terrain = it) - }) - Row { - EditableTextFieldInt("经度", (data.latitude).toString(), modifier = Modifier - .fillMaxWidth() - .weight(1f), onPipeLineChanged = { - data=data.copy(latitude = it) - }) - EditableTextFieldInt("维度", (data.longitude).toString(), modifier = Modifier - .fillMaxWidth() - .weight(1f), onPipeLineChanged = { - data=data.copy(longitude = it) - }) - } - EditableTime("采集时间", - data.collectDate, - modifier = Modifier - .fillMaxWidth() - .padding(start = 6.dp, end = 6.dp, top = 18.dp, bottom = 12.dp), - onPipeLineChanged = { - data=data.copy(collectDate = it) - }) - Row(horizontalArrangement = Arrangement.Center, modifier = Modifier.fillMaxWidth()) { - Button(modifier = Modifier.padding(end = 16.dp),onClick = { - dataList?.let { - if (index>=0) - dataList[index]=data - else - dataList.add(data) - viewModel.setData(dataList) - } - navController.popBackStack() - }) { - Text(text = "确定") - } - Button(modifier = Modifier.padding(start = 16.dp),onClick = { - navController.popBackStack() - }) { - Text(text = "取消") - } - } - } - } - } -}