1

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.

Dreamers Org
  • 1,151
  • 3
  • 12
  • 30
  • I don't understand your issue. Why not using with the Navigation Drawer directly? I mean, you know that the user has selected the Fragment B, so you know what has happenned. So what not to put your logic in the onClickListener of the Navigation Drawer. Like that you'll know what is displayed or not without trying to guess it from the Fragment's state. But are you sure your Fragment A is not displayed btw? Can you had the code of the Navigation Drawer listener? – Eselfar Jul 26 '17 at 16:36
  • 1
    Please post more code, Where are you calling this function? what does your activity look like? – Derek Jul 26 '17 at 16:44
  • @Eselfar i have added more info onto why i need it like this. – Dreamers Org Jul 26 '17 at 21:42
  • So why not calling `.stop` in the `onPause` of the Fragment as the fragment is paused when not displayed? Still don't understand your issue. – Eselfar Jul 26 '17 at 22:20
  • @Eselfar onPause only gets called in a fragment when its activity onPause get called. It doesnt get called when its out of view – Dreamers Org Jul 26 '17 at 23:51
  • 1
    How do you switch your fragment? – BakaWaii Jul 27 '17 at 07:18
  • @BakaWaii i updated with code on how i switch fragments – Dreamers Org Jul 28 '17 at 04:31
  • Did you try to put the code in `onCreateView()` and `onDestroyView()`? I think it should work. – BakaWaii Jul 28 '17 at 04:48
  • you are calling both `supportFragmentManager` and `fragmentManager`, is that supported? also `setUserVisibleHint` had issues in several early SDK levels, not sure which level called it fixed. – Les Jul 30 '17 at 01:23
  • you could have a look aat one of my answers [here](https://stackoverflow.com/questions/45384809/configure-a-fragment-to-be-shown-first-when-application-is-started/45384948#45384948) – Akshay Nandwana Aug 01 '17 at 17:10

2 Answers2

3

You can simply call

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

or another way is

public boolean isFragmentUIActive() {
    return isAdded() && !isDetached() && !isRemoving();
}
Tobias Alt
  • 453
  • 1
  • 3
  • 11
  • Since this is not an overriden method i would have to have an infinite loop constantly checking if the fragment is visible or not, this would not be ideal. – Dreamers Org Jul 26 '17 at 21:43
2

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.

Les
  • 10,335
  • 4
  • 40
  • 60
  • I would add specifically to use the PreferenceFragmentCompat to use with the supportfragmentmanager. That was the issue since i was using both Native and Support fragment managers – Dreamers Org Aug 01 '17 at 19:09