ViewPager的Fragment嵌套子片段的setUserVisibleHint为true

时间:2018-02-01 17:48:18

标签: android android-fragments android-viewpager fragmentpageradapter setuservisiblehint

我有一个ViewPager有4个片段(a,b,c,d),c和d都有ViewPager个,基本上是嵌套的视图寻呼机。我附上了一张图片来说明我的布局。因此,当我选择片段B时,setUserVisibleHint(boolean)true且片段C中的setUserVisibleHint(boolean)false,这是预期的行为。但是,即使片段C中的setUserVisibleHint(boolean)false,片段C setUserVisibleHint(boolean)中的第一个片段中的ViewPager也为true

我不认为这是预期的行为。有人可以告诉你这里可能会发生什么吗?我非常感谢任何意见。

enter image description here

2 个答案:

答案 0 :(得分:0)

From docs of setUserVisibleHint(boolean):

Set a hint to the system about whether this fragment's UI is currently visible to the user. This hint defaults to true and is persistent across fragment instance state save and restore.

An app may set this to false to indicate that the fragment's UI is scrolled out of visibility or is otherwise not directly visible to the user. This may be used by the system to prioritize operations such as fragment lifecycle updates or loader ordering behavior.

As can be outlined from the docs:

  • This value defaults to true
  • This value is not being set automatically by framework. It's "the app" that may or may not set this flag.

If you look into the usages of setUserVisibleHint(boolean), you'll see following list:

enter image description here

Thus, only PagerAdapter implementations are calling that method. As you have outlined, PagerAdapter is correctly setting that flag for all the fragment that it possesses.

PagerAdapter is non-wiser about child fragments, that fragment C has. It's not the responsibility of PagerAdapter to set that flag for child fragment of fragment C. It's the responsibility of "the app" (in this case you) to manually set that boolean value.

答案 1 :(得分:0)

您应该检查父级是否为片段且可见或不为片段,然后开始逻辑。

public class Child1FragmentC extend Fragment {
    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        if (isVisibleToUser && isParentFragmentVisible())
            //TODO: start your logic 
    }

    /**
     *
     * @return true if the parent is fragment and visible or the parent isn't fragment
     */
    boolean isParentFragmentVisible() {
        return getParentFragment() == null || getParentFragment().getUserVisibleHint();
    }
}

现在,当平台将调用片段C上的第一个片段时,它将询问它的父片段C,并且它是不可见的,因此不会启动它的逻辑。

另外,当片段C的可见性更改时,平台不会通知片段c的第一个片段其可见性已更改,这是因为平台之前已告知它,并且由于父级是不可见的,所以我们没有启动逻辑。

因此,我们需要在其可见性更改时制作片段c,以通知其视图分页器中的当前可见片段(片段C的第一个片段)可见性已更改。

public class FragmentC extend Fragment {
    /**
     * Notify the current selected fragment inside view pager that it's visibility changed.
     *
     * @param isVisibleToUser
     */
    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        if (isVisibleToUser)
            //TODO: start your logic 

            //Notify the current fragment in view pager the the isVisibleToUser changed.
            Fragment currentFragment = getChildFragmentManager().findFragmentByTag(
                    "android:switcher:" + getViewPagerId() + ":" + getViewPager().getCurrentItem());
            if (currentFragment != null)
                currentFragment.setUserVisibleHint(isVisibleToUser);

    }
}

您应该处理的另一件事是在创建视图之前平台调用setUserVisibilityHint,因此您应该通过检查getView()!= null或任何其他条件来检查视图是否已准备就绪。

所以最终的代码将是这样。

public abstract class BaseFragment extend Fragment {

    /**
     * start logic if isVisibleToUser, isViewPrepared and isParentFragmentVisible equal true.
     *
     * => We check isViewPrepared equal true before starting logic because setUserVisibleHint
     * called before all fragment lifecycle callbacks. So we should check if the view is
     * prepared.
     * 
     * => We check if the parent is visible to solve if we have a fragment inside a view pager and 
     * that fragment also has view pager that contains children fragments but this fragment isn't
     * visible right now. the platform will call the first child fragment in the view pager that
     * it's visibility is true.
     *
     * @param isVisibleToUser
     */
    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        if (isVisibleToUser && isViewPrepared() && isParentFragmentVisible())
            //TODO: start your logic 
    }

    /**
     *
     * @return true if the parent is fragment and visible or the parent isn't fragment
     */
    boolean isParentFragmentVisible() {
        return getParentFragment() == null || getParentFragment().getUserVisibleHint();
    }

    /**
     *
     * @return true if the view isn't null
     */
    boolean isViewPrepared() {
        return getView() != null;
    }
}


/**
 * Use this base fragment when you have fragment that has view pager and you want to handle
 * when child fragments become actually visible to the user to start the logic.
 */
public abstract class BaseBootstrapHostViewPagerFragment extends BaseFragment {

    /**
     * Notify the current selected fragment inside view pager that it's setUserVisibleHint changed.
     */
    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);

        if (isViewPrepared()) {
            Fragment currentFragment = getChildFragmentManager().findFragmentByTag(
                    "android:switcher:" + getViewPagerId() + ":" + getViewPager().getCurrentItem());
            if (currentFragment != null)
                currentFragment.setUserVisibleHint(isVisibleToUser);
        }

    }

    public abstract int getViewPagerId();

    public abstract ViewPager getViewPager();
}

public FragmentC extend BaseBootstrapHostViewPagerFragment {....}

public Child1FragmentC extend BaseFragment {....}