如何确定片段何时在ViewPager中可见


754

问题:片段onResume()ViewPager之前的片段变成实际可见被激发。

例如,我有2个带有ViewPager和的片段FragmentPagerAdapter。第二个片段仅适用于授权用户,我需要让用户在该片段可见时登录(使用警报对话框)。

但是,ViewPager当第一个片段可见时,它会创建第二个片段,以便缓存第二个片段,并在用户开始滑动时使其可见。

因此,onResume()事件早在它可见之前就在第二个片段中触发。这就是为什么我试图找到一个事件,该事件在第二个片段变得可见时触发,并在适当的时候显示一个对话框。

如何才能做到这一点?


18
“我有两个带有ViewPager和FragmentPagerAdapter的片段。第二个片段只能供授权用户使用,并且当该片段变得可见时,我应该要求使用登录(警报对话框)。” -恕我直言,这真是糟糕的UX。因为用户水平滑动而弹出一个对话框,这会使我给您在Play商店中的一星评级。
CommonsWare,2012年

使用“登录”按钮仅在TextView中显示信息会更好吗?对于这种情况,您有什么解决方案?
4ntoine 2012年

5
“如果不显示数据,则无需加载数据。” -那么你不应该把它放在一个ViewPager。在两页寻呼机中,无论​​您是否喜欢,都将立即加载两个页面。的用户体验ViewPager应该是内容在滑动时立即存在,而不是在一段时间之后。这就是为什么ViewPager在可见内容之前初始化页面,以帮助确保用户体验的原因。
CommonsWare'Apr 11'2012

2
似乎ViewPager不够灵活,并且不允许关闭缓存,因为最小setOffscreenPageLimit为1:stackoverflow.com/questions/10073214/…。没有看到任何原因,并且预期的行为(在强制缓存的情况下)是在片段可见时创建片段BUT火灾片段的onResume()。
4ntoine 2012年

1
有点晚了,但是对于面临相同问题的任何人,您都可以尝试FragmentViewPager 库(我是作者),该库可以解决此问题并提供一些额外的功能。有关示例,请检查项目的GitHub页面或此stackoverflow答案
S. Brukhanda

Answers:


582

如何确定片段何时在ViewPager中可见

你可以通过覆盖以下setUserVisibleHintFragment

public class MyFragment extends Fragment {
    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        if (isVisibleToUser) {
        }
        else {
        }
    }
}

7
感谢今天的Android支持库更新(rev 11),最终解决了用户可见的提示问题。现在可以对ViewPager使用用户可见的提示了。
绿洲

58
我发现在调用onCreateView之前先调用setUserVisibleHint方法,这将使跟踪任何初始化变得困难。
AndroidDev 2015年

11
@AndroidDev如果要在提示为true时运行一些代码,但需要已初始化视图树,则只需包装该代码块isResumed()即可避免NPE。一直为我工作。
拉维·萨普利雅尔

13
荒谬的是,默认情况下,SDK应该提供的东西有很多不同的技巧。
Mike6679

15
setUserVisibleHint现在已弃用
SR

525

更新:Android支持库(rev 11)最终解决了用户可见的提示问题,现在,如果您将支持库用于片段,则可以按照gorn的答案安全地使用getUserVisibleHint()或覆盖setUserVisibleHint()捕获更改。

更新1这是的一个小问题getUserVisibleHint()。此值为默认值true

// Hint provided by the app that this fragment is currently visible to the user.
boolean mUserVisibleHint = true;

因此,当您尝试在setUserVisibleHint()调用之前尝试使用它时,可能会出现问题。作为解决方法,您可以在这样的onCreate方法中设置值。

