gpt4 book ai didi

android - Android如何在ViewPager中处理 fragment 生命周期?

转载 作者:行者123 更新时间:2023-12-03 13:58:26 25 4
gpt4 key购买 nike

这将不是一个简单的问题。我将尽我所能解释,所以这可能会有点长。

说明

最近,我在Fragments和ViewPager方面进行了很多努力。

我发现,ViewPager可以容纳任意数量的 fragment 。首次实例化时,根据类型(FragmentStatePagerAdapterFragmentPagerAdapter Difference between FragmentPagerAdapter and FragmentStatePagerAdapter),它将创建所有 fragment 或仅创建“所需” fragment 。

在这些 fragment 中,您可以自由使用getActivity()之类的方法,该方法基本上是用于创建 View ,查找 View ,显示对话框的上下文。

但是,一旦离开 fragment ,getActivity()就开始返回null,因为该 fragment 是从“ Activity ”中分离出来的。听起来很逻辑。

但是,对我来说听起来不合逻辑,因此我在这里问,是当您返回到 fragment 页面(或足够接近)时,它再次触发了基本方法,例如onAttach

一旦拥有onAttach,就可以保留Activity提供的上下文,但是如果尝试执行以下操作(伪代码),则可以:

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()后调用此方法, onAttach仍返回null。

如果决定将保留的上下文强制转换为Activity,以执行 findViewById任务,则将看到您要查找的 View 为null,这意味着找不到/不存在。

问题

假设您有一个带有5个标签( fragment )的ViewPager。
在这5个选项卡中的任何一个上执行任务时,您都想通知“所有者” Activity ,以便在发生某些变化时通知应该更新其内容的所有 fragment 。
但是当您通知他们时,如果他们必须更改布局,他们将无法执行,因为一旦您尝试 findViewById,就会发生两种情况: getActivity()返回null,因此您无法获取Views,并且如果将上下文转换为 Activity ,搜索任何内容时都不会返回任何 View 。

最让我害怕的是,当您旋转设备时,就像离开 fragment 页面然后返回一样; “丢失” Activity 。

实际问题

因此,我要寻找的是一个答案,该答案向我解释了内部发生的事情,因此我可以找到适当的代码来应对这些情况。

我无法提供太多代码,因为这将毫无用处。谁将ViewPager与 Fragments一起使用,可能已经处理了这些事情,所以您将了解我。

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

谢谢。

最佳答案

注意:这是篇冗长且可能很无聊的文章

您的ViewPager问题不是新问题。别难过我们所有人都经历了似乎是新范式的魔术解决方案的过程(我将在斜体中添加新内容,因为在当时它不一定是新的)。有了对Fragments的额外支持(通过您提到的State和Stateless适配器),事情变得越来越有趣。

我不打算详细介绍适配器是好还是烦人(您可以得出自己的结论),或者适配器(以及小部件本身)缺少非常基本的东西,而没人能理解Android开发人员在思考它们时会怎么想的原因公开了这些的公共(public)API。

相反,我将以大家认为的方式帮助您管理“ Activity /fragment ”通信。

ViewPager中的 fragment 发生了什么?

在我的愚见中, Activity /fragment 的概念是可怕的,这是骇人听闻的事。但是有效的方法,因为它很快就证明了它的有效性,以至于Android团队决定开始为越来越多的东西添加Fragment支持。甚至嵌套的 fragment ,是 fragment 中的 fragment !而且由于Android开发人员(越来越少,但仍然非常频繁)必须处理平台的旧版本,因此所有这些支持最初都已添加到支持库中,并且从未移出它。

但是,让我们回到话题上。 Fragment的生命周期有些令人困惑(部分原因是某些生命周期方法的命名不正确以及它们无法保证发生的顺序)。有些东西隐藏在不友好的回调后面(TreeLayout有人吗?)。

因此,... ViewPager需要一个适配器。适配器负责向ViewPager提供其 View 。因此,尽管ViewPager是一个了解触摸,拖动,甩动,绘制,测量等内容的 View ,但它确实希望适配器能够提供要显示的数据。 (这是一个巨大的简化)。

在这里,我们有两种类型的适配器,它们知道如何处理 fragment 。一个保持状态,而另一个则不保持状态。意思是一个人实际上并没有释放任何东西(FragmentPagerAdapter),一个人确实确实释放了其 fragment (FragmentStatePagerAdapter)……但是等等……在这种情况下,“释放”是什么?

事实证明, fragment 实际上不是在自由世界中存在的,它们由FragmentManager支配,该 fragment 管理器确保 fragment 不晚并决定何时发布它们。这是一个规则,即使适配器也不能覆盖!因此,无论如何,他们必须向此FragmentManager报告。您可以说适配器必须与此FragmentManager对话,因为它必须在构造函数中接收一个!

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

FragmentManager到底是谁?

好吧,文档不是很友好,只是说:

Interface for interacting with Fragment objects inside of an Activity



好吧,谢谢先生先生!

实际上,FragmentManager是静态类,您无需创建它,而只是从Activity中获取它。

现在想想一个 Controller 的FragmentManager,该 Controller 即使在您离开 fragment 后仍会保留对您 fragment 的引用。它知道如何使它们恢复原状,当配置发生变化(例如旋转)时,它可以帮助他们保存状态,保留一堆/一堆它们,知道可以按照什么顺序弹出它们,并且可以完成所有这些工作,使用必须执行的FragmentTransactions,非常类似于关系数据库事务。

大多数这种复杂性(我相信他们都有其原因)是在幕后发生的,因此您不必为此担心太多。

