【问题标题】:Android activity with many fragments, the correct way to handle lifecycle changes?具有许多片段的Android活动,处理生命周期变化的正确方法?
【发布时间】:2016-08-10 15:07:17
【问题描述】:

我有一个 Android 活动,它保存和管理六个片段,片段是流程中的一个步骤,一些片段被替换,其中一些被添加。

Activity 只是使用 Framelayout 作为片段的容器,如下所示:

<FrameLayout
        android:id="@+id/content"
        android:layout_below="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

那么片段的流程是这样的:

//Activity starts, add first Fragment

 fragmentManager.beginTransaction().replace(R.id.content, FirstFragment.newInstance(listOfItems)).commit();

然后

//User pressed button, activity got callback from first fragment

FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.replace(R.id.content, fragment2);
transaction.addToBackStack("frag2");
transaction.commit();

然后

//Another callback from Frag2, perform the add of frag 3

FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.add(R.id.content, fragment3);
transaction.addToBackStack("frag3");
transaction.commit();

等等……

我还像这样从 Activity 管理后台堆栈:

//Controlling the back stack when the user selects the soft back button in the toolbar
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case android.R.id.home:
                if (fragmentManager.getBackStackEntryCount() == 0) {
                    super.onBackPressed();
                    overridePendingTransition(R.anim.no_change, R.anim.slide_down);
                } else {
                    if(!BaseFragment.handleBackPressed(getSupportFragmentManager())){
                        super.onBackPressed();
                        Fragment fragment = fragmentManager.getFragments()
                                .get(fragmentManager.getBackStackEntryCount());
                        fragment.onResume(); //Make sure the fragment that is currently at the top of the stack calls its onResume method
                    }
                }
                return true;
        }
        return super.onOptionsItemSelected(item);
    }

    //Controlling the back stack when the user selects the "hardware" back button
    @Override
    public void onBackPressed() {
        if (fragmentManager.getBackStackEntryCount() == 0) {
            super.onBackPressed();
            overridePendingTransition(R.anim.no_change, R.anim.slide_down);
        } else {
            if(!BaseFragment.handleBackPressed(getSupportFragmentManager())){
                super.onBackPressed();
                Fragment fragment = fragmentManager.getFragments()
                        .get(fragmentManager.getBackStackEntryCount());
                fragment.onResume(); //Make sure the fragment that is currently at the top of the stack calls its onResume method
            }
        }
    }

我的问题是我打开应用程序并转到加载片段的此活动,然后通过流程到某个阶段(我还没有缩小范围)然后我按下主页按钮并空白我的屏幕.现在经过一段时间后,当我再次打开应用程序时,它会在我留下的片段上打开,但一切似乎都搞砸了,当我按下返回时,它似乎弹出了错误的片段,并且 UI 与不同的片段混合在一起。

我的猜测是,当我再次打开应用程序时,Activity onResume 或 Fragment onResume 或某些生命周期事件被调用,但我没有正确处理?

所以我想知道在像我这样使用 Fragment 模式时是否应该遵守最佳实践、指南或模式?

【问题讨论】:

  • 我认为这不是问题的原因,但是没有必要在从后台重新添加的 Fragment 上调用“onResume”。如果片段从“替换”中删除,则重新添加时将再次调用“onResume”。如果片段从未被移除,那么状态将保持不变。
  • 是的,我同意,但你是对的,这不是问题的原因,我在片段的 onResume 中所做的只是调用 Activity 来设置工具栏的标题。
  • 好吧,你必须在 Fragment 本身上调用“super.onResume()”,因为它需要做自己的事情,乱序调用可能会导致意外行为。
  • 你为什么在 fragment3 中使用transaction.add? (无论如何我认为one Activity and many Fragments 模式很难维护。)
  • 因为我想在片段 2 的顶部添加片段 3 并将片段 2 保留在那里,因为片段 3 是用户选择,然后他们移回片段 2

标签: android android-fragments android-lifecycle fragment-backstack


【解决方案1】:

由于您在一个活动中有这么多片段,并且它们使用相同的容器,这意味着所有片段都在同一个位置,并且一次只会显示一个片段。

那么为什么不使用 ViewPager 并让 FragmentPagerAdapter 管理这些片段呢?这样就不需要自己去管理fragment生命周期,只需要重写FragmentPagerAdapter方法即可:

  • 通过getItem创建片段实例,
  • 通过getItemPositionAdapter.notifyDataSetChanged()更新片段,
  • 通过mViewPager.setCurrentItem(i)显示选定的片段

代码sn-ps,详细参考https://github.com/li2/Update_Replace_Fragment_In_ViewPager/

private FragmentPagerAdapter mViewPagerAdapter = new FragmentPagerAdapter(getSupportFragmentManager()) {
    @Override
    public int getCount() {
        return PAGE_COUNT;
    }

    // Return the Fragment associated with a specified position.
    @Override
    public Fragment getItem(int position) {
        Log.d(TAG, "getItem(" + position + ")");
        if (position == 0) {
            return Page0Fragment.newInstance(mDate);
        } else if (position == 1) {
            return Page1Fragment.newInstance(mContent);
        }

        return null;
    }

    @Override
    // To update fragment in ViewPager, we should override getItemPosition() method,
    // in this method, we call the fragment's public updating method.
    public int getItemPosition(Object object) {
        Log.d(TAG, "getItemPosition(" + object.getClass().getSimpleName() + ")");
        if (object instanceof Page0Fragment) {
            ((Page0Fragment) object).updateDate(mDate);
        } else if (object instanceof Page1Fragment) {
            ((Page1Fragment) object).updateContent(mContent);
        }
        return super.getItemPosition(object);
    };
};

【讨论】:

  • FragmentPagerAdapter 是否适用于本质上是动态且需要相互通信的 Fragment,在此处的 Android 文档上 - developer.android.com/reference/android/support/v4/app/… - 它声明“此版本的寻呼机最适合在以下情况下使用有一些通常更静态的片段需要分页”
  • 你的动态是什么意思?只是数据改变了?
  • ViewPager 有两个适配器:FragmentPagerAdapterFragmentStatePagerAdapter。两者的区别在于第二个会销毁不需要的片段,第一个会保留所有片段......所以如果你有一个小的固定数量的片段,FragmentPagerAdapter 是合适的。如果你有一个很长的列表,比如电子邮件列表,你应该使用 FragmentStatePagerAdapter 来节省内存。
  • 动态我的意思是他们需要在彼此之间进行通信,并且片段之间的状态变化取决于对其他片段采取的操作
  • Android API Guides 说a handful of typically more static fragments,我认为这句话中的“静态”意思是这样的:视图布局是固定的,但数据可能会改变。如果一切都是静态的,你可以只绘制一个静态视图,不需要使用片段,这会使事情变得复杂。所以根据您的描述,我认为FragmentPagerAdapter 适合您。
猜你喜欢
  • 2012-02-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-11-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多