public void onCreate(@Nullable Bundle savedInstanceState) {
    setUserVisibleHint(false);

过时的答案:

在大多数使用情况下,一次ViewPager只显示一页,但如果在中使用FragmentStatePagerAdapter,则预缓存的片段也会置于“可见”状态(实际上是不可见)Android Support Library pre-r11

我覆盖:

public class MyFragment extends Fragment {
    @Override
    public void setMenuVisibility(final boolean visible) {
        super.setMenuVisibility(visible);
        if (visible) {
            // ...
        }
    }
   // ...
}

要捕获片段的焦点状态,我认为这是“可见性”的最合适状态,因为ViewPager中只有一个片段实际上可以将其菜单项与父活动的项一起放置。


73
请小心使用getActivity(),在首次启动时它将为null。
vovkab 2012年

10
请注意,setUserVisibleHint()在FragmentPagerAdapter中一直正常工作。
louielouie 2013年

8
这个解决方案(还有破烂的解决方案)有点滞后。如果viewpager快速滑动并达到多个时间,则将延迟调用此方法(当片段不再可见/不可见时)。
AsafK 2014年

3
我正在获得truegetUserVisibleHint()onCreateOptionsMenu的调用时间,并且在setUserVisibleHint调用时,菜单似乎尚未创建。最终,当我只想要可见片段中的菜单时,便得到了两个选项。在这方面有什么建议吗?
cYrixmorten 2014年

13
setUserVisibleHint现在已弃用
SR

143

这似乎可以恢复onResume()您期望的正常行为。按下Home键离开应用程序,然后重新进入应用程序,可以很好地发挥作用。 onResume()连续两次不被调用。

@Override
public void setUserVisibleHint(boolean visible)
{
    super.setUserVisibleHint(visible);
    if (visible && isResumed())
    {
        //Only manually call onResume if fragment is already visible
        //Otherwise allow natural fragment lifecycle to call onResume
        onResume();
    }
}

@Override
public void onResume()
{
    super.onResume();
    if (!getUserVisibleHint())
    {
        return;
    }

    //INSERT CUSTOM CODE HERE
}

4
正是我想要的。解决了setUserVisibleHint之前被调用的问题,如果应用程序先后台运行又不会前台调用onCreateViewsetUserVisibleHint则不会被调用。太棒了!谢谢!
Alex

11
我认为自己调用onResume是一个很糟糕的主意,但是否则,您需要回答此问题的所有内容都在setUserVisibleHint函数中!
Quentin G.

4
我宁愿实现一个普通的onVisibleToUser()方法,并从onResume()和从中setUserVisibleHint(boolean)调用该方法,而不是调用onResume()自己并干扰生命周期回调。否则,我认为这种方法效果很好,谢谢!
Stephan Henningsen,

1
是的,我同意以上评论。通常,请尝试避免从类中的公共方法中调用公共方法。创建一个私有方法,并从两个公共方法中调用它。
JohanFranzén18年

4
setUserVisibleHint已弃用!
哈米德·雷扎

70

这是使用的另一种方式onPageChangeListener

  ViewPager pager = (ViewPager) findByViewId(R.id.viewpager);
  FragmentPagerAdapter adapter = new FragmentPageAdapter(getFragmentManager);
  pager.setAdapter(adapter);
  pager.setOnPageChangeListener(new OnPageChangeListener() {

  public void onPageSelected(int pageNumber) {
    // Just define a callback method in your fragment and call it like this! 
    adapter.getItem(pageNumber).imVisible();

  }

  public void onPageScrolled(int arg0, float arg1, int arg2) {
    // TODO Auto-generated method stub

  }

  public void onPageScrollStateChanged(int arg0) {
    // TODO Auto-generated method stub

  }
});

1
此解决方案效果很好,谢谢。对于使用(v4)片段支持包为较旧平台进行构建的人员,这应该是推荐的解决方案
Frank Yin

7
值得注意的是,如果您使用FragmentStatePagerAdapter并在getItem()上实例化一个新的Fragment实例,这将无法给您预期的结果,因为您只能在新的片段上设置状态,而不是在viewpager上设置状态
本·皮尔森

1
如果您希望在片段可见时执行某些操作,则此解决方案很好。它不会保持片段的可见状态(也就是说,它不知道片段何时不可见)。
AsafK 2014年

我是同一类型的问题。请帮助我解决问题。我的帖子:stackoverflow.com/questions/23115283/...
Jeeten帕尔马

这为我在片段的iamVisible方法中使用的适配器提供了空指针异常。我试图仅在可见片段时设置从网络接收的数据。
zaphod100.10 2015年

58

setUserVisibleHint()有时在事前 onCreateView()或事后被调用会造成麻烦。

为了克服这个问题,您还需要检查isResumed()内部setUserVisibleHint()方法。但是在这种情况下,我意识到只有在Fragment恢复并可见时才setUserVisibleHint()被调用,而不是在创建时被调用。

因此,如果您想在Fragment为时更新某些内容visible,请将更新函数同时放在onCreate()和中setUserVisibleHint()

@Override
public View onCreateView(...){
    ...
    myUIUpdate();
    ...        
}
  ....
@Override
public void setUserVisibleHint(boolean visible){
    super.setUserVisibleHint(visible);
    if (visible && isResumed()){
        myUIUpdate();
    }
}

更新:我仍然意识到myUIUpdate()有时会被两次调用,原因是,如果您有3个选项卡,并且此代码位于第二个选项卡上,则当您第一次打开第一个选项卡时,即使它不可见并被myUIUpdate()调用,也会创建第二个选项卡。然后,当您滑动到第二个选项卡时,会myUIUpdate()if (visible && isResumed())调用,结果myUIUpdate()可能在一秒钟内被调用两次。

另一个问题!visiblesetUserVisibleHint被称为两个1)当你去片段屏幕和2)被创建之前,出来当你切换到片段屏幕第一次。

