如何将单Activity的CSV绘图代码迁移适配到Drawer导航的Fragment架构
Android CSV图表功能Fragment架构迁移适配方案
原有应用逻辑为点击按钮唤起文件选择窗口,选中CSV文件后读取数据绘制到图表上,现适配基于Fragment的导航架构的迁移步骤如下:
1. 调整布局文件fragment_chart.xml
将原有activity_main.xml中的按钮和图表控件迁移到fragment_chart.xml中,保持控件id不变:
- 加载CSV按钮id:
R.id.button_loadCsv - 折线图控件id:
R.id.lineChart
2. 修改ChartViewModel.kt
增加图表数据的存储和观察能力,修改后代码如下:
package com.example.aplikacjadlataty.ui.chart import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import com.github.mikephil.charting.data.Entry class ChartViewModel : ViewModel() { private val _chartEntries = MutableLiveData<List<Entry>>() val chartEntries: LiveData<List<Entry>> = _chartEntries fun updateChartEntries(entries: List<Entry>) { _chartEntries.value = entries } }
3. 重构ChartFragment.kt
将原有MainActivity中的业务逻辑全部迁移到Fragment中,适配Fragment生命周期和上下文调用规则,修改后代码如下:
package com.example.aplikacjadlataty.ui.chart import android.content.Intent import android.net.Uri import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.Button import androidx.activity.result.contract.ActivityResultContracts import androidx.fragment.app.Fragment import androidx.lifecycle.ViewModelProviders import com.example.aplikacjadlataty.R import com.github.mikephil.charting.charts.LineChart import com.github.mikephil.charting.data.Entry import com.github.mikephil.charting.data.LineData import com.github.mikephil.charting.data.LineDataSet import com.github.mikephil.charting.animation.Easing import java.io.BufferedReader import java.io.IOException import java.io.InputStreamReader import java.util.Collections class ChartFragment : Fragment() { private lateinit var chartViewModel: ChartViewModel private lateinit var lineChart: LineChart private val getContent = registerForActivityResult(ActivityResultContracts.GetContent()) { uri: Uri? -> uri ?: return@registerForActivityResult val lines = readCSV(uri) val newEntries = lines.map { line -> toEntry(line) } .groupingBy { entry -> entry.x } .reduce { _, accumulator, element -> if (accumulator.y > element.y) accumulator else element }.values chartViewModel.updateChartEntries(newEntries.toList()) } override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { chartViewModel = ViewModelProviders.of(this).get(ChartViewModel::class.java) val root = inflater.inflate(R.layout.fragment_chart, container, false) lineChart = root.findViewById(R.id.lineChart) val loadCsvBtn = root.findViewById<Button>(R.id.button_loadCsv) initChartBaseConfig() loadCsvBtn.setOnClickListener { val intent = Intent(Intent.ACTION_OPEN_DOCUMENT) intent.addCategory(Intent.CATEGORY_OPENABLE) intent.type = "text/*" getContent.launch("text/*") } chartViewModel.chartEntries.observe(viewLifecycleOwner) { entries -> refreshChart(entries) } return root } private fun initChartBaseConfig() { lineChart.apply { description.isEnabled = false isHighlightPerDragEnabled = false isScaleYEnabled = false axisRight.isEnabled = false marker = CustomMarker(requireContext(), R.layout.marker_view) } } private fun refreshChart(entries: List<Entry>) { val vl = LineDataSet(entries.take(4000), "cost") vl.apply { setDrawValues(false) setDrawFilled(true) lineWidth = 1.5f fillColor = R.color.gray fillAlpha = R.color.red setDrawCircles(false) } lineChart.data = LineData(vl) lineChart.notifyDataSetChanged() lineChart.animateX(1800, Easing.EaseInExpo) } @Throws(IOException::class) private fun readCSV(uri: Uri): List<String> { val csvFile = requireContext().contentResolver.openInputStream(uri) ?: return Collections.emptyList() val isr = InputStreamReader(csvFile) return BufferedReader(isr).readLines() } private fun toEntry(line: String): Entry { val split = line.split(";") val time = split[1] val secondsStartIdx = time.length - 2 val minutesStartIdx = time.length - 4 val hoursStartIdx = Math.max(time.length - 6, 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() return Entry(newTime, split[2].toFloat()) } }
4. 保留NavigationActivity.kt无需修改
原有NavigationActivity已经完成了导航架构配置、广告初始化、分享功能等逻辑,且Chart页面已配置为顶层目的地,不需要额外调整。
内容的提问来源于stack exchange,提问作者MaksChojniak
相关产品推荐
相关产品推荐

