11

我面临的场景是在我的应用程序中,我有一个单窗格双窗格样式布局。对于每种不同样式的布局,我没有单独处理屏幕之间可能的每一个导航操作,而是使用一个函数,它可以在给定所需屏幕时正确设置布局。

它基本上是switch应用程序中每个屏幕的语句,每个屏幕中都有一个嵌套switch语句来处理每种布局样式。这就是我在代码中所说的:

protected void setupScreen() {
    switch(currentScreen) {
    case SCREEN_ONE:
        switch(currentLayout) {
        case SINGLE_PANE:
            // Perform actions to setup the screen
            break;
        case DUAL_PANE:
            // Perform actions to setup the screen
            break;
        }
        break;
    case SCREEN_TWO:
        switch(currentLayout) {
        case SINGLE_PANE:
            // Perform actions to setup the screen
            break;
        case DUAL_PANE:
            // Perform actions to setup the screen
            break;
        }
        break
    // ... etc ....
    }
}

在我要执行设置屏幕操作的部分中,这包括以下三个基本操作:

// Create the fragments if necessary
if (screenFragment == null) {
    screenFragment = new myFragment();
}

// Remove the existing fragments from the layout views
// HOW???

// Add the fragments for this screen to the view
getSupportFragmentManager().beginTransaction().add(pane1.getId(), myFragment, "myFragment").commit();

如您所见,我正在努力的是如何进行第二步。在不确切知道要删除哪些的情况下,如何从给定中删除所有Fragments ?View我发现的最接近的是FragmentTransaction.replace()它确实为每种情况都成功地做到了这一点,事实证明你正在用Fragment相同的片段替换 a 。在这种情况下,它不会全部删除,然后添加(如文档建议的那样),它似乎只是删除了。这是使用兼容性库的问题还是不FragmentTransaction.replace()应该使用的方式?

无论如何,我应该怎么做呢?我是否必须编写一个removeAllFragments()函数来遍历每个片段并将其分离,或者有没有办法完成“二合一”FragmentTransaction.replace()函数声称要做的前半部分?

4

5 回答 5

19

其他答案都没有真正为我工作。这是我所做的:

List<Fragment> al = getSupportFragmentManager().getFragments();
if (al == null) {
   // code that handles no existing fragments
   return;
}

for (Fragment frag : al)
{
   // To save any of the fragments, add this check.
   // A tag can be added as a third parameter to the fragment when you commit it
   if (frag == null || frag.getTag().equals("<tag-name>")) {
      continue;
   }

   getSupportFragmentManager().beginTransaction().remove(frag).commit(); 
}

或者,如果您被迫使用它(但不推荐):

.commitAllowingStateLoss();

此外,如果您多次从视图中删除所有片段,您可能会考虑检查当前片段是否为 null 或isDetached()orisRemoving()或者您可能会得到NullPointerExceptions.

更新: 的文档现在getSupportFragmentManger().getFragments()显然是隐藏的,但在我的代码中仍然可以正常工作。这是文档的屏幕截图:

在此处输入图像描述

话虽如此,由于它是隐藏的,他们不再希望使用这种方法,所以请参阅下面的更新。

更新 8-4-15:如果您不使用片段的支持库,很遗憾没有getFragments()可用的库,但仍有一些更不方便的选项。

  1. 在创建时给每个fragmentatagid,并遍历它们以fragment根据需要处理每个。
  2. 创建一个侦听器onAttachListener,以便每次将新fragment的附加到 时activity,您可以存储它fragment,然后遍历该数据结构以fragment根据需要处理每个。

当不使用getSupportFragmentManager(), 来处理您需要使用的事务时getFragmentManager()

于 2014-06-19T15:25:35.353 回答
7

典型的机制是使用FragmentManager.findFragmentByTag(). 您使用它并将标签添加到您的片段(或 id 的替代品)。通过这种方式,您可以确定当前正在管理哪些片段。然后,一旦你有一个当前片段的句柄(findFragmentByTag 返回非空值),你就可以使用FragmentManager.beginTransaction()启动一个 FragmentTransaction 并删除/添加必要的片段。以这种方式工作将使您避免对要保留的片段进行“重新添加”过程。

