2012-02-08 87 views
43

我使用以下代碼推片段堆棧上的片段:彈出片段返回堆棧而不播放彈出式動畫

FragmentManager fragmentManager = getActivity().getSupportFragmentManager(); 
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); 
fragmentTransaction.setCustomAnimations(R.anim.slide_in_right, R.anim.slide_out_right, 
    R.anim.slide_in_left, R.anim.slide_out_left); 
fragmentTransaction.replace(getId(), newFragment); 
fragmentTransaction.addToBackStack(null); 
fragmentTransaction.commit(); 

這樣,當所述片段棧被彈出,例如通過按下後退按鈕,播放片段流行動畫。然而,在某些情況下,我想彈出碎片疊加而不顯示該動畫,例如因爲我剛剛從另一個活動返回,並且想要一次顯示前一個片段,而沒有動畫。

一個例子導航可能看起來像這樣:

  • 用戶是開始畫面與根片段上
  • 赫根片段,其然後顯示一個新的片段,以顯示細節選擇項目該項目。它使用片段事務處理來爲推送和流行案例設置動畫(所以當用戶按下後退按鈕時,過渡是動畫的)
  • 從這個片段他開始一個活動(無論何種原因)刪除剛顯示的項目
  • 當此活動結束時,我想返回根片段,而不顯示「細節片段」

的「流行動畫」有沒有辦法彈出片段沒有播放指定的流行動畫的後臺堆棧?

+0

我剛剛從其他活動返回時表示什麼意思?你能否告訴過渡步驟,即你如何嘗試導航。 – 500865 2012-02-16 18:13:56

+0

嗨500865,我給這個問題添加了一個示例導航。 – ChristophK 2012-02-17 08:46:25

+0

不會將setCustomAnimations中的第3個和第4個參數設置爲0嗎? – Boy 2012-10-11 05:45:23

回答

72

所以Warpzit是在正確的軌道上,他只是沒有滿足您的特定問題太清楚了。我遇到了完全相同的問題,這是我如何解決它。

首先我創建了一個靜態布爾變量(爲簡單起見,讓我們把它放在FragmentUtils類)...

public class FragmentUtils { 
    public static boolean sDisableFragmentAnimations = false; 
} 

然後,你有充分的片段,你需要重寫onCreateAnimation方法。 ..

@Override 
public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) { 
    if (FragmentUtils.sDisableFragmentAnimations) { 
     Animation a = new Animation() {}; 
     a.setDuration(0); 
     return a; 
    } 
    return super.onCreateAnimation(transit, enter, nextAnim); 
} 

然後,當你需要從你的活動清除堆棧中根本就以下...

public void clearBackStack() { 
    FragmentUtils.sDisableFragmentAnimations = true; 
    getSupportFragmentManager().popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE); 
    FragmentUtils.sDisableFragmentAnimations = false; 
} 

瞧,對clearBackStack()的調用將會讓你回到沒有任何轉換動畫的根片段中。

希望大G會在未來增加一個不那麼愚蠢的做法。

+6

這個工程,但片段仍然會顯示一個簡短的幀。有沒有辦法讓它不出現? – plackemacher 2012-09-07 22:32:48

+1

+1似乎是2.3和4.3中唯一適用於我的東西:) – Dori 2013-10-21 16:43:35

+7

+1必須記住,這隻適用於popBackStackImmediate(它不適用於popBackStack)。 – Tiago 2013-11-29 03:19:59

0

只需使用另一重載方法的setCustomAnimation()並在其中沒有設置R.anim.slide_out ,這將解決您的問題

乾杯:)

+0

然而,這將防止任何從動畫播放用戶按下時後退按鈕,不是嗎?我想在默認情況下(用戶按下後退按鈕)的動畫,但有一種情況下,片段堆棧響應按鈕按下彈出,在這種情況下,我不想動畫。 – ChristophK 2012-02-14 08:56:21

4

的用戶是在用根片段開始屏幕

