How to tell when fragment is not visible in a Navi

2019-04-15 08:42发布

问题:

I am trying to tell when a user selects a different fragment in my navigation drawer. I was trying to use

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

How i switch fragments in my MainActivity:

override fun onNavigationItemSelected(item: MenuItem): Boolean {
        // Handle navigation view item clicks here.
        when (item.itemId) {

            R.id.nav_camera -> {
                // Handle the camera action
                val fragment: HomeFragment = HomeFragment()
                supportFragmentManager.beginTransaction().replace(R.id.content_main, fragment).commit()

            }
            R.id.nav_manage -> {
                val fragment: SettingFragment = SettingFragment()
                fragmentManager.beginTransaction().replace(R.id.content_main, fragment).commit()

            }
            R.id.nav_share -> {
                onInviteClicked()

            }
            R.id.nav_send -> {

                val emailIntent: Intent = Intent(android.content.Intent.ACTION_SEND)
                emailIntent.type = Constants.FEEDBACK_EMAIL_TYPE

                emailIntent.putExtra(android.content.Intent.EXTRA_EMAIL,
                        arrayOf(Constants.FEEDBACK_EMAIL_ADDRESS))

                emailIntent.putExtra(android.content.Intent.EXTRA_SUBJECT,
                        Constants.FEEDBACK_EMAIL_SUBJECT)

                startActivity(Intent.createChooser(
                        emailIntent, Constants.FEEDBACK_TITLE))


            }
        }

        val drawer: DrawerLayout = findViewById(R.id.drawer_layout)
        drawer.closeDrawer(GravityCompat.START)
        return true
    }

However this does not seem to get called at all. For example, in my NavigationDrawer activity, it shows Fragment A. The user opens the navigation drawer and selects Fragment B. setUserVisibleHint() does not get called in fragment A so my code can know it is no longer shown. I need my code that is isolated in fragment A to know when it is not shown so it can call .stop() on some variables. This is the same use case as onPause() in an activity.

回答1:

Here are a few things I can think of...

  1. Use a consistent fragment, either Support or Native, not both. And, some say the Support fragment is preferable (better maintained).
  2. Make sure the fragment container is not hard coded in XML. If you intend to replace a fragment, then the initial fragment should be loaded dynamically by your code (you typically will load into a FrameLayout using the id as your R.id.{frameLayoutId}).
  3. Do Use the Frament lifecycle events. onPause fires when you replace a fragment, so does onDetach. That will tell you when your old fragment is no longer visible (or will be invisible shortly). If it does not fire, then you have another issue in your code, possibly mixing of Fragment types, or a hardcoded fragment in XML?
  4. Use setUserVisibleHint only in a fragment pager, or be prepared to set it manually. this answer has a little more to say about the use of setUserVisibleHint. When using a pager, multiple fragments can be attached at once, so an additional means (some call it lifecycle event) was needed to tell if a fragment was "really, truly" visible, hence setUserVisibleHint was introduced.
  5. Bonus: If appropriate for your app, use the back stack for backing up by calling addToBackStack after replace. I add this mainly as an addition lifecycle item one would typically want in their app. The code looks like this...

    // to initialize your fragment container
    supportFragmentManager
            .beginTransaction()
            .add(R.id.content_fragment, fragment)
            .addToBackStack("blank")
            .commit()
    
    // to update your fragment container
    supportFragmentManager
            .beginTransaction()
            .replace(R.id.content_fragment, fragment)
            .addToBackStack("settings")
            .commit()
    
    //in your XML, it can be as simple as adding the FrameLayout below,
    // if you start with the Android Studio template for Navigation drawer,
    // you can replace the call that includes the "content_main" layout
    
    <!--<include layout="@layout/content_main" /> -->
    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/content_fragment" />
    

I hope this helps.



回答2:

You can simply call

if (myFragment.isVisible()) {...}

or another way is

public boolean isFragmentUIActive() {
    return isAdded() && !isDetached() && !isRemoving();
}


回答3:

Keep a global variable activeFragment. When a user clicks on a fragment, set activeFragment to that fragment.

...
val fragment: HomeFragment = HomeFragment()
activeFragment = HomeFragment

...
val fragment: SettingFragment = SettingFragment()
activeFragment = SettingFragment

Then,

if(FragmentA != activeFragment) {
    //Call stop here
}