2013-02-08 105 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' –