优化 app 架构

This commit is contained in:
2025-02-14 01:28:37 +08:00
parent 68d6ca0285
commit fb947accd9
34 changed files with 1894 additions and 1435 deletions

View File

@@ -1,10 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<application
android:name=".App"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"

View File

@@ -0,0 +1,7 @@
package com.zywl.test1229
import android.app.Application
import dagger.hilt.android.HiltAndroidApp
@HiltAndroidApp
class App : Application()

View File

@@ -1,210 +1,43 @@
package com.zywl.test1229
import android.annotation.SuppressLint
import android.content.Intent
import android.content.pm.ActivityInfo
import android.os.Build
import android.os.Bundle
import android.os.Environment
import android.provider.Settings
import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.annotation.RequiresApi
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
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.width
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.material3.Surface
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.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.compose.LocalLifecycleOwner
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import com.google.gson.Gson
import com.zywl.test1229.bean.Constant
import com.zywl.test1229.bean.PointInfo
import com.zywl.test1229.bean.StakeInfo
import com.zywl.test1229.data.SimpleViewModel
import com.zywl.test1229.ktx.toast
import androidx.lifecycle.ViewModelProvider
import com.zywl.test1229.ui.SharedNavHost
import com.zywl.test1229.ui.common.PermissionDialog
import com.zywl.test1229.ui.theme.Test1229Theme
import com.zywl.test1229.utils.ExcelUtils
import com.zywl.test1229.utils.Tools
import com.zywl.test1229.view.FilesScreen
import com.zywl.test1229.view.FloatingButtonS
import com.zywl.test1229.view.PermissionDialog
import com.zywl.test1229.view.StakeInfoViewPager
import com.zywl.test1229.view.TopBar
import com.zywl.test1229.view.mainPager
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
var lastBackPressedTime: Long = 0
val backPressInterval: Long = 2000
@RequiresApi(Build.VERSION_CODES.R)
private val viewModel by lazy {
ViewModelProvider(this)[MainViewModel::class]
}
@SuppressLint("SourceLockedOrientationActivity")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
setContent {
MyApp()
}
}
val outerItems:MutableList<StakeInfo> = 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<List<StakeInfo>> by lazy { SimpleViewModel() }
val viewModel:SimpleViewModel<List<StakeInfo>> = 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<List<StakeInfo>>){
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
)
}
}

View File

@@ -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<PermissionState>,
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
)

View File

@@ -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"
}

View File

@@ -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,
"",
"",
"",
"",
"",
"",
""
)
}
}

View File

@@ -0,0 +1,5 @@
package com.zywl.test1229.data
enum class PermissionState {
None, Granted, Denied
}

View File

@@ -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<PermissionState> {
return MutableStateFlow(PermissionState.None)
}
}
@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class PermissionStateFlow

View File

@@ -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<PermissionState>
) {
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
}
}

View File

@@ -0,0 +1,3 @@
package com.zywl.test1229.ktx
val Any.TAG: String get() = this::class.java.simpleName

View File

@@ -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)
}
}

View File

@@ -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<String>,
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
}
)
}
}
}
}

View File

@@ -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)
}
}
}
}

View File

@@ -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))
}
}
}

View File

@@ -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
)
}

View File

@@ -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
)
}
}

View File

@@ -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

View File

@@ -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 = {
}
)
}

View File

@@ -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 = "删除")
}
}
}
}
}

View File

@@ -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
)
)
}

View File

