HOW do you invalidate a fragment view?

1.6k Views Asked by At

Alright, so I have a fragment that displays user account information.

The way this works is it issues a request that may or may not touch the server, depending on what information is already cached.

When the information is available, there's a callback in which the fragment will update its textfields.

At that point, however, the UI needs be refreshed, so I'd like to issue some kind of invalidate...

Except that does perfectly nothing.

First, I noticed that this.view returns null. So rather than rely on that, I store the view I create in onCreateView explicitly.

Then after I updated the textfields, I call fragmentView.postInvalidate() ... which does nothing.

I also tried doAsync { uiThread { fragmentView.invalidate() } } ... which also does nothing.

Then I found this answer and tried

    val fragment = this
    doAsync { uiThread {
        activity!!.supportFragmentManager.beginTransaction().detach(fragment).commit()
        activity!!.supportFragmentManager.beginTransaction().attach(fragment).commit()
    }}

... which is even WORSE than not doing anything, because the other two ways will at least update the view if you switch to a different fragment and back, whereas this will perpetually refuse to display any useful information.

So maybe I can "cheat" by delaying the call to super::setUserVisibleHint until after I updated the values ... no, I can't.

What I can do, though, is I can force a redraw of the ui by toasting something. So that's what I am currently doing. But still, that can't be the solution.

How do I get a proper ui refresh?

Oh, and this is an android.support.v4.app.Fragment and I'm using an android.support.v4.app.FragmentStatePagerAdapter to switch between fragments, if that matters.

Full fragment code:

class AccountFragment : Fragment() {
    lateinit var fragmentView: View

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
        super.onCreateView(inflater, container, savedInstanceState)
        val view = inflater.inflate(R.layout.fragment_account, container, false)
        /*for some reason [this.view] remains null when we later try to use it, so we're explicitly storing a reference*/
        fragmentView = view
        return view
    }

    override fun setUserVisibleHint(isVisibleToUser: Boolean) {
        if(isVisibleToUser)setAccountInformation()
        super.setUserVisibleHint(isVisibleToUser)
    }

    private fun setAccountInformation(){
        val um = DataConfig.getUserManager()
        val au = um.getActiveUser()

        um.getUserInfo(au, Callback(
                onResponse = {when(it){
                    is SuccessfulAccountGetResponse -> {
                        val info = it.result

                        usernameText.text = info.name
                        uuidText.text = info.uuid
                        adminText.text = if(info.isAdmin) "YES" else "NO"

                        createdText.text = info.created.toString()
                        lastLoginText.text = info.lastLogin?.toString() ?: "-"

                        //now actually force the new information to show up
                        refreshUI()
                    }
                    else -> doAsync { uiThread{ activity?.longToast("could not get information") } }
                }},
                onError = {doAsync { uiThread { activity?.longToast("error: $it") } }}
        ))
    }

    /** forces a refresh, making changes visible*/
    private fun refreshUI(){
        /*hack: force a redraw by toasting */
        doAsync { uiThread { activity?.toast("updating values") } }

        //THIS does absolutely nothing: //TODO: figure out why
//        fragmentView.postInvalidate()
    }
}
0

There are 0 best solutions below