android - viewpager简书 - viewpager fragment缓存




如何确定Fragment何时在ViewPager中可见 (15)

如何确定Fragment何时在ViewPager中可见

你可以通过在你的Fragment重写setUserVisibleHint来完成以下工作:

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

问题: ViewPager片段onResume()在片段实际可见之前被触发。

例如,我有2个片段与ViewPagerFragmentPagerAdapter 。 第二个片段仅适用于授权用户,我需要让用户在片段变为可见时使用警报对话框登录。

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

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

如何才能做到这一点?


在这里发布的另一个解决方案是在krad 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();
        }
    }
}

通过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();
}

为了检测ViewPager Fragment可见,我确信只有使用 setUserVisibleHint是不够的。
这里是我的解决方案,检查片段可见,首次启动viewpager时不可见,在页面之间切换,转到另一个活动/ fragment / background / foreground`

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 call when viewpager create fragment and when we go to this fragment from
     * background or another activity, fragment
     * NOT call 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 call 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项目在这里

希望它有帮助


在使用FragmentStatePagerAdapters和3个选项卡时遇到同样的问题。 每当第一个标签被点击时,我必须展示一个Dilaog,并在点击其他标签时隐藏它。

仅重写setUserVisibleHint()无助于查找当前可见片段。

从第三个标签----->第一个标签点击。 它为第二个片段和第一个片段触发两次。 我将它与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.
    }

}

在片段中使用GestureDetector.OnGestureListenerand ,当发生火灾时,您将知道“this”片段是可见的


在片段中添加以下代码

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

     }
}

当我试图让viewpager中的片段显示在屏幕上供用户查看时,我试图获得一个定时器来触发这个问题。

定时器总是在用户看到片段之前开始。 这是因为片段中的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等。


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

所以这两种方法可以用作解决这个问题的方法,特别适用于小型和短期工作。

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

问候。



我遇到过同样的问题。 ViewPager执行其他片段生命周期事件,我无法改变这种行为。 我用碎片和可用的动画写了一个简单的寻呼机。 SimplePager


有时会 onCreateView() 之前调用setUserVisibleHint() ,有时在之后会导致麻烦。

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

所以如果你想在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 !visible被调用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 :确保onCreateView() myUIUpdate()仅在创建并显示片段时调用,而不是在简历中调用。 当您处于第一个标签时,它也可以解决问题,即使第二个标签不可见,也会创建第二个标签。 这解决了这个问题,并检查onCreate时是否可见碎片屏幕。

fragmentOnCreated :确保片段不可见,并且在第一次创建片段时不会调用。 所以现在这个if子句只在你从片段中滑出时才被调用。

更新您可以像这样将所有代码放入BaseFragment代码中并覆盖方法。


请注意, 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
    }
}

这似乎恢复了您期望的正常onResume()行为。 它按住主键离开应用程序,然后重新进入应用程序,发挥很好。 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
}

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);
    }
}




android-viewpager