当前位置: 首页 > 知识库问答 >
问题:

Android如何处理ViewPager中的片段livecycle?

莫振
2023-03-14

解释

最近,我一直在与碎片和viewpager进行大量的斗争。

我发现ViewPager可以保存你想要的任意多的片段。当它首次实例化时,根据类型(FragmentStatePagerAdaptervsFragmentPagerAdapterFragmentPagerAdapter和FragmentStatePagerAdapter之间的差异),它将创建所有片段或只创建“需要的”片段。

class MyFragment extends Fragment
{
    private Context context;

    private doThingsAfterAttach()
    {
        getActivity(); //this is null.

        null != context; //TRUE
    }

    @Override
    public onAttach( Activity activity )
    {
        context = activity;
        doThingsAfterAttach();
    }
}

您可以看到,getactivity()返回null,尽管您是在onattach被触发后调用此方法的。

如果您决定将保留的上下文强制转换为活动,以执行findviewbyid任务,您将看到您试图查找的视图为null,这意味着无法找到/不存在。

发行

谢谢大家,在这里我准备回答大家的问题。

谢了。

共有1个答案

越狐若
2023-03-14

注意:这是一篇冗长且可能令人厌烦的文章

你的查看页问题并不新鲜。别难过。我们都经历过似乎是一个新范例的神奇解决方案(我将用斜体表示new,因为它在当时并不一定是新的)。添加了对片段的支持(通过您提到的状态和无状态适配器),事情很快变得更加有趣。

我不打算详细说明适配器是好的还是烦人的(您可以得出自己的结论),或者为什么适配器(以及小部件本身)缺乏非常基本的东西,没有人知道Android开发人员在为这些东西公开公共API时是怎么想的。

所以…取景器需要一个适配器。适配器负责向视图页提供视图。因此,虽然ViewPager是一个理解触摸、拖动、抛出、绘制、测量等内容的视图,但它实际上希望适配器提供要显示的数据。(这是一个巨大的简化)。

这里我们有两种类型的适配器,它们知道如何处理片段。一个保持一种状态,另一个不。意思是一个并没有真正释放任何东西(碎片PagerAdapter)和一个确实释放了它的碎片(碎片StatePagerAdapter)…但是等等…在这个上下文中什么是“释放”?

事实证明,碎片并不真的存在于一个自由的世界中,它们由一个FragmentManager控制,他确保它们不会迟到,并决定何时释放它们。这是一个连适配器都不能覆盖的规则!所以他们无论如何都必须向这个碎片管理器报告。您可以看出,适配器必须与这个FragmentManager对话,因为它必须在构造函数中接收一个!