解:

private boolean fragmentResume=false;
private boolean fragmentVisible=false;
private boolean fragmentOnCreated=false;
...

@Override
public View onCreateView(...){
    ...
    //Initialize variables
    if (!fragmentResume && fragmentVisible){   //only when first time fragment is created
        myUIUpdate();
    }
    ...        
}

@Override
public void setUserVisibleHint(boolean visible){
    super.setUserVisibleHint(visible);
    if (visible && isResumed()){   // only at fragment screen is resumed
        fragmentResume=true;
        fragmentVisible=false;
        fragmentOnCreated=true;
        myUIUpdate();
    }else  if (visible){        // only at fragment onCreated
        fragmentResume=false;
        fragmentVisible=true;
        fragmentOnCreated=true;
    }
    else if(!visible && fragmentOnCreated){// only when you go out of fragment screen
        fragmentVisible=false;
        fragmentResume=false;
    }
}

说明:

fragmentResumefragmentVisible:确保仅在创建片段并显示片段时调用myUIUpdate()in onCreateView(),而不在恢复时调用in 。当您位于第一个选项卡时,即使看不见第二个选项卡,它也解决了问题。这可以解决该问题,并在时检查片段屏幕是否可见onCreate

fragmentOnCreated:确保片段不可见,并且在您首次创建片段时不被调用。因此,现在仅当您从片段中划出时才调用此if子句。

更新 你可以把所有这些代码BaseFragment代码像这样和覆盖方法。


1
您的解决方案可以正常工作,除了在一种情况下,当片段正常打开时,然后单击某个按钮以将其替换为另一个片段,然后单击“后退”以从后堆栈中弹出新片段,这种情况setUserVisibleHint没有得到调用。 !和内部onCreateView方法fragmentVisiblefalse!?所以片段显示为空..!有什么想法吗。?
Alaa AbuZarifa

28