我可能会做的是这样的代码:(警告伪代码)

Fragment pane1 = FragmentManager.findFragmentByTag("myFragmentPane1");
Fragment pane2 = FragmentManager.findFragmentByTag("myFragmentPane2");

setupScreen(pane1, pane2);

您还应该考虑班级的子班级,而不是“一个班级中的所有内容”。您有一个相当明显的 Martin Fowler 的Replace Conditional with Subclass案例。否则,我担心当您添加另一个屏幕时,这将难以管理。

于 2013-02-08T02:02:27.353 回答
3

如果你使用android.support.v4.app.Fragment你可以这样做:

List<Fragment> fragments = getSupportFragmentManager().getFragments();
if (fragments != null) {
    for (Fragment fragment : fragments) {
        getSupportFragmentManager().beginTransaction().remove(fragment).commit();
    }
}
于 2016-04-30T09:52:13.600 回答
1

原来FragmentTransaction.replace()是正确的操作,应该可以正常工作。它只是在使用时不起作用ActionBarSherlockSherlockFragmentActivity所以我只能假设它是这个兼容性库中的一个错误。

我已经通过使用下面的代码在 API11+、android 兼容性库和 ActionBarSherlock 上通过 Android 实现所需的行为来确认这一点。它只在最后一个实例中中断。

package com.test.test;

import com.actionbarsherlock.app.SherlockFragmentActivity;

import android.os.Bundle;
import android.content.Context;
import android.graphics.Color;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.LinearLayout.LayoutParams;
import android.widget.TextView;

public class MainActivity extends SherlockFragmentActivity {
  // Consistent fragment instance
    myFragment myFrag = null;

    // Views
    FrameLayout fl = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        LinearLayout ll = new LinearLayout(this);
        ll.setOrientation(LinearLayout.VERTICAL);

        Button b = new Button(this);
        b.setText("Repeat");
        b.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {

                // Reattach the same fragment
                getSupportFragmentManager().beginTransaction().replace(fl.getId(), myFrag).commit();

            }
        });

        fl = new FrameLayout(this);
        fl.setId(200);
        fl.setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));

        myFrag = new myFragment();
        getSupportFragmentManager().beginTransaction().add(fl.getId(), myFrag).commit();

        ll.addView(b);
        ll.addView(fl);

        setContentView(ll);
    }

    public static class myFragment extends Fragment {

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                Bundle savedInstanceState) {
            TextView tv = new TextView(getActivity());
            tv.setText("My fragment");
            tv.setGravity(Gravity.CENTER);
            tv.setBackgroundColor(Color.RED);

            return tv;
        }
    }

}
于 2013-02-09T06:08:23.383 回答
0

这或多或少是我处理这个问题的方式。让您的所有片段都实现类似以下的接口:

public interface NamedFragment{

    public FragmentName getFragmentName();

}

制作一个与您的片段相对应的枚举:

public enum FragmentName{

    SOME_FRAGMENT,
    SOME_OTHER_FRAGMENT;

}

然后在您的片段切换活动中:

// Get the manager and transaction separately so you can use both: 
    FragmentManager fragmentManager = getSupportFragmentManager();
    FragmentTransaction ft = fragmentManager.beginTransaction();

// Get a reference to the fragment(s) currently in the container(s)
    Fragment currentFragment = fragmentManager.findFragmentById(position);

    FragmentName cFragmentName =
            ((NamedFragment) currentFragment).getFragmentName();
    Fragment nextFragment =
            Fragment.instantiate(this, some_new_fragment_string);
    FragmentName nFragmentName =
            ((NamedFragment) nextFragment).getFragmentName();
// Compare the "names"
    if(cFragmentName != nFragmentName){
        ft.replace(position, nextFragment);
    }

你必须改变一些东西以适应你的细节。

于 2013-02-08T01:57:34.113 回答