我们现在可以回到原始问题吗?

是的是,这是一个很长的主题,您可以看到,但是让我们说清楚一点,因为我必须回去上类……

当Fragment消失时,适配器会 secret 告诉FragmentManager:“糟糕,此Fragment现在不再需要了”。 (它可能不使用该短语,而是类似的东西)。有关更详细的响应,请添加 you can actually look at the more or less recent code of the FragmentStatePagerAdapter并从中学到很多。

看看它如何具有Fragments和SavedStates列表:
private ArrayList<Fragment.SavedState> mSavedState = new ArrayList<Fragment.SavedState>();
private ArrayList<Fragment> mFragments = new ArrayList<Fragment>();

以及对FragmentManager的引用:
private final FragmentManager mFragmentManager;

毫不奇怪, FragmentPagerAdapter既没有Fragments列表也没有保存状态列表,它只是让FragmentManager进行工作。

因此,让我们首先看一下“状态”寻呼机适配器,看看何时应该销毁 fragment ……

在Google的允许下,让我们看一下 destroyItem()的源代码:
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 }

如果尚未开始,则第4行开始FragmentTransaction。

第9-11行用空条目填充mSavedState数组,直到它至少等于我们要删除的 fragment 的索引大小为止。

第12行保存了被删除的Fragment的状态(以便将来可以根据需要对其进行还原)。

第13行有效地删除了 fragment 引用…

第14行将其添加到Fragment的Manager事务中,以便FragmentManager知道该怎么做。

(注意:当动态添加/更改/删除 fragment 时,ViewPager中存在一个难以理解的错误,我将在最后链接到问题/解决方案)。

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

它首先检查对象是否已实例化,然后立即将其返回。

如果 fragment 不存在,那么将创建一个……
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}

Fragment fragment = getItem(position);

记住,您通过 扩展这个适配器,并创建自己的 getItem()方法,这就是调用它的地方。您正在为适配器提供一个新创建的 fragment 。

接下来,适配器检查该 fragment 的saveState,以查看是否可以找到该 fragment (并在此出错)(请参见最后的链接)…

最后,它继续添加新接收到的Fragment:
while (mFragments.size() <= position) {
mFragments.add(null);
}
fragment.setMenuVisibility(false);
fragment.setUserVisibleHint(false);
mFragments.set(position, fragment);
mCurTransaction.add(container.getId(), fragment);

它必须添加null填充以使数组具有确切的大小,并且当然还使用FragmentTransaction。

到目前为止,故事的寓意是适配器保留自己的东西集合,但通过让老板知道他在控制中来使老板(又称FragmentManager)感到高兴。

作为引用, the support v13 versions几乎相同,但是引用了不支持的Fragment,FragmentManager,FragmentTransaction等版本。

因此,如果适配器保留一个列表并询问FragmentManager(通过FragmentTransaction),或者只是使用FragmentManager,那么FragmentManger会做什么?

这更加“复杂”,但FragmentManager实现具有“已添加”和“ Activity ” fragment 的列表(以及无数其他集合和数据结构)。
 ArrayList<Fragment> mActive;
ArrayList<Fragment> mAdded;

^这来自FragmentManagerImpl类!

因此,我不会详细介绍FragmentManager(您可以找到其源代码 here),因为它是一个非常大的类,并且它使用事务进行所有操作,而且 super 困惑。它为每个 fragment (创建,初始化等)保留状态机。有趣的方法可能是 moveToState(),这是进行Fragment生命周期回调的地方,因此请查看源代码以了解发生了什么。

还要看看那里的 removeFragment()方法,最后最后调用 moveToState()方法。

所有这些都足够了……什么时候可以调用getActivity()而不在 fragment 中获取null?

好的,所以您提供了您想做的事的例子。

您有5个 fragment 的ViewPager(例如),并且您想通知他们发生了什么事情,以便他们可以对此做些事情。很多东西。

我会说这是一个典型的Observer模式+ ViewPager.OnPageChangeListener()。

观察者模式很简单,您可以创建一个接口(interface),如下所示:
public interface FragmentInterface {
void onBecameVisible();
}

您的 fragment 实现了此接口(interface)…
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?

您的Activity中的ViewPager.OnPageChangeListener():
public void onPageSelected(final int i) {
FragmentInterface fragment = (FragmentInterface) mPagerAdapter.instantiateItem(mViewPager, i);
if (fragment != null) {
fragment.onBecameVisible();
}
}

因此,现在该 Activity 可以可靠地告诉Fragment它已经变得可见。

现在……如果您想告诉其他 fragment ,则必须有一个“监听器”列表(观察者模式),但是您必须了解,根据我们对适配器和 fragment 管理器的了解, fragment 可能会分离(甚至不一定一定要销毁它们)。它们不可见的事实可能意味着它们倾向于破坏其 View 层次结构(因此,此时无法更改 View 层次结构)。

最好的选择是确保onCreateView能够根据您的业务逻辑询问应显示哪个 View 。

理解生命周期的最好方法是在Fragment生命周期的每个EACH中添加一个日志(onStart,Stop,Pause,Resume,Detach,Destroy,ActivityCreated等),并查看它们在ViewPager中移动时的 react 。

我希望这篇冗长的文章能使您对ViewPager对您的Fragment所做的事情有个了解。让我们知道您是否还有其他具体问题,或者我是否真的没有帮助您:)

关于我之前提到的BUG,请看 this post,它讨论了FragmentStatePagerAdapter处理 fragment 还原的方式中的错误(及其修复)。

关于android - Android如何在ViewPager中处理 fragment 生命周期?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21232690/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com