0

Get numbers

class Base : Fragment() {
    val time = ArrayList<Double>()
    val amplitude = ArrayList<Double>()
    var flag = 0
    
    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)
        timeTextView.movementMethod = ScrollingMovementMethod()
        amplitudeTextView.movementMethod = ScrollingMovementMethod()
        try {

            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())
                    timeTextView.append(parts[1] + "\n")
                    amplitudeTextView.append(parts[0] + "\n")
                }
                line = reader.readLine()
            }
            flag = 1
            reader.close()

        } catch (e: FileNotFoundException) {
            textView.text = "Error: File Not Found"
        } catch (e: Exception) {
            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)
        val path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).toString()
        val file = File(path, "data.csv").toString()
        readNumbersFromCSV(file)
        /*now im ready to pass data to another class*/
    }
}

Do some calculations on those numbers

class Calculations : Fragment() {
    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 copiedList = Base().amplitude.toList() /* data from file passed to new array*/
        val textViewAmp: TextView = view.findViewById(R.id.Camplitude)
        val valueOfMean = meanAmplitude(copiedList).toString() /*calculate mean value*/
        textViewAmp.text = valueOfMean /*display it*/
    }
}

MyAdapter

internal class MyAdapter (var context: Context, fm: FragmentManager, var totalTabs: Int): FragmentPagerAdapter(fm) {
    override fun getCount(): Int {
        return totalTabs
    }

    override fun getItem(position: Int): Fragment {
        return when(position){
            0 -> {
                Base()
            }
            1 -> {
                Calculations()
            }
            2 -> {
                About()
            }
            else -> getItem(position)
        }
    }
}

HomeActivity

class HomeActivity : AppCompatActivity() {

    private lateinit var tabLayout: TabLayout
    private lateinit var viewPager: ViewPager
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        window.setFlags(
            WindowManager.LayoutParams.FLAG_FULLSCREEN,
            WindowManager.LayoutParams.FLAG_FULLSCREEN
        )
        supportActionBar?.hide()
        setContentView(R.layout.activity_home)

        tabLayout = findViewById(R.id.tabLayout)
        viewPager = findViewById(R.id.viewPager)

        tabLayout.addTab(tabLayout.newTab().setText("Data"))
        tabLayout.addTab(tabLayout.newTab().setText("Calculations"))
        tabLayout.addTab(tabLayout.newTab().setText("About"))
        tabLayout.tabGravity = TabLayout.GRAVITY_FILL

        val adapter = MyAdapter(this, supportFragmentManager, tabLayout.tabCount)
        viewPager.adapter = adapter
        viewPager.addOnPageChangeListener(TabLayout.TabLayoutOnPageChangeListener(tabLayout))
        tabLayout.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
            override fun onTabSelected(tab: TabLayout.Tab?) {
                viewPager.currentItem = tab!!.position
            }

            override fun onTabUnselected(tab: TabLayout.Tab?) {

            }

            override fun onTabReselected(tab: TabLayout.Tab?) {

            }

        })
    }
}

Im new in Kotlin. I have a problem with initializing an array that is being filled with data from a .csv file in the Base class, and then its contents should be passed to the Calculations class. The problem is that the array instance is being passed before it is being filled with numbers. Two fragments are generated probably in the same time.

Loading from file and initializing an array in the first class works, elements are displayed on the screen without any problems. After passing the array to the second class, it is empty.

I tried to do a flag, but it doesnt work like I though. Im not using activities, just Fragments and ViewPager. I tried Bundles but its hard to apply new things in my messy project.

Junaid Khalid
  • 811
  • 2
  • 11
  • 26
aescaesse
  • 5
  • 3
  • you try to pass it to a fragment. so check this out https://stackoverflow.com/questions/17436298/how-to-pass-a-variable-from-activity-to-fragment-and-pass-it-back – Elias Fazel Jan 18 '23 at 17:55

1 Answers1

0

Here:

val copiedList = Base().amplitude.toList()

You are instantiating a new instance of Base by calling its constructor. This new instance shares nothing with any previous instance. It's a brand new Base that hasn't done anything yet so its lists are still empty.

To pass data between fragments, you should create an arguments Bundle and pass that to the new fragment. The reason you need to do it this way is that Android automatically destroys and recreates Fragment instances under various conditions, and only the arguments data is preserved for the new instance.

The conventional way to do this is to define a Fragment factory function named newInstance() in its companion object. Then the Fragment can unpack the new data in onViewCreated(). You have to convert to and from DoubleArrays because Bundle doesn't support Lists.

class Calculations private constructor(): Fragment(R.layout.fragment_calculations) {

    companion object {
        private const val TIME_LIST_KEY = "timeList"
        private const val AMP_LIST_KEY = "ampList"

        fun newInstance(timeList: List<Double>, ampList: List<Double>) =
            Calculations().apply {
                arguments = bundleOf(
                    TIME_LIST_KEY to timeList.toDoubleArray(),
                    AMP_LIST_KEY to ampList.toDoubleArray()
                )
            }
    }

    private fun meanAmplitude(amplitudes: List<Double>): Double {
        if(amplitudes.isEmpty()) return 3.5
        return amplitudes.sum() / amplitudes.size
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        val timeList = requireArguments().getDoubleArray(TIME_LIST_KEY).toList()
        val ampList = requireArguments().getDoubleArray(AMP_LIST_KEY).toList()

        val textViewAmp: TextView = view.findViewById(R.id.Camplitude)
        val valueOfMean = meanAmplitude(ampList).toString() /*calculate mean value*/
        textViewAmp.text = valueOfMean /*display it*/
    }
}

Then in your first fragment, you use Calculations.newInstance() to create your second fragment before passing it to the transaction manager.


By the way, there's a major bug in your Base class. Since Fragment instances can be reused by the OS, the same fragment can go through multiple lifecycles. Since you are adding your data to the same ArrayLists every time onViewCreated() is called, they will get longer and longer as the user rotates the screen or navigates back and forth in the app. You should either remove those properties and use local variables instead, or you should clear those ArrayLists in onDestroyView().

Tenfour04
  • 83,111
  • 11
  • 94
  • 154
  • I understand the idea, but got an error: Fragment Calculations does not have any arguments. – aescaesse Jan 18 '23 at 19:01
  • Did you make the constructor private and only create Calculations instance using `newInstance()`? – Tenfour04 Jan 18 '23 at 22:41
  • If I make contstructor private i cant access it in Adapter for Fragments `Cannot access '': it is private in 'Calculations'` – aescaesse Jan 18 '23 at 22:53
  • There should not be any reason to access the fragment constructor. That’s the purpose of the `newInstance()` function. The constructor is marked private to intentionally make it impossible to accidentally use it. Why would you need to create a new Fragment inside an adapter? – Tenfour04 Jan 18 '23 at 23:52
  • Its really hard to say, I was watching tutorials about tabs and that worked for me. I will just update question with all files included in my problem – aescaesse Jan 19 '23 at 00:17