Поп-фрагмент backstack без воспроизведения Pop-Animation
Я нажимаю фрагмент на стеке фрагментов, используя следующий код:
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();
Таким образом, когда стек фрагмента выталкивается, например. нажатием кнопки "Назад" воспроизводится анимация поп-фрагмента. Тем не менее, есть ситуации, в которых я хотел бы поместить фрагмент backstack, не показывая эту анимацию, например. потому что я только что вернулся из другого действия и хочу отобразить предыдущий фрагмент сразу, без анимации.
Пример навигации может выглядеть следующим образом:
- Пользователь находится на стартовом экране с корневым фрагментом
- Он выбирает элемент корневого фрагмента, который затем отображает новый фрагмент для отображения деталей этого элемента. Он делает это с помощью транзакции фрагмента, которая устанавливает анимацию как для нажатия, так и для поп-флага (поэтому, когда пользователь нажимает кнопку "Назад", переход анимируется)
- Из этого фрагмента начинается действие, которое (по какой-либо причине) удаляет только что показанный объект
- Когда это действие завершается, я хотел бы вернуться к корневому фрагменту, не показывая "поп-анимацию" фрагмента детали
Есть ли способ вытащить фрагмент фрагмента без воспроизведения указанной поп-анимации?
Ответы
Ответ 1
Итак, 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);
}
Затем, когда вам нужно очистить backstack от вашей активности, просто выполните следующие действия:
public void clearBackStack() {
FragmentUtils.sDisableFragmentAnimations = true;
getSupportFragmentManager().popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
FragmentUtils.sDisableFragmentAnimations = false;
}
И voila, вызов clearBackStack() вернет вас обратно в корневой фрагмент без каких-либо переходных анимаций.
Надеюсь, что большой G добавит менее глупый способ сделать это в будущем.
Ответ 2
Итак, для библиотеки поддержки следующие работы:
В фрагменте, который должен иметь пользовательскую поп-анимацию, вы переопределяете 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 {
}
Ответ 3
Пользователь находится на стартовом экране с корневым фрагментом
Допустим, корневой фрагмент содержится в Деятельности А.
Он выбирает элемент в корневом фрагменте, который затем отображает новый фрагмент, чтобы показать детали этого элемента. Для этого используется транзакция фрагмента, которая устанавливает анимацию как для push, так и для всплывающего окна (поэтому, когда пользователь нажимает кнопку "назад", переход анимируется)
Транзакция добавляется в задний стек. Это означает, что при нажатии кнопки "Назад" из фрагмента детали процесс всплытия анимируется.
С этого фрагмента он начинает действие, которое (по какой-либо причине) удаляет только что показанный элемент.
Допустим, это деятельность 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();
Это запустит действие A, сбрасывая его в его корневое состояние в соответствии с документацией (проверьте последний абзац в разделе, который говорит: "Этот режим запуска также может быть использован для достижения хорошего эффекта в сочетании с FLAG_ACTIVITY_NEW_TASK:......" )
При такой конфигурации анимация будет присутствовать в случае по умолчанию, тогда как в особом случае вы можете управлять анимацией, используя:
intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
Который начинает новую деятельность без анимации. Если вам нужна анимация, вы можете сделать это с помощью метода overridePendingTransition
.
Ответ 4
Итак, я хотел бы предложить небольшое изменение для ответа @Geoff.
Вместо того, чтобы иметь глобальное статическое логическое значение, я бы предпочел локальный нестатический. Это то, что я придумал.
Создать интерфейс
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();
}
}
Очень похоже, но без статических полей.
Ответ 5
Просто используйте другой перегруженный метод setCustomAnimation()
и в котором не устанавливайте R.anim.slide_out
и это решит вашу проблему.
Приветствия:)
Ответ 6
Прежде чем ответить на ваш вопрос, мне нужно задать вопрос сам.
В методе onBackPressed() второго действия вы можете получить доступ к стопке первого действия?
Если да, то вы можете вызвать popBackStackImmediate (String trnaisiotnName, int inclusive), и он удалит переход фрагмента из backstack, и вам не нужно беспокоиться об анимации.
Я предполагаю, что вы можете получить доступ к предыдущей активности, в противном случае это не будет работать
Ответ 7
Это довольно легко достичь через overridePendingTransition(int enterAnim, int exitAnim)
с помощью 0
без анимации.
FragmentManager fm = getSupportFragmentManager();
if (fm.getBackStackEntryCount() > 0) {
fm.popBackStack();
overridePendingTransition(0, 0);
}
Ответ 8
Это продолжение превосходного ответа на @Geoff, но для более динамичного и реального сценария.
Я подумал, что это хороший маленький пост, но теперь я понимаю, что он немного вышел из-под контроля. Тем не менее, код есть все, и я считаю его действительно полезным, хотя он охватывает гораздо больше, чем просто отключить анимацию перехода.
Обычно, когда я работаю с Fragments, мне нравится иметь 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
, который может в свою очередь pop 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 );
}
});
}
}
Ответ 9
Отмените это в фрагменте, который вы хотите вывести без анимации, и сохраняйте анимацию при вводе
@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);
}
Ответ 10
Более простое решение:
for (fragment in supportFragmentManager.fragments) {
removeFragment(fragment)
}
if (supportFragmentManager.backStackEntryCount > 0) {
supportFragmentManager.popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE)
}
Ответ 11
На самом деле, у Android теперь есть способ сделать это без ответа @Geoff.
Чтобы анимация не запускалась в popBackStack()
, при накачивании вашего fragment
добавьте .setReorderingAllowed(true)
к вашему fragmentTransaction
.setReorderingAllowed(true)
.
Так, например:
supportFragmentTransaction.beginTransaction()
.setReorderingAllowed(true)
.addToBackStack(null)
.setCustomAnimations(
android.R.anim.fade_in,
android.R.anim.fade_out,
android.R.anim.fade_in,
android.R.anim.fade_out
)
.replace(yourContainer.id, yourFragment)
.commit()
Вы заметите, что если вы установите setReorderingAllowed(true)
, анимация поп больше не будет воспроизводиться. Результаты на самом деле похожи на результат ответа @Geoff.
Ответ 12
Ответ на комментарий Джеффа и 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);
}
Извините за мой английский