片段:删除视图中的所有片段

时间:2013-02-08 00:59:31

标签: android android-fragments

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

它基本上是应用程序中每个屏幕的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();

正如您所看到的,我正在努力的第二步是如何 如何删除给定Fragment中的所有View,而不知道您要删除哪些FragmentTransaction.replace()?我找到的最接近的是Fragment,它确实成功地为每个案例成功执行此操作,结果发现您正在用相同的片段替换FragmentTransaction.replace()。在这种情况下,它不会删除所有,然后添加(如文档建议),它似乎删除。这是使用兼容性库的问题还是不应该使用removeAllFragments()的方式?

无论如何,我该怎么做呢?我是否必须对FragmentTransaction.replace()函数进行编码以遍历每个片段并将其分离,或者是否有办法执行'二合一'{{1}}函数声称要执行的操作的前半部分?

5 个答案:

答案 0 :(得分: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();

此外,如果您多次从视图中删除所有片段,您可以考虑检查当前片段是否为空或isDetached()isRemoving(),或者您可能会NullPointerExceptions }。

更新getSupportFragmentManger().getFragments()的文档现在显然是hidden,但在我的代码中仍能正常运行。这是文档的屏幕截图:

enter image description here

话虽如此,由于它是隐藏的,因此不再需要使用此方法,请参阅下面的更新。

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

  1. 在创建时为每个fragment提供tagid,并根据需要对其进行迭代以处理每个fragment
  2. 使用onAttachListener创建一个监听器,因此每次将新fragment附加到activity时,您都可以存储fragment,然后遍历该数据结构根据需要处理每个fragment
  3. 如果不使用getSupportFragmentManager(),要处理交易,您需要使用getFragmentManager()

答案 1 :(得分:7)

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

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

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

setupScreen(pane1, pane2);

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

答案 2 :(得分:3)

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

List<Fragment> fragments = getSupportFragmentManager().getFragments();
if (fragments != null) {
    for (Fragment fragment : fragments) {
        getSupportFragmentManager().beginTransaction().remove(fragment).commit();
    }
}

答案 3 :(得分:1)

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

我通过使用下面的代码确认了这一点,通过API on API11 +,Android兼容性库和ActionBarSherlock实现了所需的行为。它只会在最后一个例子中中断。

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

}

答案 4 :(得分: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);
    }

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