Rajdeep
Rajdeep

Reputation: 2462

Fragment retaining old information (not updating properly)

I'm developing an app in Kotlin and have some issues when it comes to a specific fragment of mine called ChartsFragment. Basically what is happening is, when a navigation drawer item is clicked, I will query data from the database pass it to a fragment called ChartsFragmentto be displayed. I am using Graph View to plot my data on the fragment. Now, everything is working fine, just that for some reason, I have to click the navigation icon twice for the fragment to load the updated data. It will not update the data when the navigation icon is clicked once.

Here is a global variable used to store the data from database query:

private var completed = mutableListOf<String>()

Here is my code for when I query the data from the database:

/**
 * For firebase database
 * Read user progress from db
 */
private fun getProgress() {
    val uid = FirebaseAuth.getInstance().getCurrentUser()?.uid

    val rootRef = FirebaseDatabase.getInstance().getReference()
    val progressRef = rootRef.child("Users").child(uid!!).child("Progress")
    val valueEventListener = object : ValueEventListener {
        override fun onDataChange(dataSnapshot:DataSnapshot) {
            for(dSnapshot : DataSnapshot in dataSnapshot.getChildren()) {
                for(ds : DataSnapshot in dSnapshot.getChildren()) {
                    val key = ds.getKey() as String
                    completed.add(key)
                    Log.d(TAG2, key)
                }
            }
        }

        override fun onCancelled(@NonNull databaseError : DatabaseError) {
            Log.d(TAG2, databaseError.getMessage())
        }
    }
    progressRef.addListenerForSingleValueEvent(valueEventListener)
}

Here is my code for when the navigation drawer icon Charts is clicked:

        R.id.nav_chart -> {
            val currentUser = mAuth!!.currentUser
            if (currentUser != null) {
                getProgress()

                var basics = 0
                var loops = 0
                var functions = 0

                completed.forEach {
                    if (it == "Data Type" || it == "Operation" || it == "Condition" || it == "Null Safety") {
                        basics++
                    } else if (it == "For" || it == "Break" || it == "Repeat" || it == "When" || it == "While") {
                        loops++
                    } else if (it == "Call" || it == "Main" || it == "Recursion" || it == "Single Expression") {
                        functions++
                    }
                }
                completed.clear()

                val bundle = Bundle()
                bundle.putDouble("basics",basics.toDouble())
                bundle.putDouble("loops",loops.toDouble())
                bundle.putDouble("functions",functions.toDouble())
                chartFragment(bundle)

            } else {
                Toast.makeText(applicationContext, R.string.sign_in_to_track, Toast.LENGTH_SHORT).show()
            }
        }

Here is the code for when I load the fragment:

/**
 * For loading fragments
 */

fun chartFragment(bundle: Bundle){
    this.setTitle(R.string.navigation_drawer_chart)
    val fragmentManager = supportFragmentManager
    val transaction = fragmentManager.beginTransaction()
    val fragment = ChartFragment()
    fragment.setArguments(bundle)
    transaction.replace(R.id.frameLayout1, fragment)
    transaction.commit()
}

Here is my fragment's onCreateView:

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                          savedInstanceState: Bundle?): View? {

    val view = inflater.inflate(R.layout.fragment_chart, container, false)
    val graph = view?.findViewById(R.id.graph) as GraphView
    val basics = arguments!!.getDouble("basics")
    val loops = arguments!!.getDouble("loops")
    val functions  = arguments!!.getDouble("functions")
    val series = LineGraphSeries(arrayOf(
            DataPoint(0.0, 0.0),
            DataPoint(1.0, basics),
            DataPoint(2.0, loops),
            DataPoint(3.0, functions),
            DataPoint(4.0, 0.0)
    ))

    series.setColor(Color.GREEN)
    series.setDrawDataPoints(true)
    series.setAnimated(true)

    graph.addSeries(series)

    return view
}

I'm thinking is this some caching issue ? Is there a way to force click a navigation drawer's item just as a temporary solution ?

Update: I'm thinking may AsyncTasks would solve the issue but I have no idea on how to implement AsyncTasks tasks. It seems that the most probably reason is that getProgress() may be too slow and the program is executing the next line instead of waiting for getProgress() to finish.

Update: The size of completed I get after the first click is always the size of the previous click (not the updated data from db), on the second click, i will get the current size (current from the db). So, if my db has 0 items, on the first click, ill get 0 which is correct, then if I complete an activity in the app, the db is then updated with 1 item. So, if I click the chart icon now, I should get size 1 but I'm getting size 0. On the second click, Ill get size 1.

Update: So if I then go and complete another activity in the app, my db should now have 2 items. So, if i click the chart icon again, I should get size 2 but I am not still stuck at the previous size which is 1. On my second click for this, Ill get size 2.

Upvotes: 2

Views: 197

Answers (1)

B&#246; macht Blau
B&#246; macht Blau

Reputation: 13009

It seems that the most probably reason is that getProgress() may be too slow and the program is executing the next line instead of waiting for getProgress() to finish.

Yes, the program is executing the next line after getProgress() before completed is updated. But in fact getProgress() has finished. Its job was just to trigger the database call. The call itself is executed asynchronously (i.e. on another thread). The main thread will have access to the results as soon as onDataChange() is executed. This can only happen after the currently running function ( the one handling the icon click) has finished.

Since your logic is depending on the results of the database call, you should move the lines where you evaluate the new data to onDataChange().

Upvotes: 1

Related Questions