为了检测FragmentViewPager可见的,我敢肯定那只是使用 setUserVisibleHint是不够的。
这是我检查片段是否可见或不可见的解决方案。首先启动viewpager时,在页面之间切换,转到另一个活动/片段/背景/前景`

public class BaseFragmentHelpLoadDataWhenVisible extends Fragment {
    protected boolean mIsVisibleToUser; // you can see this variable may absolutely <=> getUserVisibleHint() but it not. Currently, after many test I find that

    /**
     * This method will be called when viewpager creates fragment and when we go to this fragment background or another activity or fragment
     * NOT called when we switch between each page in ViewPager
     */
    @Override
    public void onStart() {
        super.onStart();
        if (mIsVisibleToUser) {
            onVisible();
        }
    }

    @Override
    public void onStop() {
        super.onStop();
        if (mIsVisibleToUser) {
            onInVisible();
        }
    }

    /**
     * This method will called at first time viewpager created and when we switch between each page
     * NOT called when we go to background or another activity (fragment) when we go back
     */
    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        mIsVisibleToUser = isVisibleToUser;
        if (isResumed()) { // fragment have created
            if (mIsVisibleToUser) {
                onVisible();
            } else {
                onInVisible();
            }
        }
    }

    public void onVisible() {
        Toast.makeText(getActivity(), TAG + "visible", Toast.LENGTH_SHORT).show();
    }

    public void onInVisible() {
        Toast.makeText(getActivity(), TAG + "invisible", Toast.LENGTH_SHORT).show();
    }
}

说明 您可以仔细检查下面的logcat,然后我想您可能知道为什么该解决方案会起作用

首次发射

Fragment1: setUserVisibleHint: isVisibleToUser=false isResumed=false
Fragment2: setUserVisibleHint: isVisibleToUser=false isResumed=false
Fragment3: setUserVisibleHint: isVisibleToUser=false isResumed=false
Fragment1: setUserVisibleHint: isVisibleToUser=true isResumed=false // AT THIS TIME isVisibleToUser=true but fragment still not created. If you do something with View here, you will receive exception
Fragment1: onCreateView
Fragment1: onStart mIsVisibleToUser=true
Fragment2: onCreateView
Fragment3: onCreateView
Fragment2: onStart mIsVisibleToUser=false
Fragment3: onStart mIsVisibleToUser=false

转到第2页

Fragment1: setUserVisibleHint: isVisibleToUser=false isResumed=true
Fragment2: setUserVisibleHint: isVisibleToUser=true isResumed=true

转到第3页

Fragment2: setUserVisibleHint: isVisibleToUser=false isResumed=true
Fragment3: setUserVisibleHint: isVisibleToUser=true isResumed=true

转到背景:

Fragment1: onStop mIsVisibleToUser=false
Fragment2: onStop mIsVisibleToUser=false
Fragment3: onStop mIsVisibleToUser=true

转到前台

Fragment1: onStart mIsVisibleToUser=false
Fragment2: onStart mIsVisibleToUser=false
Fragment3: onStart mIsVisibleToUser=true

此处的DEMO项目

希望对你有帮助


1
当片段具有另一个也包含片段的视图分页器时,它将不起作用。
Shihab Uddin

抱歉,由于我没有足够的时间,我目前无法发布答案,如果可能的话,请在github.com/PhanVanLinh/AndroidViewPagerSkeleton/tree/master上参阅我的git来了解您的问题。SubChildContainerFragment用于检测a fragment has another view pager which also consists fragment。您可以混合SubChildContainerFragmentChildContainerFragment上一堂课。希望对您有所帮助。我稍后会发布完整答案
Phan Van Linh

26
package com.example.com.ui.fragment;


import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import com.example.com.R;

public class SubscribeFragment extends Fragment {

    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_subscribe, container, false);
        return view;
    }

    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);

        if (isVisibleToUser) {
            // called here
        }
    }

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
    }
}

2
这应该是公认的答案。也可以与android.app.Fragment一起使用,这是一个巨大的好处。
RajV

setUserVisibleHint不推荐使用
ekashking '19

23

ViewPager2ViewPager从版本androidx.fragment:fragment:1.1.0你可以使用onPauseonResume回调,以确定哪些片段是目前用户可见。onResume片段变为可见时以及片段onPause停止可见时调用回调。

对于ViewPager2,它是默认行为,但是可以ViewPager很容易地为旧商品启用相同的行为。

要在第一个ViewPager中启用此行为,您必须将FragmentPagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT参数作为FragmentPagerAdapter构造函数的第二个参数传递。

FragmentPagerAdapter(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT)

注意:android jetpack的Fragment新版本中不推荐使用带有一个参数的setUserVisibleHint()方法和FragmentPagerAdapter构造函数。


1
感谢您的好解决方案。我一直在寻找这个。伟大的工作
Anurag Srivastava

1
对于ViewPager2,选项BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT不是默认行为,您需要手动设置。设置此选项时,您的解决方案对我有用。谢谢。
德里安

1
从2020年4月开始,这是更新的解决方案,并且像魅力一样起作用。
Prakash

1
@ 4ntoine请考虑接受此答案作为正确答案,因为到目前为止,这是正确的,并且大多数答案都使用了不赞成使用的setUserVisibleHint方法
Jorn Rigter,

16

覆盖setPrimaryItem()FragmentPagerAdapter子类。我使用这种方法,并且效果很好。

@Override
public void setPrimaryItem(ViewGroup container, int position, Object object) {
    // This is what calls setMenuVisibility() on the fragments
    super.setPrimaryItem(container, position, object);

    if (object instanceof MyWhizBangFragment) {
        MyWhizBangFragment fragment = (MyWhizBangFragment) object;
        fragment.doTheThingYouNeedToDoOnBecomingVisible();
    }
}

1
这几乎可行,但是NPE出现问题,因此被多次调用。在下面发布替代项!
Gober


@Gober第一次保存对象对象,并像FragmentStateAdapter在setPrimaryItem()方法中一样检查并忽略对同一对象对象的后续调用
wanglugao

12

覆盖Fragment.onHiddenChanged()了点。

public void onHiddenChanged(boolean hidden)

isHidden()片段的隐藏状态(由返回)已更改时调用。片段开始时没有隐藏;每当片段从该状态更改状态时,将调用此方法。

参数
hidden- boolean:如果片段现在被隐藏,则为true;如果不可见,则为false。


3
此方法现在已不推荐使用,不能再使用
bluewhile

4
@bluewhile在哪里看到它已被弃用?developer.android.com/reference/android/app/…–
Cel

1
我刚刚成功地使用了它,文档似乎没有表明它已被弃用。
Trevor 2012年

1
@bluewhile,在4.1(api 16)中尚未被弃用。
2012年

8
我正在使用API​​级别19,并且可以确认虽然不建议弃用它,但它不能像宣传的那样工作。例如,当片段被另一个片段隐藏时,不会调用onHiddenChanged。

4

我发现只有在真正可见的片段的情况下才调用onCreateOptionsMenuonPrepareOptionsMenu方法。我找不到任何行为像这样的方法,我也尝试过,但不适用于这种情况,例如,我需要在method中初始化一个变量。OnPageChangeListeneronCreate

因此,可以将这两种方法用作解决此问题的方法,特别是对于少量工作和短期工作。

我认为,这是更好的解决方案,但不是最好的解决方案。我将使用它,但同时等待更好的解决方案。

问候。


2

克里斯·拉尔森(Kris larson)在这里发布的另一种解决方案,覆盖了pageradapter中的setPrimaryItem,几乎对我有用。但是,每次设置都会多次调用此方法。我也从片段中的视图等获得了NPE,因为在此方法的前几次调用中还没有准备好。通过以下更改,这对我有效:

private int mCurrentPosition = -1;

@Override
public void setPrimaryItem(ViewGroup container, int position, Object object) {
    super.setPrimaryItem(container, position, object);

    if (position == mCurrentPosition) {
        return;
    }

    if (object instanceof MyWhizBangFragment) {
        MyWhizBangFragment fragment = (MyWhizBangFragment) object;

        if (fragment.isResumed()) {
            mCurrentPosition = position;
            fragment.doTheThingYouNeedToDoOnBecomingVisible();
        }
    }
}

2

在片段内添加以下代码

@Override
public void setMenuVisibility(final boolean visible) 
 {
    super.setMenuVisibility(visible);
    if (visible && isResumed()) 
     {

     }
}

这仅适用于ViewPagerFragments。不适用于正常活动的碎片。
AndroidGuy

2

使用FragmentStatePagerAdapters和3个标签时,我遇到了相同的问题。每当单击第一个选项卡时,我都必须显示Dilaog,然后在单击其他选项卡时将其隐藏。

setUserVisibleHint()单独覆盖无助于找到当前可见的片段。

从第3个选项卡单击----->第一个选项卡。它为第二个片段和第一个片段触发了两次。我将其与isResumed()方法结合在一起。

    @Override
public void setUserVisibleHint(boolean isVisibleToUser) {
    super.setUserVisibleHint(isVisibleToUser);
    isVisible = isVisibleToUser;

    // Make sure that fragment is currently visible
    if (!isVisible && isResumed()) {
        // Call code when Fragment not visible
    } else if (isVisible && isResumed()) {
       // Call code when Fragment becomes visible.
    }

}

2

对于MVP,我们有一个特殊情况:片段需要通知演示者该视图已可见,并且演示者由Dagger注入fragment.onAttach()

setUserVisibleHint()还不够,我们已经检测到需要解决的3种不同情况(onAttach()提到了这些情况,以便您知道演示者何时有空):

  1. 片段刚刚创建。系统进行以下调用:

    setUserVisibleHint() // before fragment's lifecycle calls, so presenter is null
    onAttach()
    ...
    onResume()
  2. 已创建的片段,并按下主页按钮。将应用程序还原到前台时,这称为:

    onResume()
  3. 方向变化:

    onAttach() // presenter available
    onResume()
    setUserVisibleHint()

我们只希望可见性提示一次到达演示者,所以这是我们的操作方式:

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View root = inflater.inflate(R.layout.fragment_list, container, false);
    setHasOptionsMenu(true);

    if (savedInstanceState != null) {
        lastOrientation = savedInstanceState.getInt(STATE_LAST_ORIENTATION,
              getResources().getConfiguration().orientation);
    } else {
        lastOrientation = getResources().getConfiguration().orientation;
    }

    return root;
}

@Override
public void onResume() {
    super.onResume();
    presenter.onResume();

    int orientation = getResources().getConfiguration().orientation;
    if (orientation == lastOrientation) {
        if (getUserVisibleHint()) {
            presenter.onViewBecomesVisible();
        }
    }
    lastOrientation = orientation;
}

@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
    super.setUserVisibleHint(isVisibleToUser);
    if (presenter != null && isResumed() && isVisibleToUser) {
        presenter.onViewBecomesVisible();
    }
}

@Override public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putInt(STATE_LAST_ORIENTATION, lastOrientation);
}

2

通过检测focused view

这对我有用

public static boolean isFragmentVisible(Fragment fragment) {
    Activity activity = fragment.getActivity();
    View focusedView = fragment.getView().findFocus();
    return activity != null
            && focusedView != null
            && focusedView == activity.getWindow().getDecorView().findFocus();
}

1

当我试图在屏幕上查看供用户查看的分页中的片段时触发计时器触发时遇到了这个问题。

计时器总是在用户看到片段之前就开始计时。这是因为onResume()片段中的方法是在看到片段之前被调用的。

我的解决方案是对onResume()方法进行检查。当片段8是视图分页器的当前片段时,我想调用某个方法'foo()'。

@Override
public void onResume() {
    super.onResume();
    if(viewPager.getCurrentItem() == 8){
        foo();
        //Your code here. Executed when fragment is seen by user.
    }
}

希望这可以帮助。我已经看到了很多问题。这似乎是我所见过的最简单的解决方案。许多其他与较低的API等不兼容。




1

我支持带有子片段的SectionsPagerAdapter,因此,经过很多头痛之后,我终于有了基于该主题的解决方案的工作版本:

public abstract class BaseFragment extends Fragment {

    private boolean visible;
    private boolean visibilityHintChanged;

    /**
     * Called when the visibility of the fragment changed
     */
    protected void onVisibilityChanged(View view, boolean visible) {

    }

    private void triggerVisibilityChangedIfNeeded(boolean visible) {
        if (this.visible == visible || getActivity() == null || getView() == null) {
            return;
        }
        this.visible = visible;
        onVisibilityChanged(getView(), visible);
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (!visibilityHintChanged) {
            setUserVisibleHint(false);
        }
    }

    @Override
    public void onResume() {
        super.onResume();
        if (getUserVisibleHint() && !isHidden()) {
            triggerVisibilityChangedIfNeeded(true);
        }
    }

    @Override
    public void onHiddenChanged(boolean hidden) {
        super.onHiddenChanged(hidden);
        triggerVisibilityChangedIfNeeded(!hidden);
    }

    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        visibilityHintChanged = true;
        if (isVisibleToUser && isResumed() && !isHidden()) {
            triggerVisibilityChangedIfNeeded(true);
        } else if (!isVisibleToUser) {
            triggerVisibilityChangedIfNeeded(false);
        }
    }

    @Override
    public void onPause() {
        super.onPause();
        triggerVisibilityChangedIfNeeded(false);
    }

    @Override
    public void onStop() {
        super.onStop();
        triggerVisibilityChangedIfNeeded(false);
    }

    protected boolean isReallyVisible() {
        return visible;
    }
}

忘记提及了,一旦您将子片段添加到父片段,就设置fragment.setUserVisibleHint(true);。
Andoctorey

并且不要忘记使用getChildFragmentManager()而不是getFragmentManager()来添加子片段。
Andoctorey

0

请注意,setUserVisibleHint(false)未在活动/片段停止时调用。您仍然需要检查开始/停止以正确地register/unregister侦听任何侦听器/等。

此外,setUserVisibleHint(false)如果片段以不可见状态开始,您将获得;您不想去unregister那里,因为在这种情况下您从未注册过。

@Override
public void onStart() {
    super.onStart();

    if (getUserVisibleHint()) {
        // register
    }
}

@Override
public void onStop() {
    if (getUserVisibleHint()) {
        // unregister
    }

    super.onStop();
}

@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
    super.setUserVisibleHint(isVisibleToUser);

    if (isVisibleToUser && isResumed()) {
        // register

        if (!mHasBeenVisible) {
            mHasBeenVisible = true;
        }
    } else if (mHasBeenVisible){
        // unregister
    }
}

0

一种简单的实现方式是在转到该片段之前检查用户是否已登录。

在您的MainActivity中,您可以在onNavigationItemSelected方法内执行类似的操作

 case R.id.nav_profile_side:


                if (User_is_logged_in) {

                    fragmentManager.beginTransaction()
                            .replace(R.id.content_frame
                                    , new FragmentProfile())
                            .commit();
                }else {

                    ShowLoginOrRegisterDialog(fragmentManager);

                }

                break;

但是,如果您使用的是导航抽屉,尽管我们没有转到ProfileFragment,但抽屉中的选择将更改为Profile。

要将选择重置为当前选择,请运行以下代码

        navigationView.getMenu().getItem(0).setChecked(true);

-4

我覆盖了关联的FragmentStatePagerAdapter的Count方法,并使其返回的总计数减去要隐藏的页面数:

 public class MyAdapter : Android.Support.V13.App.FragmentStatePagerAdapter
 {   
     private List<Fragment> _fragments;

     public int TrimmedPages { get; set; }

     public MyAdapter(Android.App.FragmentManager fm) : base(fm) { }

     public MyAdapter(Android.App.FragmentManager fm, List<Android.App.Fragment> fragments) : base(fm)
     {
         _fragments = fragments;

         TrimmedPages = 0;
     }

     public override int Count
     {
         //get { return _fragments.Count; }
         get { return _fragments.Count - TrimmedPages; }
     }
 }

因此,如果最初在ViewPager中添加了3个片段,并且仅在满足某些条件之前才显示前2个片段,请通过将TrimmedPages设置为1来覆盖页数,并且它应仅显示前两个页面。

这对于末尾的页面效果很好,但对于开始或中间的页面却没有真正的帮助(尽管有很多方法可以做到这一点)。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.