in androidx.fragment.app.Fragment,setUserVisibleHint()is Deprecated,and not executed,why?
Asked Answered
C

7

44
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
    super.setUserVisibleHint(isVisibleToUser);
    if (getUserVisibleHint()) {
        isVisible = true;
        onVisible();
    } else {
        isVisible = false;
        onInVisible();
    }
}

I found that this part of the code is not executed.

Courtesan answered 11/9, 2019 at 9:15 Comment(0)
A
81

They just changed API in Fragments.

If you use this method to limit fragments lifecycle:

You can now set a max Lifecycle state for a Fragment by calling setMaxLifecycle() on a FragmentTransaction. This replaces the now deprecated setUserVisibleHint().

Source: https://developer.android.com/jetpack/androidx/releases/fragment#1.1.0-alpha07 .

If you need this method because you try to detect which fragment is currently visible in ViewPager. You can now just use onResume and onPause methods instead but before that you should change default behaviour in FragmentPagerAdapter constructor.

Like this:

FragmentPagerAdapter(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT)

Edit: Because FragmentPagerAdapter is deprecated as well it is better to use ViewPager2 now. In case of ViewPager2 it is default behaviour and we can use onResume and onPause methods to know which fragment is currently visible.

Altorilievo answered 11/9, 2019 at 9:48 Comment(7)
And what if fragment isn't a part of ViewPager? I need to notify other classes that fragment show up or disappear on screen. Any solution for that?Ague
@Ague any lead ?Cinereous
@Rajat Sangrame: In one activity I have playback fragment and over them controls fragment and settings fragment. I need to know if controls fragment is still visible and base on that adjust settings fragment. UX requirements.Ague
I have 3 fragments always in memory. So when the activity is resumed, the fragments' onResume is also called. I want to do analytics for the count metric how many times a fragment is actually shown to the user. For that, I need to know how many times a fragment is actually switched to and not how many times it was resumed.Pagandom
@Can I this approach with FragmentStateAdapter?Jenna
what about viewpager2 and FragmentStateAdapter ? is there a solution?Andry
FragmentPagerAdapter is also deprecated. That is just crazy, omg GOOGLE.Mylan
B
17

Now in AndroidX method setUserVisibleHint(boolean isVisibleToUser) is deprecated and if you take a look into documentation, it says:

You can now set a max Lifecycle state for a Fragment by calling setMaxLifecycle() on a FragmentTransaction. This replaces the now deprecated setUserVisibleHint(). FragmentPagerAdapter and FragmentStatePagerAdapter have a new constructor that allows you to switch to the new behavior.

So basically when you use this approach in FragmentTransaction:

.getSupportFragmentManager()
            .beginTransaction()
            .setMaxLifecycle(fragment, Lifecycle.State.STARTED);

would be equivalent to setUserVisibleHint(false) and:

.getSupportFragmentManager()
            .beginTransaction()
            .setMaxLifecycle(fragment, Lifecycle.State.RESUMED);

would be equivalent to: setUserVisibleHint(true)

Burrill answered 11/9, 2019 at 9:37 Comment(0)
J
13

This answer assumes that you are using FragmentStatePagerAdapter

On the new version of androidx.fragment (from 1.1.0+), Fragment.setUserVisibleHint will still get called if your FragmentStatePagerAdapter is using the old behavior, specified by BEHAVIOR_SET_USER_VISIBLE_HINT.

If you have constructed your FragmentStatePagerAdapter and passed BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT, then Fragment.setUserVisibleHint will no longer be called inside FragmentStatePagerAdapter.instantiateItem.

NOTE: you can still call the deprecated Fragment.getUserVisibleHint if you have specified BEHAVIOR_SET_USER_VISIBLE_HINT in your FragmentStatePagerAdapter, but be advised that it will return true even though Fragment.isResumed() will return false.


The androidx project is open source. By taking a look at the latest code on master, you can see that an if has been added around setUserVisibleHint inside instantiateItem: https://android.googlesource.com/platform/frameworks/support/+/androidx-master-dev/fragment/fragment/src/main/java/androidx/fragment/app/FragmentStatePagerAdapter.java#195

  • TL;DR:

1.0.x:

fragment.setMenuVisibility(false);
fragment.setUserVisibleHint(false);

1.1.0+:

fragment.setMenuVisibility(false);
if (mBehavior == BEHAVIOR_SET_USER_VISIBLE_HINT) {
    fragment.setUserVisibleHint(false);
}
Jellify answered 28/1, 2020 at 20:21 Comment(0)
R
1

There is a new method in fragment itself

Return true if the fragment is in the resumed state. This is true for the duration of onResume() and onPause() as well.

package androidx.fragment.app;

final public boolean isResumed() {
   return mState >= RESUMED;
}

You can call this method by typing:

if (isResumed) {
    // your code here
}
Reading answered 30/8, 2021 at 16:36 Comment(1)
Works well with FragmentStatePagerReading
J
1

A simple explanation for those who don't understand.

in your ViewPagerAdapter must be like this, constructor send super

super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);

The whole code looks like this.

private static class ViewPagerAdapter extends FragmentPagerAdapter {

    private final List<Fragment> fragments = new ArrayList<>();
    private final List<String> fragmentTitle = new ArrayList<>();

    public ViewPagerAdapter(@NonNull FragmentManager fm) {
        super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
    }

    public void addFragment(Fragment fragment, String title) {
        fragments.add(fragment);
        fragmentTitle.add(title);
    }

    @NonNull
    @Override
    public Fragment getItem(int position) {
        return fragments.get(position);
    }

    @Override
    public int getCount() {
        return fragments.size();
    }

    @Nullable
    @Override
    public CharSequence getPageTitle(int position) {
        return fragmentTitle.get(position);
    }

}

In this way, the onresume method in the fragment will work.

Jarvey answered 23/12, 2021 at 14:21 Comment(0)
S
1

I use the below in kotlin to handle code when a fragment is visible to the user.

Class ZeroFragment : Fragment() {

//Below onResume executed when this fragment is displayed to the user.
    override fun onResume() {
        super.onResume()
        readData()
    }
}
Strow answered 12/8, 2022 at 10:34 Comment(0)
N
0

If you are using FragmentStateAdapter, you should use registerFragmentTransactionCallback method to check lifecycle of a Fragment's interaction with ViewPager2.

I need to have back stack of ViewPager2 as i asked here, and implementation is

init {
    // Add a FragmentTransactionCallback to handle changing
    // the primary navigation fragment
    registerFragmentTransactionCallback(object : FragmentTransactionCallback() {

        override fun onFragmentMaxLifecyclePreUpdated(
            fragment: Fragment,
            maxLifecycleState: Lifecycle.State
        ) = if (maxLifecycleState == Lifecycle.State.RESUMED) {

            // This fragment is becoming the active Fragment - set it to
            // the primary navigation fragment in the OnPostEventListener
            OnPostEventListener {
                fragment.parentFragmentManager.commitNow {
                    setPrimaryNavigationFragment(fragment)
                }
            }

        } else {
            super.onFragmentMaxLifecyclePreUpdated(fragment, maxLifecycleState)
        }
    })
}

My question was to set back stack for fragments of ViewPager2 with each it's own back stack only when they are visible since having OnBackPressed on every fragment caused undesired pop stack, you can modify or use other methods of FragmentTransactionCallback.

However, i don't know it's false positive or not but Leak Canary shows FragmentMaxLifeCycleEnforcer instance leaking sometimes, so if you face a problem like this, you might need to unregister FragmentTransactionCallback in onPause or other relevant method

Netty answered 11/7, 2020 at 9:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.