2013-02-08 56 views
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(); 

正如你所看到的,我正在努力的是如何做第二步。 如何您是否从给定的View中删除了所有Fragment s而不知道您想要删除哪些?我找到的最接近的是FragmentTransaction.replace(),它对每种情况都成功地做到了这一点:但是当你用同一个片段替换Fragment时。在这种情况下,它不会删除全部,然后添加(如文档建议),它似乎只是删除。这是使用兼容性库的问题吗?还是不应该使用FragmentTransaction.replace()

无论如何,我应该如何去做这件事?我是否需要编写一个removeAllFragments()函数来遍历每个片段并将其分离,或者是否有办法执行“二合一”功能声称要执行的操作的前半部分?

回答

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。否则,我担心这将是非常难以管理,当你添加另一个屏幕。

+0

+1,听起来很愚蠢,但从未想过使用标签来标识片段的放置位置,始终用它来标识片段的类型 –

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

你必须稍微改变一下,以适应你的具体情况。

+0

我的回答是有道理的,如果您只想在一个给定的时间类的一个实例; @尼克的是有道理的,如果你需要每个实例的唯一标识符。 – anthropomo

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

} 
+0

+1'Add'与'Replace'确实不同。 – VenomVendor

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

另外,如果你从视图下多次删除所有片段,你可能会考虑检查当前的frag是空的还是isDetached()isRemoving()或者你可能得到NullPointerExceptions

更新getSupportFragmentManger().getFragments()的文档显然是hidden现在,但仍然在我的代码中工作得很好。下面是对文件的截图:

enter image description here

说了这么多,因为它是隐藏的,他们不再想用这种方法,所以请参阅下面我更新。

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

  1. 给每个fragment一个在创建时tagid,并遍历它们,以根据需要处理每个fragment
  2. 创建使用onAttachListener所以每当一个新fragment附接到activity时间的监听器,则可以存储fragment,然后通过该数据结构迭代根据需要来处理每个fragment

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

+0

Downvoted,因为没有getFragments()API。 – sandrstar

+0

其实,有;它只是被隐藏起来,因为这个答案是写的。它仍然有效。尝试一下。 – craned

+0

这不是真的。它仅在4.3以后才在支持库中提供,但它不在android.app.FragmentManager中提供。答案是错误的方向,只适用于支持库(有关使用隐藏的API的其他提及)。 – sandrstar

3

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

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

不起作用?它说它需要'android.app.Fragment' –