You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Kotlin中ViewPager下Fragment间传递非空数组的简易方案

ViewPager中Fragment间数据传递问题解决

核心问题分析

  1. 错误的Fragment实例引用:你在Calculations中直接Base().amplitude.toList()是创建了一个全新的Base实例,并非ViewPager中正在加载的那个,自然拿不到已读取的数据。
  2. ViewPager预加载机制:ViewPager默认会预加载相邻页面的Fragment,导致Calculations可能在Base还未完成CSV读取时就已初始化,此时数组为空。
  3. flag和Bundle的局限性:flag无法跨Fragment实例同步状态;Bundle仅适合传递一次性静态数据,无法监听数据加载完成后的变化。

解决方案:使用共享ViewModel

利用ViewModel的生命周期特性,同一个Activity下的Fragment可以共享同一个ViewModel实例,通过LiveData监听数据变化,当Base完成数据读取后,Calculations能自动收到数据并执行计算。

1. 创建共享数据的ViewModel

import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel

class DataViewModel : ViewModel() {
    // 存储时间数据的LiveData
    val timeList = MutableLiveData<ArrayList<Double>>()
    // 存储振幅数据的LiveData
    val amplitudeList = MutableLiveData<ArrayList<Double>>()
}

2. 修改Base Fragment(数据读取端)

import android.os.Environment
import android.view.View
import android.widget.TextView
import android.widget.ScrollingMovementMethod
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.BufferedReader
import java.io.File
import java.io.FileNotFoundException
import java.io.FileReader

class Base : Fragment() {
    private lateinit var dataViewModel: DataViewModel
    private val time = ArrayList<Double>()
    private val amplitude = ArrayList<Double>()
    
    private fun readNumbersFromCSV(fileName: String) {
        val textView: TextView = requireView().findViewById(R.id.result)
        val timeTextView: TextView = requireView().findViewById(R.id.Time)
        val amplitudeTextView: TextView = requireView().findViewById(R.id.Amplitude)
        
        try {
            withContext(Dispatchers.Main) {
                timeTextView.movementMethod = ScrollingMovementMethod()
                amplitudeTextView.movementMethod = ScrollingMovementMethod()
                timeTextView.append("Time, s\n")
                amplitudeTextView.append("Amplitude\n")
            }

            val file = File(fileName)
            if(!file.exists()){
                throw FileNotFoundException("File not found")
            }
            val reader = BufferedReader(FileReader(file))
            var line = reader.readLine()
            while (line != null) {
                val parts = line.split(",")
                if (parts.size == 2) {
                    time.add(parts[1].toDouble())
                    amplitude.add(parts[0].toDouble())
                    // 主线程更新UI
                    withContext(Dispatchers.Main) {
                        timeTextView.append(parts[1] + "\n")
                        amplitudeTextView.append(parts[0] + "\n")
                    }
                }
                line = reader.readLine()
            }
            // 数据读取完成后更新ViewModel
            withContext(Dispatchers.Main) {
                dataViewModel.timeList.value = time
                dataViewModel.amplitudeList.value = amplitude
            }
            reader.close()
        } catch (e: FileNotFoundException) {
            withContext(Dispatchers.Main) {
                textView.text = "Error: File Not Found"
            }
        } catch (e: Exception) {
            withContext(Dispatchers.Main) {
                textView.text = "Error: ${e.message}"
            }
        }
    }
    
    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.fragment_base, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        // 获取与Activity共享的ViewModel
        dataViewModel = ViewModelProvider(requireActivity())[DataViewModel::class.java]
        val path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).toString()
        val file = File(path, "data.csv").toString()
        
        // 开启子线程读取文件,避免阻塞主线程
        lifecycleScope.launch(Dispatchers.IO) {
            readNumbersFromCSV(file)
        }
    }
}

3. 修改Calculations Fragment(数据计算端)

import android.view.View
import android.widget.TextView
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider

class Calculations : Fragment() {
    private lateinit var dataViewModel: DataViewModel
    
    private fun meanAmplitude(amplitudes: List<Double>): Double {
        if(amplitudes.isEmpty()) return 3.5
        return amplitudes.sum() / amplitudes.size
    }
    
    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.fragment_calculations, container, false)
    }
    
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        val textViewAmp: TextView = view.findViewById(R.id.Camplitude)
        
        // 获取共享ViewModel
        dataViewModel = ViewModelProvider(requireActivity())[DataViewModel::class.java]
        // 监听振幅数据变化,数据就绪后自动计算并更新UI
        dataViewModel.amplitudeList.observe(viewLifecycleOwner) { amplitudes ->
            val valueOfMean = meanAmplitude(amplitudes).toString()
            textViewAmp.text = valueOfMean
        }
    }
}

4. MyAdapter和HomeActivity无需修改

原Adapter和Activity的代码可以保持不变,因为ViewModel是通过requireActivity()获取的,同一个Activity下的Fragment会自动共享ViewModel实例。


内容的提问来源于stack exchange,提问作者aescaesse

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 14:10:31