MVVM架构下SettingsFragment选CSV传数据到ChartFragment绘图实现咨询
解决方案
- 第一步:补全ChartViewModel实现
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel class ChartViewModel : ViewModel() { // 存储CSV解析后的行数据 val csvResultLiveData = MutableLiveData<List<String>>() }
- 第二步:修改SettingsFragment逻辑
现有代码存在3个问题:
- 使用
startActivity无法获取文件选择结果,需改用Activity Result API - ViewModel使用
by viewModels()获取的是Fragment局部实例,无法和ChartFragment共享,需改为by activityViewModels()获取Activity全局共享实例 - 文件选择未限定CSV格式,需添加MIME类型限制
修改后的完整代码:
import androidx.activity.result.contract.ActivityResultContracts class SettingsFragment : PreferenceFragmentCompat() { private val SETTINGS_DEBUG = "SETTINGS_DEBUG" private val resolver by lazy { requireContext().contentResolver } // 改为获取Activity范围的共享ViewModel private val myViewModel by activityViewModels<ChartViewModel>() // 注册文件选择结果回调 private val pickCsvLauncher = registerForActivityResult(ActivityResultContracts.OpenDocument()) { uri -> uri ?: return@registerForActivityResult // 读取选中的CSV文件内容 val csvLines = mutableListOf<String>() try { resolver.openInputStream(uri)?.use { inputStream -> inputStream.bufferedReader().useLines { lines -> csvLines.addAll(lines) } } // 过滤空行后发送到ViewModel myViewModel.csvResultLiveData.postValue(csvLines.filter { it.isNotBlank() }) } catch (exc: Exception) { Log.d(SETTINGS_DEBUG, "读取CSV文件失败: ${exc.message}") } } override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { setPreferencesFromResource(R.xml.root_preferences, rootKey) val myPref: Preference? = findPreference("load_csv_file") as Preference? myPref?.onPreferenceClickListener = Preference.OnPreferenceClickListener { try { // 限定仅选择CSV格式文件 pickCsvLauncher.launch(arrayOf("text/csv", "text/comma-separated-values")) } catch (exc: Exception) { Log.d(SETTINGS_DEBUG, exc.message.toString()) } true } } }
- 第三步:优化ChartFragment异常兼容
现有CSV解析逻辑未做异常捕获,遇到格式错误的行会直接崩溃,优化toEntry方法:
private fun toEntry(line: String): Entry? { return try { val split = line.split(";") if (split.size < 3) return null val time = split[1] val secondsStartIdx = time.length - 2 val minutesStartIdx = time.length - 4 val hoursStartIdx = (time.length - 6).coerceAtLeast(0) val hour = time.substring(hoursStartIdx, hoursStartIdx + time.length - 4) val minutest = time.substring(minutesStartIdx, minutesStartIdx + 2) val seconds = time.substring(secondsStartIdx, secondsStartIdx + 2) val newTime = hour.toFloat() * 10000 + (minutest.toFloat() * 100 / 60).toInt() * 100 + (seconds.toFloat() * 100 / 60).toInt() Entry(newTime, split[2].toFloat()) } catch (e: Exception) { Log.d(CHART_DEBUG, "解析行失败: $line, 错误: ${e.message}") null } }
对应修改观察者里的转换逻辑:
val newEntries = csvLines.mapNotNull { line -> toEntry(line) } .groupingBy { entry -> entry.x } .reduce { _, accumulator, element -> if (accumulator.y > element.y) accumulator else element }.values
内容的提问来源于stack exchange,提问作者MaksChojniak1
相关产品推荐
相关产品推荐