可以說活動A中包含根片段。

他在根片段上選擇一個項目,然後顯示一個新片段以顯示該項目的詳細信息。它使用片段事務處理來爲推送和流行案例設置動畫(所以當用戶按下後退按鈕時,過渡是動畫的)

事務被添加到後退堆棧。這意味着當從詳細片段中按下後退按鈕時,彈出過程被動畫化。

從這個片段他開始一個活動(無論什麼原因)刪除剛剛顯示的項目。

可以說這是活動B

當該活動完成時,我想返回到根片段,而沒有顯示「詳細片段」

的「流行動畫」

獲取此行爲的一種方法是通過在活動B中執行此操作:

Intent intent = new Intent(this, A.class); 
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); 
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 
startActivity(intent); 
finish(); 

這會開始e活動A根據documentation將其重置爲其根狀態(檢查「此啓動模式也可以與FLAG_ACTIVITY_NEW_TASK一起使用效果良好:......」部分中的最後一段)

利用這種配置,動畫將出現在默認的情況下,而在特殊情況下,你可以控制使用動畫:

intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION); 

其中沒有任何動畫開始新的活動。如果您確實需要任何動畫,可以使用overridePendingTransition方法進行。

+0

感謝您的回答!我的特定應用只有一個問題:活動A包含在由我的「主要活動」(我們稱之爲C)創建的TabHost中。有沒有辦法重置A而不重置C? – ChristophK 2012-02-17 14:20:11

+0

如果您查看我在答案中發佈的鏈接中的第一段,您會看到「頂部的所有其他活動都將被關閉」。由於C在堆棧中低於A(Tabhost Activity將位於堆棧底部),因此它不會重置C. – 500865 2012-02-17 14:36:43

+0

不幸的是,這不起作用:當我在活動B中運行代碼時,活動A不會顯示在其選項卡中,而是顯示爲「全屏」 – ChristophK 2012-02-17 14:52:09

6

所以對以下工作的支持庫:

在其中應該有一個自定義彈出動畫您自己的自定義一個覆蓋onCreateAnimation的片段。你可以得到它並根據你想要的設置一些參數。可能需要做一些額外的工作才能使它與常規碎片一起工作。

這裏就是我重寫它,改變設定時間段的例子:

@Override 
public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) { 
    Animation anim = (Animation) super.onCreateAnimation(transit, enter, nextAnim); 
    if(!enter) { 
     if(anim != null) { 
      anim.setDuration(0); // This doesn't seem to be called. 
      return anim; 
     } else { 
      Animation test = new TestAnimation(); 
      test.setDuration(0); 
      return test; 
     } 
    } 
    return anim; 
} 

private class TestAnimation extends Animation { 

} 
+0

warpzit的答案其實是正確的,只是沒有解釋超好...因爲我不能在評論中發佈代碼,我要提交另一個答案... – Geoff 2012-06-28 22:36:53

0

在回答你的問題之前,我需要自己提問。

在第二個活動的onBackPressed()方法中,您可以訪問第一個活動的backstack嗎?

如果是,那麼你可以調用popBackStackImmediate(String trnaisiotnName,int inclusive),它將從後臺堆棧中移除片段轉換,並且你不需要擔心動畫。

我假設你可以訪問以前活動的堆棧中,否則這不會工作

-1

回覆傑夫和plackemacher評論。

您可以嘗試刪除此片段中的所有視圖。然後片段將顯示,但它應該是透明的。

刪除所有-1(我用的導航抽屜所以抽屜片段應留)片段:

int size = fragmentsList.size()-1; 

    FragmentTransaction transaction = fragmentManager.beginTransaction(); 
    transaction.setTransition (FragmentTransaction.TRANSIT_NONE); 

    Fragment fragment; 

    for (int i = size ; i > 0 ; i--) 
    { 
     fragment = fragmentsList.get (i); 
     if(fragment != null) 
     { 
      View viewContainer = fragment.getView(); 
      if (viewContainer != null) 
      { 
       ((ViewGroup) viewContainer).removeAllViews(); 
      } 
      transaction.remove (fragment); 
     } 
    } 

    size = fragmentManager.getBackStackEntryCount(); 

    for (int i = 0; i < size ; i++) 
    { 
     fragmentManager.popBackStack (null, FragmentManager.POP_BACK_STACK_INCLUSIVE); 
    } 

對不起,我的英語

0

這是很容易通過overridePendingTransition(int enterAnim, int exitAnim)實現既0對於沒有動畫。

FragmentManager fm = getSupportFragmentManager(); 
if (fm.getBackStackEntryCount() > 0) { 
    fm.popBackStack(); 
    overridePendingTransition(0, 0); 
} 
+1

這隻適用於如果你正在處理活動。問題是指片段。 – LightMan 2016-10-14 20:22:45

1

所以,我想提出一個小的變化,以@傑夫的回答。

而不是有一個全局靜態布爾值,我寧願有一個本地非靜態。這是我想出的。

創建界面

public interface TransitionAnimator { 
    void disableTransitionAnimation(); 
    void enableTransitionAnimation(); 
} 

使該片段實現該接口。

public class MyFragment extends Fragment implements TransitionAnimator { 

    private boolean mTransitionAnimation; 

    @Override 
    public void disableTransitionAnimation() { 
     mTransitionAnimation = false; 
    } 

    @Override 
    public void enableTransitionAnimation() { 
     mTransitionAnimation = true; 
    } 

    @Override 
    public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) { 
     Animation result; 
     if (!mTransitionAnimation) { 
      Animation dummyAnimation = new Animation() { 
      }; 
      dummyAnimation.setDuration(0); 
      result = dummyAnimation; 
     } else { 
      result = super.onCreateAnimation(transit, enter, nextAnim); 
     } 
     return result; 
    } 

然後,當你需要禁用過渡動畫的一個片段,只是做

if (fragment instanceof TransitionAnimator) { 
    ((TransitionAnimator) fragment).disableTransitionAnimation(); 
} 

啓用它們,只是做

if (fragment instanceof TransitionAnimator) { 
    ((TransitionAnimator) fragment).enableTransitionAnimation(); 
} 

如果你想要做的相同的片段管理器中的所有片段,只要做

List<Fragment> fragments = getSupportFragmentManager().getFragments(); 
for (Fragment fragment : fragments) { 
    if (fragment instanceof TransitionAnimator) { 
     // disable animations 
     ((TransitionAnimator) fragment).disableTransitionAnimation(); 
    } 
} 

非常相似,但沒有靜態字段。

0

這是@ Geoff的出色答案的後續,但適合更加動態和真實的場景。

我想這是一個很好的小貼子,但我現在意識到它有一點失控。但是,代碼就在那裏,我發現它非常有用,不過它涵蓋的不僅僅是如何禁用過渡動畫。

通常,當我使用片段時,我喜歡將BaseFragment附加到BaseActivityCallback上。這BaseActivityCallback可以通過我的碎片可以用來在上面添加一個新片段的本身,甚至彈出的碎片在其下方,因此希望禁用彈出動畫 - 或彈出默默

interface BaseActivityCallback 
{ 
    void addFragment (BaseFragment f, int containerResId); 
    void popFragment (boolean silently); 
} 

class BaseActivity extends android.support.v4.app.FragmentActivity implements BaseActivityCallback 
{ 
    public void addFragment (BaseFragment f, int containerResId) 
    { 
     FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); 
     ft.setCustomAnimations(R.anim.enter, R.anim.exit, R.anim.enter, R.anim.pop_exit); // http://stackoverflow.com/a/17488542/2412477 
     ft.addToBackStack(DEFAULT_FRAGMENT_STACK_NAME); 
     ft.replace(containerResId, fragment); 
     ft.commitAllowingStateLoss(); 
    }   

    public void popFragment (boolean silently) 
    { 
     FragmentManager fm = getSupportFragmentManager();     
     if (silently) { 
      int count = fm.getFragments().size(); 
      BaseFragment f = (BaseFragment)fm.getFragments().get(count-1); 
      f.setDisableTransitionAnimations(true); 
     } 
     fm.popBackStackImmediate(); 
    } 
} 

