如何保存和重复使用同一片段的实例?

15

我最近开始使用片段,并创建了一个演示应用程序,看起来像这样:

Demo App

点击每个按钮将切换到 Fragment 1、Fragment 2 和 Fragment 3。

我想要实现的是,只有每个 Fragment 的一个实例并重用它。(请注意,所有的片段都是动态创建和添加的。)我目前是通过创建 Fragment 的 HashMap 并放置每个实例以及从中获取它来实现这一点。

所以我的问题是: 有没有更好的方法来实现这一点: 通过使用 FragmentManager 的 putFragment(...) 方法?putFragment (Bundle bundle, String key, Fragment fragment) 我无法弄清楚如何在我的情况下使用它。如果有人可以给我一个如何使用此方法的示例,那就太好了。

保持对每个片段的引用在我的活动中是否会消耗大量资源? 这是否会使所有片段保持活动状态?我正在使用软引用来解决这个问题,但我不确定这是否是正确的做法。请告诉我是否有其他更好的方法来实现这一点或让我知道这是最好的方法。

提前感谢您的帮助。

以下是我的代码:

更新: 我正在尝试从后堆栈中重用片段,仅在后堆栈中不存在它们时才添加它们。在我离开 Fragment 1 -> 回来 -> 然后尝试按返回按钮时,下面的代码会给我一个非法状态异常:

10-28 13:21:40.255: ERROR/MessageQueue-JNI(3548): java.lang.IllegalStateException: Fragment already added: FragmentOne{423db570 #0 id=0x7f050006 fragOne}

public class MainActivity extends Activity implements View.OnClickListener {

private Button btnOne;
private Button btnTwo;
private Button btnThree;

 /* Called when the activity is first created.
  */
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    initialize();
    if(findViewById(R.id.fl) != null){
        if(savedInstanceState != null)
            return;
    }

    FragmentManager.enableDebugLogging(true);
    updateView("fragOne");
}

private void initialize(){
    btnOne = (Button) findViewById(R.id.button1);
    btnTwo = (Button) findViewById(R.id.button2);
    btnThree = (Button) findViewById(R.id.button3);
    btnOne.setOnClickListener(this);
    btnTwo.setOnClickListener(this);
    btnThree.setOnClickListener(this);
    fragHolder = new HashMap<String, SoftReference<Fragment>>();
}

private void updateView(String tag){
    FragmentTransaction ft = getFragmentManager().beginTransaction();
    Fragment frag = getFragmentManager().findFragmentByTag(tag);

    boolean addToStack = true;
    if(frag == null){
        if(tag.equals("fragOne"))
            frag = new FragmentOne();    
        else if(tag.equals("fragTwo"))
            frag = new FragmentTwo();
        else if(tag.equals("fragThree"))
            frag = new FragmentThree();
    }else{
        //Don't add to back stack
        addToStack = false;
    }
    ft.replace(R.id.fl, frag, tag);
    if(addToStack)
        ft.addToBackStack(null);
    ft.commit();
}

    @Override
    public void onClick(View v) {
        switch(v.getId()){
            case R.id.button1:
                updateView("fragOne");
                break;
            case R.id.button2:
                updateView("fragTwo");
                break;
            case R.id.button3:
                updateView("fragThree");
                break;
        }
    }
}

你有研究过FragmentTransactions吗?它们可能对你有帮助。 - Andrew Schuster
2个回答

2
为了演示一个FragmentTransaction,以下示例可能对您有所帮助。
首先,您需要在活动的onCreate()中进行所有初始化工作,这一点您已经做对了,但我们会做一些改变。
public class MainActivity extends Activity implements View.OnClickListener {

private Button btnOne;
private Button btnTwo;
private Button btnThree;

/* Called when the activity is first created.*/
@Override
public void onCreate(Bundle savedInstanceState) 
{
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    initialize();
    if(findViewById(R.id.fl) != null)
    {
        if(savedInstanceState != null)
        {
            FragmentTransaction trans = getFragmentManager().beginTransaction();

            //This is where we add our first fragment
            trans.add(R.id.fl, new FragmentOne());
            trans.commit();
        }
    }
}

private void initialize()
{
    btnOne = (Button) findViewById(R.id.button1);
    btnTwo = (Button) findViewById(R.id.button2);
    btnThree = (Button) findViewById(R.id.button3);
    btnOne.setOnClickListener(this);
    btnTwo.setOnClickListener(this);
    btnThree.setOnClickListener(this);
}

public void onClick(View view)
{
    //Here is where we'll actually transfer the fragments

    FragmentTransaction trans = getFragmentManager().beginTransaction();
    switch(v.getId()){
        case R.id.button1:
            trans.replace(R.id.fl, new FragmentOne());
            trans.addToBackStack(null);
            trans.commit();
            break;
        case R.id.button2:
            trans.replace(R.id.fl, new FragmentTwo());
            trans.addToBackStack(null);
            trans.commit();
            break;
        case R.id.button3:
            trans.replace(R.id.fl, new FragmentThree());
            trans.addToBackStack(null);
            trans.commit();
            break;
    }
}

这将使您能够轻松地从一个片段过渡到下一个。

感谢您的回答。我最初尝试过这种方法,但是这会将每笔交易添加到后退堆栈中,导致每笔交易的大小不断增加。正如我在实际应用中提到的,内存是一个问题。因此,我希望限制后退堆栈中的条目数量。我想更好的问题应该是:是否可以将后退堆栈用作FIFO队列->在添加第11个事务时丢弃第1个事务,以此类推保留10个事务? - Naveed
1
当您在后退栈上有太多内容时,可以调用 disallowAddToBackStack() 并跟踪静态整数或其他内容。 - Andrew Schuster
谢谢,这也解决了我遇到的非法状态异常问题。仍然需要解决的是,如果已经在同一片段上,则不要添加相同的片段,但这很简单。 - Naveed

1

FragmentManager负责自己的内存管理。根据其逻辑,它将杀死/重新创建或保留您的实例。您可以使用onSaveInstanceState()确保片段的状态被保存。

或者您可以使用setRetainInstance(true)强制系统保持您的实例活动在Fragment中。

以下是如何创建事务。

    FragmentTransaction fragmentTransaction = context.getSupportFragmentManager().beginTransaction();
    fragmentTransaction.replace(R.id.layout, new MyFragment(), f.getClass().getName());
    fragmentTransaction.commit();

2
谢谢你的回答。我之前尝试过这种方法,唯一的区别是,如果你在片段一中输入了一些内容,然后用片段二替换它,在导航回来时创建一个新的片段一实例会导致你失去那个更改,而不是重用相同的实例(在这种情况下,文本仍将存在)。 - Naveed

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