public static class MyAdapter extends FragmentStatePagerAdapter {
       public MyAdapter(FragmentManager fm) {
           super(fm);
       }

好的,谢谢明显先生!

实际上,FragmentManager是一个静态类,您不创建它,只从活动中获取它。

现在想想controller的FragmentManager,它保留对您的片段的引用,即使在您离开它们很久之后也是如此。它知道如何使它们返回,它帮助它们在配置更改(例如,旋转)时保存它们的状态,它保存它们的一堆/堆栈,知道它们可以按什么顺序弹出,并执行所有这些操作,传递必须提交的碎片事务,很像关系数据库事务。

private ArrayList<Fragment.SavedState> mSavedState = new ArrayList<Fragment.SavedState>();
private ArrayList<Fragment> mFragments = new ArrayList<Fragment>();

和对FragmentManager的引用:

private final FragmentManager mFragmentManager;

毫不奇怪,FragmentPagerAdapter没有片断列表,也没有保存状态列表,它只是让FragmentManager完成它的工作。

因此,让我们首先看看“state”寻呼机适配器,看看在销毁一个片段的时候会做什么…

1 @Override
2 public void destroyItem(ViewGroup container, int position, Object object) {
3      Fragment fragment = (Fragment)object;
4      if (mCurTransaction == null) {
5          mCurTransaction = mFragmentManager.beginTransaction();
6      }
7      if (DEBUG) Log.v(TAG, "Removing item #" + position + ": f=" + object
8              + " v=" + ((Fragment)object).getView());
9      while (mSavedState.size() <= position) {
10         mSavedState.add(null);
11     }
12     mSavedState.set(position, mFragmentManager.saveFragmentInstanceState(fragment));
13     mFragments.set(position, null);
14     mCurTransaction.remove(fragment);
15 }

第14行将此添加到片段的Manager事务中,以便FragmentManager知道要做什么。

(注意:当您动态添加/更改/删除片段时,在ViewPager中有一个模糊的bug,我将在最后链接到问题/解决方案)。

当您将片段添加到ViewPager时,过程相对类似(请参见instantiateItem()方法…

if (mCurTransaction == null) {
    mCurTransaction = mFragmentManager.beginTransaction();
}

Fragment fragment = getItem(position);

最后,它继续添加新收到的片段:

while (mFragments.size() <= position) {
    mFragments.add(null);
}
fragment.setMenuVisibility(false);
fragment.setUserVisibleHint(false);
mFragments.set(position, fragment);
mCurTransaction.add(container.getId(), fragment);

它必须添加null填充以使数组达到准确的大小,当然还使用了FragmentTransaction。

到目前为止,这个故事的寓意是,适配器保留了自己的东西集合,但通过让老板(又名:碎片管理器)知道他在控制,这让他很高兴。

 ArrayList<Fragment> mActive;
 ArrayList<Fragment> mAdded;

还可以查看其中的removeFragment()方法,该方法最终调用moveTostate()方法。

够了…我什么时候可以调用getActivity(),而不在片段中获得null?

好的,所以你提供了一个你想做的例子。

public interface FragmentInterface {
    void onBecameVisible();
}
public Fragment YourFragment implements FragmentInterface {

然后你有一个:

@Override
public void onBecameVisible() {
    if (getActivity() != null && !getActivity().isFinishing()) {
          // do something with your Activity -which should also implement an interface!-
    }
}

谁叫onbecamevisible?

活动中的viewPager.onPageChangelistener():

public void onPageSelected(final int i) {
    FragmentInterface fragment = (FragmentInterface) mPagerAdapter.instantiateItem(mViewPager, i);
    if (fragment != null) {
        fragment.onBecameVisible();
    } 
}
 类似资料:
  • 我在一个详细视图中使用ViewPager和FragmentPagerAdapter。这意味着我有一个项目列表,一个屏幕显示单个项目的细节。但用户可以左右滑动来浏览所有项目。这遵循了谷歌滑动视图的指导方针。 但是我想知道一件事。在ListView中,每一行的视图都会被重用。一旦一行从屏幕上滚动出来,它就会被重用为绑定到ListView的适配器的getView方法的转换器视图参数。但是这种重用行为似乎

  • MainActivity(扩展了fragmentActivity)有listview,它调用listviewadapter,其中有一个带有ViewpagerAdapter的ViewPager。现在,如果我需要在ViewPager中生成5个片段,如何将getFragmentManager()传递给适配器。 MainActivity[fragmentActivity]---->Listview----

  • 我需要将ViewPager放置在一个片段内部,但我有两个片段,片段1是我的菜单,片段2我想用作ViewPagerIndicator。 但一个碎片不能再有另一个碎片...我该怎么做呢?

  • 我可以将谷歌地图加载到活动中的Android片段中。这一直运行良好。 但现在我想使用ViewPager在视图之间导航(class

  • 我有以下布局 一切正常,但我想用一个片段替换浏览器 这不是重新调整ViewPager的间距,我该怎么办???

  • 我正在使用嵌套片段,当我按下时,我面临问题。 流量: 这是我的应用程序的结构。其中C、D、E、F是子片段,未添加到后堆栈中。D包含列表片段,从D中我称为B。但当我从B中按回来时,我想再次使用最后选定位置的D。我在后堆栈中添加了A和B。我怎样才能做到这一点。给我提供一些解决方案。我回忆起D,但选择的位置总是在变化。