视图翻页器和片段生命周期

46

我有一个ViewPager,每次加载三个页面。如果我从第一页向右滑动到第二页然后到第三页,第一页(fragment)将进入onPause()。然后,如果我向第二页滑动,第一页将回到onResume(),即使页面1对用户仍不可见。所以我的问题是:如何在代码中区分第一页和第二页?例如,如果我必须在片段可见时运行一段代码,该如何做到?


2
请查看我的答案,链接在这里:https://dev59.com/42Eh5IYBdhLWcg3w32xG#24386516 - LordRaydenMK
8个回答

50

FragmentPagerAdapter会将除了当前显示的fragment以外的其他fragment保持在恢复状态。解决方法是实现一个自定义的OnPageChangeListener,并为fragment显示时创建一个新的方法。

1)创建LifecycleManager接口,这个接口将有两个方法,每个ViewPager的Fragment都将实现它。这些方法如下:

public interface FragmentLifecycle {

    public void onPauseFragment();
    public void onResumeFragment();

}

2) 让每个碎片实现接口Add,将implements语句添加到每个类声明中:

public class FragmentBlue extends Fragment implements FragmentLifecycle
public class FragmentGreen extends Fragment implements FragmentLifecycle
public class FragmentPink extends Fragment implements FragmentLifecycle

3)在每个片段中实现接口方法为了检查它是否按预期工作,我将仅记录方法调用并显示Toast:

@Override
public void onPauseFragment() {
    Log.i(TAG, "onPauseFragment()");
    Toast.makeText(getActivity(), "onPauseFragment():" + TAG, Toast.LENGTH_SHORT).show(); 
}

@Override
public void onResumeFragment() {
    Log.i(TAG, "onResumeFragment()");
    Toast.makeText(getActivity(), "onResumeFragment():" + TAG, Toast.LENGTH_SHORT).show(); 
}

4) 在ViewPager页面更改时调用接口方法 您可以在ViewPager上设置OnPageChangeListener,并在每次ViewPager显示另一页时获取回调:

pager.setOnPageChangeListener(pageChangeListener);

5)实现OnPageChangeListener来调用你的自定义生命周期方法。

监听器知道新的位置,并且可以借助PagerAdapter在新Fragment上调用接口方法。我可以在这里为新片段调用onResumeFragment(),对当前片段调用onPauseFragment()。

我还需要存储当前片段的位置(最初当前位置等于0),因为我不知道用户是从左向右滚动还是从右向左滚动。在代码中可以看到我的意思:

private OnPageChangeListener pageChangeListener = new OnPageChangeListener() {

    int currentPosition = 0;

    @Override
    public void onPageSelected(int newPosition) {

        FragmentLifecycle fragmentToShow = (FragmentLifecycle)pageAdapter.getItem(newPosition);
        fragmentToShow.onResumeFragment();

        FragmentLifecycle fragmentToHide = (FragmentLifecycle)pageAdapter.getItem(currentPosition);
        fragmentToHide.onPauseFragment();

        currentPosition = newPosition;
    }

    @Override
    public void onPageScrolled(int arg0, float arg1, int arg2) { }

    public void onPageScrollStateChanged(int arg0) { }
};

我没有编写这段代码。完整的教程在这里:http://looksok.wordpress.com/2013/11/02/viewpager-with-detailed-fragment-lifecycle-onresumefragment-including-source-code/


1
在onResumeFragment中,getActivity()可能会返回Null(至少在我的情况下是这样)。解决方法可能是通过接口传递上下文(或活动)。例如:public void onResumeFragment(Activity activity) - Pranaysharma
3
在我的情况下,'getItem();' 方法返回的是没有视图的片段。我通过使用 'instantiateItem' 方法来解决了这个问题。具体信息请参考:http://developer.android.com/reference/android/support/v4/view/PagerAdapter.html#instantiateItem(android.view.ViewGroup, int)。 - Alexey
示例:http://www.alexandroid.net/android/fragments/viewpager-fragments-onpageselectedonpageunselected/ - Alexey
10
大多数适配器的 getItem(pos) 方法返回新的 Fragment 实例,但我们需要将已缓存的片段添加到片段管理器。因此,您需要在新创建但未附加到Activity实例上的片段上调用 .onPauseFragment() 方法。请不要复制粘贴未经测试的代码。为什么它会被接受呢? - vigilancer
1
getActivity返回null,这是因为正如@vigilancer所说,大多数适配器的getItem(pos)返回新实例...如果您缓存它,则不会这样。 - Ucdemir
1
onPageSelected(currentPosition) 返回的片段将不是之前可见的同一个片段。它将是一个全新的 Fragment,onCreateView 尚未被调用,并且直到您返回该位置才会被调用。如果您想使用不再可见的 Fragment,请将其缓存,但请确保在不再需要时将其置空。 - user1608385

11

如果你的Fragment扩展了android.support.v4.app.Fragment

你可以使用这个,它对我有效。

@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
    super.setUserVisibleHint(isVisibleToUser);
    if (!isVisibleToUser) {
        //do sth..
    }
}

当片段首次创建时,在此方法内,getActivity()getContext()将返回null。我个人更喜欢onPageSelected()方法的方式。 - Sira Lam
setUserVisibleHint现在已经被弃用了。有没有它的替代方法? - Anil Ugale

6

