将Fragment置于前台(无需重新创建Fragment)

27

我有三个片段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 部分中我应该写什么才能将已经创建的片段带到视图层次结构的前面

请帮忙,我已经卡了两天了。


你有多少种类型的片段? - Triode
@Rajesh CP 所有片段都仅从 FRAGMENT 类扩展,没有其他类型的片段。 - Nitin Misra
我的回答有帮助吗? - Rick Falck
5个回答

13

我会把这段代码放在活动类(activity class)中,该活动类必须有一个id为R.id.fragment_containerFrameLayout

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 应该为每个片段实现 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了:(从开发人员文档中可以看到: 只有在包含此事务的活动保存其状态之前,才能使用此方法提交事务。如果在此之后尝试提交,则会抛出异常。 - Abc.Xyz
@developer1011:问题没有说明在活动恢复时是否应该恢复状态。从文档中得知:commitAllowingStateLoss() 类似于commit(),但允许在保存活动状态后执行提交。注意:谨慎使用! - Solata

6

通过标签获取片段,然后替换到容器中。

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>);

我检查了您提供的代码,但它重新创建了片段并放置在视图层次结构的顶部,而不是将先前创建的片段带回来。 - Nitin Misra
仍然没有用,因为当我尝试显示先前创建的线程时,代码没有效果。我已经执行了以下操作:Fragment existingFragment = (CategoriesFragment)fragmentManager.findFragmentByTag("apps"); transaction.show(existingFragment); - Nitin Misra

4

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 ();
    }
}

Kotlin:

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()
    }
}

1
还需要隐藏其他片段,否则显示似乎无法正常工作。 - Jemshit Iskenderov

1
  1. 使用简单的 ArrayList<Fragment> 存储您的 Fragment, 按顺序添加它们,这样您就知道 get(0) 将获取 F1,get(1) 获取 F2 等等。

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

    private static Fragment mMyInstance = null;
    
    public static Fragment newInstance() {
       if (mMyInstance == null) {
           mMyInstance = new F1();
       }
       return mMyInstance;
    }
    
  3. 使用静态方法创建 Fragments,并将它们添加到 ArrayList 中。

  4. 在每个 Fragment 的 onCreate() 方法中添加 setRetainInstance(true); 命令。

当您使用FragmentManager添加Fragment时,onCreate()只会在第一次调用,但每次都会调用onCreateView()。您需要每次充气视图并连接小部件,以防Activity由于配置更改而重新创建。但是,您可以检查添加的某些内容是否为第一次,如果不是,则将小部件重置为其先前的状态。因此,您需要在Fragments中使用成员变量来跟踪它们的状态。覆盖onStop()以保存状态,并在onCreateView()中重新应用它,以在连接小部件后更新其状态。
然后,当侧边栏按钮被按下时,您会得到与该按钮对应的Fragment,删除先前的Fragment,并使用FragmentManager添加当前Fragment(或者只需使用replace()命令而非remov()/add())。

2
保留自己的片段数组列表被认为是一种不好的做法。为什么?考虑到用户更改方向,UI 将被销毁等情况。你仍然会有对不存在的片段的引用,这会导致讨厌的内存泄漏,并在某些情况下导致应用程序崩溃。PS:如果您正在使用 supportFragmentManager,则可以使用 SupportFragmentManager..getFragments() 轻松访问它们,并查找要使用的片段是否已存在或者您必须创建一个新的片段。您还将避免内存泄漏。我建议使用为片段操作设计的内容。;) - vanomart
在具有UI的片段上,不应调用´setRetainInstance(true)´。 - Tobias Lindberg

0
如果您正在使用支持片段,则此静态方法会完成工作。
    /**
 * 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() 给出了警告,它只能从同一库组内调用。 - Jemshit Iskenderov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接