public abstract class BaseFragment extends android.support.v4.app.Fragment 
{ 
    private static final String TAG = "BaseFragment"; 
    private final String STATE_DISABLE_TRANSITION_ANIMATIONS = TAG+".stateDisableTransitionAnimations"; 

    protected BaseActivityCallback baseActivityCallback; 
    private boolean disableTransitionAnimations;  

    @Override 
    public void onCreate (@Nullable Bundle savedInstanceState) 
    { 
     super.onCreate(savedInstanceState); 
     disableTransitionAnimations = (savedInstanceState==null ? false : savedInstanceState.getBoolean(STATE_DISABLE_TRANSITION_ANIMATIONS, false)); 
    } 

    @Override 
    public void onAttach (Context context) 
    { 
     super.onAttach(context); 
     baseActivityCallback = (BaseActivityCallback)context; 
    } 

    @Override 
    public void onSaveInstanceState (Bundle outState) 
    { 
     super.onSaveInstanceState(outState); 
     outState.putBoolean(STATE_DISABLE_TRANSITION_ANIMATIONS, disableTransitionAnimations); 
    } 

    @Override 
    public Animation onCreateAnimation (int transit, boolean enter, int nextAnim) 
    { 
     if (disableTransitionAnimations) { 
      Animation nop = new Animation(){}; 
      nop.setDuration(0); 
      return nop; 
     } 
     return super.onCreateAnimation(transit, enter, nextAnim); 
    } 

    public void setDisableTransitionAnimations (boolean disableTransitionAnimations) 
    { 
     this.disableTransitionAnimations = disableTransitionAnimations; // http://stackoverflow.com/a/11253987/2412477 
    } 
} 

現在你可以創建你MainActivity,並有顯示Fragment1可以添加其他Fragment2這又會彈出Fragment1默默

public class MainActivity extends BaseActivity 
{ 
    protected void onCreate (Bundle savedInstanceState) 
    { 
     setContentView(R.layout.main_activity); 
     ... 
     if (getSupportFragmentManager().getFragments() != null && !getSupportFragmentManager().getFragments().isEmpty()) { 

      addFragment(FragmentA.newInstance(), R.id.main_activity_fragment_container); 
     } 
    } 
    ... 
} 

public class FragmentA extends BaseFragment 
{ 
    public View onCreateView (LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) 
    { 
     ViewGroup root = (ViewGroup)inflater.inflate(R.layout.fragment_a, container, false); 
     ... 
     root.findViewById(R.id.fragment_a_next_button) 
      .setOnClickListener(new View.OnClickListener() { 
       public void onClick (View v) { 
         baseActivityCallback.addFragment(FragmentB.newInstance(), R.id.main_activity_fragment_container); 
       } 
      }); 
    } 
} 

public class FragmentB extends BaseFragment 
{ 
    public View onCreateView (LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) 
    { 
     ViewGroup root = (ViewGroup)inflater.inflate(R.layout.fragment_b, container, false); 
     ... 
     root.findViewById(R.id.fragment_b_pop_silently_button) 
      .setOnClickListener(new View.OnClickListener() { 
       public void onClick (View v) { 
         baseActivityCallback.popFragment(true); 
       } 
      }); 
    } 
} 
0

覆蓋該片段中的你想要在沒有動畫的情況下彈出,並且在輸入時仍然保留動畫

@Override 
public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) { 
    if(!enter){ 
     Animation a = new Animation() {}; 
     a.setDuration(0); 
     return a; 
    } 
    return super.onCreateAnimation(transit, enter, nextAnim); 
}