将 Fragment 放在前面(无 Fragment Recreation)

Posted

技术标签:

【中文标题】将 Fragment 放在前面(无 Fragment Recreation)【英文标题】:Bring Fragment to Front (No fragment recreation) 【发布时间】:2014-01-11 07:38:33 【问题描述】:

我有三个片段 F1 F2 F3 F4 都可以从侧边栏访问。

所有四个都可以在任何时间以任何顺序调用,

现在我想,如果 F1 已经被点击(创建)然后不再创建 F1,而只使用片段管理器将片段 F1 带回到前面。所有其他片段都相同

到目前为止,我对容器中的每个片段都进行了尝试(片段活动)

if (fragmentManager.findFragmentByTag("apps")==null) 
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);

Fragment newFragment = new CategoriesFragment();
transaction.replace(R.id.content_frame, newFragment, "apps");
transaction.addToBackStack("apps");
transaction.commit();   
 else


If 部分确保我没有重新创建片段(如果它已经创建),但是我应该在else 部分中写什么,以便已经创建的片段可以在视图层次结构中放在前面

请帮忙,我在这个问题上卡了 2 天。

【问题讨论】:

你有多少种片段? @Rajesh CP 所有片段都是从 FRAGMENT 类扩展而来的,没有其他类型的片段 我的回答有帮助吗? 【参考方案1】:

我会将此代码放在活动类中,该类必须具有 FrameLayout,ID 为 R.id.fragment_container

private Fragment1 F1;
private Fragment2 F2;
private Fragment3 F3;
private Fragment4 F4;       

@Override
protected void onCreate(Bundle savedInstanceState) 
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    F1 = new Fragment1();
    getSupportFragmentManager().beginTransaction().add(R.id.fragment_container, F1).commit();
    F2 = new Fragment2();
    getSupportFragmentManager().beginTransaction().add(R.id.fragment_container, F2).commit();
    F3 = new Fragment3();
    getSupportFragmentManager().beginTransaction().add(R.id.fragment_container, F3).commit();
    F4 = new Fragment4();
    getSupportFragmentManager().beginTransaction().add(R.id.fragment_container, F4).commit();

    //if needed show F1
    getSupportFragmentManager().beginTransaction().show(F1).commit();


并添加此按钮单击:

public void onBtnClick(View view)
    if(mShowF1)
        getSupportFragmentManager().beginTransaction().show(F1).commit();
        getSupportFragmentManager().beginTransaction().hide(F2).commit();
        getSupportFragmentManager().beginTransaction().hide(F3).commit();
        getSupportFragmentManager().beginTransaction().hide(F4).commit();
    
    //...

在按钮单击时,您可以显示您想要的片段并隐藏其他片段。

注意 (@developer1011): 在活动保存状态调用commitAllowingStateLoss () 后使用。小心使用,因为片段不会随着活动恢复而恢复。

注意: MainActivity 应该为每个 Fragment 实现 OnFragmentInteractionListener。

public class MainActivity extends FragmentActivity implements Fragment1.OnFragmentInteractionListener, Fragment2.OnFragmentInteractionListener, Fragment3.OnFragmentInteractionListener, Fragment4.OnFragmentInteractionListener //..

    @Override
    public void onFragmentInteraction(Uri uri) 
        //
    

【讨论】:

调用 commit 会导致 java.lang.IllegalStateException: commit already called :( From developer docs :- A transaction can only be committed with this method prior to its containing activity saving its state. If the commit is attempted after that point, an exception will be thrown. @developer1011:问题未说明,是否应在活动恢复时恢复状态。来自文档: commitAllowingStateLoss () 与 commit() 类似,但允许在保存活动状态后执行提交。注意:小心使用!【参考方案2】:

通过标签获取片段并在容器中替换,

else
Fragment existingFragment = (CategoriesFragment)fragmentManager.findFragmentByTag("apps");
transaction.replace(R.id.content_frame,existingFragment, "apps");
transaction.addToBackStack("apps");
transaction.commit();

更新: 您可以使用隐藏和显示片段来避免娱乐。而不是使用“transaction.replace()”

fragmentTransaction.hide(<oldFragment>);
fragmentTransaction.show(<newFragment>);

【讨论】:

我检查了您提供的代码,但它在视图层次结构顶部重新创建了片段,而不是带回之前创建的片段 它仍然没有用,因为当我尝试显示以前创建的线程然后代码没有效果我做Fragment existingFragment = (CategoriesFragment)fragmentManager.findFragmentByTag("apps"); transaction.show(existingFragment);【参考方案3】:

JAVA:

如果您只是想添加一个Fragment 而不必担心重新创建它,那么我认为我写的这个添加Fragment 的方法可以帮到您。

public static void attachFragment ( int fragmentHolderLayoutId, Fragment fragment, Context context, String tag ) 


    FragmentManager manager = ( (AppCompatActivity) context ).getSupportFragmentManager ();
    manager.findFragmentByTag ( tag );
    FragmentTransaction ft = manager.beginTransaction ();

    if (manager.findFragmentByTag ( tag ) == null)  // No fragment in backStack with same tag..
        ft.add ( fragmentHolderLayoutId, fragment, tag );
        ft.addToBackStack ( tag );
        ft.commit ();
    
    else 
        for (Fragment frag : manager.getFragments())
          ft.hide(frag)
        
        ft.show ( manager.findFragmentByTag ( tag ) ).commit ();
    

科特林:

fun attachFragment(fragmentHolderLayoutId: Int, fragment: Fragment?, tag: String?) 
    val manager: FragmentManager = supportFragmentManager
    val ft: FragmentTransaction = manager.beginTransaction()
    if (manager.findFragmentByTag(tag) == null)  // No fragment in backStack with same tag..
        ft.add(fragmentHolderLayoutId, fragment!!, tag)
        ft.addToBackStack(tag)
        ft.commit()
     else 
        //Hide other fragments
        for (frag in manager.fragments)
            ft.hide(frag)
        
        //Shows the selected fragment.
        ft.show(manager.findFragmentByTag(tag)!!).commit()
    

【讨论】:

还需要隐藏其他片段,否则show 看起来不起作用【参考方案4】:

    为您的 Fragment 使用简单的ArrayList&lt;Fragment&gt;,并按顺序添加它们,这样您就知道 get(0) 将获得 F1,get(1) 将获得 F2,等等。

    将片段创建为单例。在每个片段中添加一个静态字段和方法:

     private static Fragment mMyInstance = null;
    
     public static Fragment newInstance() 
         if (mMyInstance == null) 
             mMyInstance = new F1();
         
         return mMyInstance;
     
    

    使用静态方法创建 Fragments 并将它们添加到 ArrayList。

    在每个 Fragment 中,将 setRetainInstance(true); 命令添加到 onCreate() 方法中。

现在当你用 FragmentManager 添加 Fragment 时,onCreate() 只会在第一次被调用,而onCreateView() 每次都会被调用。您希望每次都膨胀视图并连接小部件,以防万一您的 Activity 由于配置更改而重新创建。但是您可以检查您添加的内容是否是第一次,如果不是,则将小部件重置为之前的状态。因此,您将需要片段中的成员变量来跟踪它们的状态。覆盖 onStop() 以保存状态,并在连接小部件后在 onCreateView() 中重新应用它。

然后当侧边栏按钮被按下时,你会得到与那个按钮相对应的 Fragment,移除之前的 Fragment,然后用 FragmentManager 添加当前的 Fragment(或者只使用 replace() 命令而不是 remov()/add ())。

【讨论】:

自己保存片段的数组列表被认为是一种不好的做法。为什么?考虑到用户改变方向,ui 将被销毁等。您仍然会引用不存在的片段,这会导致严重的内存泄漏,在某些情况下会导致应用程序崩溃。 PS:如果您使用的是 supportFragmentManager,您可以使用 SupportFragmentManager..getFragments() 轻松访问它们,并确定您想要的片段是否已经存在或者您必须创建一个新片段。您还将避免内存泄漏。我建议使用设计用于片段操作的东西;) 你不应该在带有 UI 的片段上调用“setRetainInstance(true)”。【参考方案5】:

如果您使用的是支持片段,那么这个静态方法就可以完成这项工作。

    /**
 * Takes a Fragment TAG and tries to find the fragment in the manager if it exists and bring it to front.
 * if not, will return false;
 * @param manager
 * @param tag
 */
public static boolean resurfaceFragment(FragmentManager manager, String tag )
    Fragment fragment = manager.findFragmentByTag(tag);
    FragmentTransaction transaction = manager.beginTransaction();
    if (fragment!=null)
        for (int i = 0; i < manager.getFragments().size(); i++) 
            Fragment f =  manager.getFragments().get(i);
            transaction.hide(f);

        
        transaction.show(fragment).commit();
        return true;
    

    return false;

【讨论】:

fragmentManager.getFragments() 给出警告,它只能在同一个库组内调用

以上是关于将 Fragment 放在前面(无 Fragment Recreation)的主要内容,如果未能解决你的问题,请参考以下文章

Fragmen直接来回切换deno

Fragment 使用详解

android Fragment

如何将数据从Activity返回到Fragment

如何在同一页面上的 Fragment 内创建具有 3 个 Fragment 的 ViewPager?

Fragment的基本应用