Kotlin中ViewPager下Fragment间传递非空数组的简易方案
ViewPager中Fragment间数据传递问题解决
核心问题分析
- 错误的Fragment实例引用:你在
Calculations中直接Base().amplitude.toList()是创建了一个全新的Base实例,并非ViewPager中正在加载的那个,自然拿不到已读取的数据。 - ViewPager预加载机制:ViewPager默认会预加载相邻页面的Fragment,导致
Calculations可能在Base还未完成CSV读取时就已初始化,此时数组为空。 - 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
相关产品推荐
相关产品推荐

