可以更新片段而不是创建新实例吗?

Posted

技术标签:

【中文标题】可以更新片段而不是创建新实例吗?【英文标题】:Ok to update fragments instead of creating new instances? 【发布时间】:2012-02-22 02:51:48 【问题描述】:

android 文档中关于使用片段的example 中,当应用程序处于“双视图”模式时,只要应用程序需要显示不同标题的详细信息,就会重新创建详细信息片段。 FragmentTransaction.replace() 用于将每个旧的详细信息片段实例替换为新的。

这是推荐的做法吗?当真正的意图(没有双关语)是更新 UI 显示的内容,而不是 UI 本身时,创建一个新的 UI 实例不是很浪费。在我看来,创建新实例的唯一原因是如果打算将它们添加到后台堆栈,以便用户可以追溯步骤。否则,直接更新片段是否安全/可取?

在示例的情况下,它表示类似于DetailsFragment.setShownIndex() 的方法。这将被调用,传入新的标题索引,而不是重新创建DetailsFragment

假设我们有一个版本的示例,其中一个活动管理两个片段,但一次只显示一个,根据需要交换每个片段。活动是否可以为每个片段创建一个实例,保留对每个片段的引用,然后根据需要简单地从自身添加或删除这两个实例?

这样做的一个可能的棘手后果是,当标题片段处于resumed 状态(即处于“前景”)时,选择标题将导致在详细信息时调用DetailsFragment.setShownIndex()片段处于stopped 状态。

好主意?坏主意?

提前致谢。

【问题讨论】:

【参考方案1】:

就像你说的,创建新的 Fragment 实例的主要原因是为了便于使用后栈。重用现有的 Fragment 也是非常安全的(使用 FragmentManager.findFragmentById()FragmentManager.findFragmentByTag() 查找它)。有时你需要充分利用像isVisible()isRemoving() 等Fragment 方法,这样你就不会在DetailsFragmentstopped 时非法引用UI 组件。

无论如何,在您提议的具有 2 个片段的单窗格 Activity 中,您的 setShownIndex 方法可以在 DetailsFragment 中设置一个私有字段,该字段在 onCreateViewonActivityCreated 中加载。

例如,

DetailsFragment df = getFragmentManager().findFragmentByTag("details");
if (df != null) 
    df.setShownIndex(getSelectedIndex());
 else 
    df = DetailsFragment.newInstance(getSelectedIndex());

fragmentTransaction.replace(R.id.frame, df, "details").commit();

在这两种情况下,无论 df 是新创建的还是重用的,onCreateViewonActivityCreated 都会在 DetailsFragment 添加到容器时被调用。

但是,如果您想要一个后退堆栈,我强烈建议您只创建新实例,否则您只是为DetailsFragment 的内容实现自己的后退堆栈。

【讨论】:

【参考方案2】:

我已经尝试了以下代码,它对我有用:

private void replaceFragment(Class fragmentClass, String FRAGMENT_NAME, android.support.v4.app.FragmentManager fragmentManager) 

    Fragment fragment = null;
    String backStateName = fragmentClass.getName(); // nome della classe del Fragment

    Log.d("Fragment: ", "Creazione Fragment: "+backStateName);


    Boolean fragmentExit = isFragmentInBackstack(fragmentManager, backStateName);


    if (fragmentExit)  //Il Fragment è presente nello stacback

        // Fragment exists, go back to that fragment
        //// you can also use POP_BACK_STACK_INCLUSIVE flag, depending on flow
        fragmentManager.popBackStackImmediate(fragmentClass.getName(), 0);

     else 

        // se non esiste lo aggiungiamo
        try 
            fragment = (Fragment) fragmentClass.newInstance();
         catch (Exception e) 
            e.printStackTrace();
        

        // Inizializzo la transazione del Fragment
        android.support.v4.app.FragmentTransaction ft = fragmentManager.beginTransaction();
        ft.setCustomAnimations(
                R.anim.fragment_slide_left_enter,
                R.anim.fragment_slide_left_exit,
                R.anim.fragment_slide_right_enter,
                R.anim.fragment_slide_right_exit);
        ft.replace(R.id.frameLayout_contentMain, fragment, FRAGMENT_NAME);
        ft.addToBackStack(fragmentClass.getName());
        ft.commit();

        // Recupero il numero di Fragment presenti
        Integer nFragment = fragmentManager.getBackStackEntryCount();

        Log.d("Fragment: ", "Numero di Fragment: "+nFragment);

    


要确定 Fragment 是否已经在 StackBack 中,请执行此函数:

public static boolean isFragmentInBackstack(final android.support.v4.app.FragmentManager fragmentManager, final String fragmentTagName) 
    for (int entry = 0; entry < fragmentManager.getBackStackEntryCount(); entry++) 
        if (fragmentTagName.equals(fragmentManager.getBackStackEntryAt(entry).getName())) 
            return true;
        
    
    return false;

希望能帮到你

【讨论】:

如果你提供英文的 cmets 会更有帮助 对不起。我是意大利人。

以上是关于可以更新片段而不是创建新实例吗?的主要内容,如果未能解决你的问题,请参考以下文章

创建片段的新实例时菜单未膨胀

在片段中接收 NFC 的应用程序创建托管活动的新实例

转到ViewPager中的下一页

如何防止在方向更改时重新创建片段寻呼机中的片段?

永远观察实时数据的片段

ViewPager 使用片段更新屏幕外视图而不是主视图