使用pager.setOffscreenPageLimit(number)方法来设置您想在堆栈中保留多少个片段。


4
默认值为1,这意味着前一个和后一个片段保持空闲状态。 - LordRaydenMK
View Pager 总是同时加载至少 3 页。我的问题是,当其中一页变为可见时,如何获得回调。 - shreyas

1

重写 setUserVisibleHint() 方法。此方法将在片段对用户可见时调用。


1

重写 setUserVisibleHint(),当片段对用户可见时将调用此函数。


当片段首次创建时,也将调用此函数。 - AouledIssa
是的,但如果您在ViewPager中有多个片段,则该方法将非常有用。 - venu46
你只需要测试一下 (getUserVisibleHint) 这个函数,因为它返回它是否可见。确保在调用 super 方法之后再调用它。 - AouledIssa

0

解决您的问题:

public class FragmentVisibleHelper implements LifecycleObserver {

    private static final String TAG = "VipVisibleHelper";

    public interface IVisibleListener {

        void onVisible();

        void onInVisible();
    }

    boolean mIsVisibleToUser;
    boolean mStarted = false;

    volatile boolean mIsCalledVisible = false;
    volatile boolean mIsCalledInvisible = false;


    IVisibleListener mListener;

    public void setListener(IVisibleListener mListener) {
        this.mListener = mListener;
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    void onResume() {
        Log.d(TAG, String.format("%-60s %s", this.toString(), "onResume() called:"));
        if (mIsVisibleToUser) {
            dispatchVisible();
        }
    }

    private void dispatchVisible() {
        Log.d(TAG, String.format("%-60s %s", this.toString(), "dispatchVisible() called mIsCalledVisible = [" + mIsCalledVisible + "] mIsCalledInvisible = [" + mIsCalledInvisible + "] "));
        if (!mIsCalledVisible) {
            mIsCalledVisible = true;
            mIsCalledInvisible = false;

            if (Profile.LOG) {
                Log.d(TAG, String.format("%-60s %s", this.toString(), "dispatchVisible() called onVisible"));
            }

            if (mListener != null) {
                mListener.onVisible();
            }
        }
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
    void onPause() {
        Log.d(TAG, String.format("%-60s %s", this.toString(), "onPause() called:"));
        if (mIsVisibleToUser) {
            dispatchInvisible();
        }
    }

    private void dispatchInvisible() {
        Log.d(TAG, String.format("%-60s %s", this.toString(), "dispatchInvisible() called mIsCalledVisible = [" + mIsCalledVisible + "] mIsCalledInvisible = [" + mIsCalledInvisible + "] "));
        if (!mIsCalledInvisible) {
            mIsCalledInvisible = true;
            mIsCalledVisible = false;

            if (Profile.LOG) {
                Log.d(TAG, String.format("%-60s %s", this.toString(), "dispatchInvisible() called onInVisible"));
            }

            if (mListener != null) {
                mListener.onInVisible();
            }
        }
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    void onStart() {
        Log.d(TAG, String.format("%-60s %s", this.toString(), "onStart() called"));
        mStarted = true;
        if (mIsVisibleToUser) {
            dispatchVisible();
        }
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    void onStop() {
        Log.d(TAG, String.format("%-60s %s", this.toString(), "onStop() called"));
        if (mIsVisibleToUser) {
            dispatchInvisible();
        }
        mStarted = false;
    }

    public void setUserVisibleHint(boolean isVisibleToUser) {
        Log.d(TAG, String.format("%-60s %s", this.toString(), "setUserVisibleHint() called with: isVisibleToUser = [" + isVisibleToUser + "]:"));
        mIsVisibleToUser = isVisibleToUser;
        if (mStarted) { // fragment have created
            if (mIsVisibleToUser) {
                dispatchVisible();
            } else {
                dispatchInvisible();
            }
        }
    }

    public boolean isVisibleToUser() {
        return mIsVisibleToUser;
    }
}

0

在分页适配器中,您可以放置此方法。

    public Object instantiateItem(@NonNull ViewGroup container, int position) {
        Fragment fragment = fragments.get(position);
        if (fragment == null) {
            fragment = (Fragment) super.instantiateItem(container, position);
            fragments.put(position, fragment);
        }
        return fragment;
    }

    public Fragment getFragment(int position) {
        return fragments.get(position);
    }

然后在页面更改监听器中,您可以获取当前片段并在片段内运行您的方法。

currentFragment = adapter.getFragment(viewPager.getCurrentItem());
((DetailNewsFragment)currentFragment).runYourMethod();

特别提示:不要使用adapter.getItem(position),因为它会返回一个新的片段实例。


0

在片段之间滑动时,片段的生命周期方法不会被调用。您可以使用 ViewPager.SimpleOnPageChangeListener 来解决这个问题。下面是一个示例代码(Kotlin)。

// other code
mViewPager.addOnPageChangeListener(object: ViewPager.SimpleOnPageChangeListener() {
    override fun onPageSelected(position: Int) {
        val oldPosition = mViewPager.currentItem
        val oldFragment = mViewPager.adapter?.instantiateItem(mViewPager, oldPosition)
        oldFragment.onPauseStuff() // Hint: do as here call onPause
        val newFragment = mViewPager.adapter?.instantiateItem(mViewPager, position)
        newFragment.onResumeStuff() // Hint: do as here call onResume
}
// other code

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接