@@ -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<FilesRoute> {
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

View File

@@ -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<List<StakeInfo>>){
val context = LocalContext.current
val fileState = remember { FileState() }
val action = remember { mutableStateOf<FileAction>(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<String?>(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<File>())
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<ImageVector, FileAction.() -> Unit>?
val actions: List<Pair<ImageVector, () -> Unit>>
data object None : FileAction {
override val topBarTitle: String = "文件选择"
override val navigation: Pair<ImageVector, (FileAction) -> Unit>? = null
override val actions: List<Pair<ImageVector, () -> 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<ImageVector, FileAction.() -> Unit>? =
Icons.Rounded.Clear to onCancel
override val actions: List<Pair<ImageVector, () -> 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<ImageVector, FileAction.() -> Unit>? =
Icons.Rounded.Clear to onCancel
override val actions: List<Pair<ImageVector, () -> 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>(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<File>())
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<ImageVector, FileAction.() -> Unit>?
val actions: List<Pair<ImageVector, () -> Unit>>
data class None(val onBack: FileAction.() -> Unit = {}) : FileAction {
override val topBarTitle: String = "文件选择"
override val navigation: Pair<ImageVector, (FileAction) -> Unit>? =
Icons.AutoMirrored.Rounded.ArrowBack to onBack
override val actions: List<Pair<ImageVector, () -> 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<ImageVector, FileAction.() -> Unit>? =
Icons.Rounded.Clear to onCancel
override val actions: List<Pair<ImageVector, () -> 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<ImageVector, FileAction.() -> Unit>? =
Icons.Rounded.Clear to onCancel
override val actions: List<Pair<ImageVector, () -> Unit>> = listOf(
Icons.Default.Done to ::paste
)
}
}

View File

@@ -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()
}

View File

@@ -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<HomeRoute> {
val file = it.savedStateHandle.get<String>(FileSavedStateKey)
val stakeInfo = it.savedStateHandle.get<StakeInfo>(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

View File

@@ -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())
}
}

View File

@@ -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<PermissionState>
) : 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<StakeInfo> = 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 = {}
)

View File

@@ -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<StakeInfo> {
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
)

View File

@@ -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())
}
}

View File

@@ -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<StakeInfo>()
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 = {},
)

View File

@@ -14,38 +14,38 @@ import java.io.FileInputStream
import java.time.LocalDate
class ExcelUtils {
fun parseExcel(path:String):MutableList<StakeInfo>{
val list:MutableList<StakeInfo> = mutableListOf()
var file=File(path)
object ExcelUtils {
fun parseExcel(path: String): MutableList<StakeInfo> {
val list: MutableList<StakeInfo> = 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<StakeInfo>,isbackUp: Boolean=false){
fun produceExcel(context: Context?, array: List<StakeInfo>, 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<List<String>>()
for (index in array){
var listMain = mutableListOf<List<String>>()
for (index in array) {
var list = mutableListOf<String>()
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<StakeInfo>):List<PointInfo>{
var list:MutableList<PointInfo> = mutableListOf()
fun collectPoints(data: List<StakeInfo>): List<PointInfo> {
var list: MutableList<PointInfo> = 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<StakeInfo>):List<LineInfo>{
var list:MutableList<LineInfo> = mutableListOf()
fun collectLines(data: List<StakeInfo>): List<LineInfo> {
var list: MutableList<LineInfo> = mutableListOf()
return list
}
}

View File

@@ -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<String>,
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<List<StakeInfo>>){
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")
}
}
}
}

View File

@@ -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<List<StakeInfo>>){
// 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<String>("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<StakeInfo> = mutableListOf()
@Composable
fun mainPager(navController: NavController, viewModel: SimpleViewModel<List<StakeInfo>>, 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<StakeInfo>
}
}
Box(modifier= modifier
.fillMaxSize()
.padding(start = 36.dp, end = 36.dp)){
LazyColumn(modifier=Modifier.fillMaxSize()) {
itemsIndexed(dataList?:ArrayList<StakeInfo>()){
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 = "删除")
}
}
}
}
}

View File

@@ -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 = {
}
)
}

View File

@@ -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<List<StakeInfo>>,
stakeInfo: StakeInfo? =null,
modifier: Modifier=Modifier){
val result = viewModel.stateFlow.collectAsState()
val dataList = result.value.data as MutableList<StakeInfo>
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 = "取消")
}
}
}
}
}
}