Как получить фрагмент, чтобы удалить себя, то есть его эквивалент окончания ()?

230

Я конвертирую приложение для использования фрагментов, используя библиотеку совместимости. В настоящее время у меня есть ряд действий (ABCD), которые связаны друг с другом, D имеет кнопку «ОК», которая при нажатии завершает вызовы, которые затем всплывают, onActivityResult()чтобы дополнительно уничтожить C и B.

Для моей предварительной версии фрагмента Honycomb каждое действие фактически является оберткой для фрагментов Af Bf Cf Df. Все действия запускаются через startActivityForResult()и onActivityResult()внутри каждого фрагмента можно с удовольствием позвонитьgetActivity().finish()

Проблема, с которой я столкнулся, заключается в том, что в моей версии сота у меня есть только одно действие, A, и фрагменты Bf, Cf, Df загружаются с помощью FragmentManager.

Что я не понимаю, что делать в Df, когда нажимается «OK» для удаления фрагментов Df, Cf и Bf?

Я попытался получить фрагмент, выталкивающий себя из стека, но это привело к исключению. onActivityResult()бесполезно, потому что я не загрузил фрагмент, используя startActivityForResult().

Я думаю об этом совершенно неправильно? Должен ли я реализовать какой-то тип слушателя, который связывается с родительским фрагментом или действием, чтобы выполнить всплывающее окно с помощью менеджера транзакций?

PJL
источник
7
как насчет ((YourActivity) getActivity ()). onBackPressed ();
Вишванат Лекшманан,
@ViswanathLekshmanan Ваш комментарий ответ полезен для меня .. 1 Upvote от меня
Абхишек Сингх
@AbhishekSingh Рад слышать :)
Вишванат Лекшманан
@ViswanathLekshmanan :)
Абхишек Сингх

Ответы:

62

Что я не понимаю, что делать в Df, когда нажимается «OK» для удаления фрагментов Df, Cf и Bf?

Шаг № 1: Пусть Df скажет D: «Эй! Мы получили ОК!» посредством вызова метода, либо в самой активности, либо в экземпляре интерфейса, предоставленного действием.

Шаг № 2: Пусть D удалит фрагменты через FragmentManager.

Хостинговая активность (D) - это та, которая знает, какие другие фрагменты есть в активности (по сравнению с другими действиями). Следовательно, внутрифрагментные события, которые могут повлиять на фрагментную смесь, должны быть распространены на действие, которое сделает соответствующие движения оркестровки.

CommonsWare
источник
Но в моей версии сота нет D, это мое трудно. Существует просто активация A, которая загружает фрагмент Bf, который загружает Cf, который загружает Df, используя FragmentTransaction.
PJL
1
@PJL: Извините, я имел в виду A. Это одна из причин использовать интерфейс слушателя, поэтому все действия могут отреагировать на событие "мы получили ОК" от Df.
CommonsWare
1
Поскольку я в настоящее время портирую, я вызвал метод слушателя из метода onActivityResult фрагментов Df в действие, после чего я вызвал popBackStack в FragmentManager. Однако это приводит к исключению «IllegalStateException: не удается выполнить это действие после onSaveInstanceState». Есть идеи относительно того, как я могу преодолеть это?
PJL
1
@DiegoPalomar: finish()должно хватить.
CommonsWare
1
@ user3364963: Прошло много времени с тех пор, как я это исследовал, но IIRC уничтожается, когда вынимается из заднего стека. Добавьте onDestroy()метод к вашему фрагменту и посмотрите, будет ли он вызван.
CommonsWare
313

Хотя это может быть не самым лучшим подходом, самый близкий аналог, который я могу себе представить, это работает с библиотекой поддержки / совместимости.

getActivity().getSupportFragmentManager().beginTransaction().remove(this).commit();

или

getActivity().getFragmentManager().beginTransaction().remove(this).commit();

в противном случае.

Кроме того, вы можете использовать backstack и вытолкнуть его. Однако имейте в виду, что фрагмент может не находиться в обратном стеке (в зависимости от транзакции фрагмента, который его туда доставил ...) или он может оказаться не последним, попавшим в стек, поэтому извлечение стека может удалить неправильный ...

Манфред Мозер
источник
11
Хотя этот подход работает, если вы используете addToBackStack (null), он оставит обработчик кнопки назад +1. Так что вам придется нажать на нее дважды.
user123321
34
«вытолкнуть» фрагмент из FragmentManager.
user123321
2
Я пробовал описанную выше процедуру, но она выдает эту ошибку «java-lang-нелегалстей, исключение-не-выполнить-это-действие-после-на-месте». Так, где именно я должен удалить фрагмент
KK_07k11A0585
6
Этот ответ является плохой практикой и не должен набирать голоса. Фрагменты не предназначены для самосознания, как это. Это убивает повторное использование, которое является точкой фрагментов! Фрагмент должен сигнализировать об активности, чтобы удалить его с помощью любого количества средств. Метод интерфейса обратного вызова является популярным выбором. developer.android.com/training/basics/fragments/...
colintheshots
2
@ ManfredMoser Я не согласен. Это очень важный вопрос. У него есть целая последовательность фрагментов для удаления. Этот код не имеет нулевых проверок или проверок на то, прикреплено ли действие. Это сломается в производстве, потому что это зависит от слишком многих вещей, которые фрагмент не знает.
colintheshots
263

Вы можете использовать подход ниже, он отлично работает:

getActivity().getSupportFragmentManager().popBackStack();
Эрик Юань
источник
44
Этот ответ примерно в 10 раз лучше принятого - прямо в точку.
nikib3ro
50
Это также в 10 раз хуже в отношении дизайна, чем принятый. Предполагается, что фрагмент является маленьким «помощником» для действия и никогда не должен контролировать себя или другие фрагменты
Патрик
6
Решение не является правильным, как указал @avalancha. Посмотрите developer.android.com/guide/components/...
the_dark_destructor
2
Я использую этот метод onActivityResult и получаю ошибку «Невозможно выполнить это действие после onSaveInstanceState». Как я могу решить это?
Jayeshkumar Sojitra
1
popBackStack()это единственное решение, которое работает, когда вы хотите вернуть заголовок панели действий к предыдущему состоянию после удаления фрагмента. В противном случае мне не нужно было бы объединять оба решения с высокой оценкой от stackoverflow.com/questions/13472258/… и это решение здесь, чтобы всегда устанавливать правильный заголовок панели действий после различных вариантов использования. как нажатие назад, удаление, добавление замены и так далее.
Бевор
36

Вы должны позволить Activity заниматься добавлением и удалением фрагментов, как говорит CommonsWare, использовать слушателя. Вот пример:

public class MyActivity extends FragmentActivity implements SuicidalFragmentListener {

    // onCreate etc

    @Override
    public void onFragmentSuicide(String tag) {
        // Check tag if you do this with more than one fragmen, then:
        getSupportFragmentManager().popBackStack();
    }
}

public interface SuicidalFragmentListener {
    void onFragmentSuicide(String tag);
}

public class MyFragment extends Fragment {

    // onCreateView etc

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        try {
           suicideListener = (SuicidalFragmentListener) activity;
        } catch (ClassCastException e) {
           throw new RuntimeException(getActivity().getClass().getSimpleName() + " must implement the suicide listener to use this fragment", e);
        }
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        // Attach the close listener to whatever action on the fragment you want
        addSuicideTouchListener();
    }

    private void addSuicideTouchListener() {
        getView().setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
              suicideListener.onFragmentSuicide(getTag());
            }
        });
    }
}
Бландел
источник
5
Самоубийство много эмо стиль? Как насчет «SelfClosing» или «AutoClose» или «SmartClose» (r)
DritanX
21
он не закрывается, он умирает навсегда ;-(
Бланделл
3
Это гораздо более чистый подход, чем другие ответы. Действие создает и представляет фрагмент и должно контролировать его жизненный цикл. Когда происходит что-то, что указывает на то, что фрагмент больше не должен быть виден, он должен сообщить об этом Activity и позволить активности удалить его.
Кристофер Пикслей
7
Технически, если мы хотим, чтобы действие убило фрагмент, то фрагмент не является самоубийственным. Активность убийственная.
Кейси Мюррей
17

В Activity / AppCompatActivity:

@Override
public void onBackPressed() {
    if (mDrawerLayout.isDrawerOpen(GravityCompat.START)) {
        // if you want to handle DrawerLayout
        mDrawerLayout.closeDrawer(GravityCompat.START);
    } else {
        if (getFragmentManager().getBackStackEntryCount() == 0) {
            super.onBackPressed();
        } else {
            getFragmentManager().popBackStack();
        }
    }
}

а затем позвоните во фрагмент:

getActivity().onBackPressed();

или как указано в других ответах, назовите это во фрагменте:

getActivity().getSupportFragmentManager().beginTransaction().remove(this).commit();
Codeversed
источник
5

Если вы используете новый компонент навигации, просто

findNavController().popBackStack()

Он выполнит всю транзакцию FragmentTransaction за вами.

Гастон Сайлен
источник
4

Посмотрите, удовлетворяет ли ваш запрос DialogFragment. DialogFragment имеет метод dismiss (). Намного чище на мой взгляд.

Vasudev
источник
3

Я создаю простой метод для этого

popBackStack(getSupportFragmentManager());

Чем поместить его в мой класс ActivityUtils

public static void popBackStack(FragmentManager manager){
        FragmentManager.BackStackEntry first = manager.getBackStackEntryAt(0);
        manager.popBackStack(first.getId(), FragmentManager.POP_BACK_STACK_INCLUSIVE);
    }

Это отлично работает, получайте удовольствие!

Элиас Кубала
источник
2
Я не понимаю цель вашего метода. Оригинальный popBackStack кажется вполне адекватным.
Невероятный
1

OnCreate:

//Add comment fragment
            container = FindViewById<FrameLayout>(Resource.Id.frmAttachPicture);
            mPictureFragment = new fmtAttachPicture();

            var trans = SupportFragmentManager.BeginTransaction();
            trans.Add(container.Id, mPictureFragment, "fmtPicture");
            trans.Show(mPictureFragment); trans.Commit();

Вот как я скрываю фрагмент в событии клика 1

//Close fragment
    var trans = SupportFragmentManager.BeginTransaction();
    trans.Hide(mPictureFragment);
    trans.AddToBackStack(null);
    trans.Commit();

Затем показывает его обратно в событии 2

var trans = SupportFragmentManager.BeginTransaction();
            trans.Show(mPictureFragment); trans.Commit();
CODESI
источник
1

Если вам нужно вернуться с четвертого фрагмента в истории backstack к первому, используйте теги !!!

Когда вы добавляете первый фрагмент, вы должны использовать что-то вроде этого:

getFragmentManager.beginTransaction.addToBackStack("A").add(R.id.container, FragmentA).commit() 

или

getFragmentManager.beginTransaction.addToBackStack("A").replace(R.id.container, FragmentA).commit()

И когда вы хотите показать фрагменты B, C и D, вы используете это:

getFragmentManager.beginTransaction.addToBackStack("B").replace(R.id.container, FragmentB, "B").commit()

и другие письма ....

Чтобы вернуться к FragmentA, просто вызовите popBackStack(0, "A")yes, используйте флаг, который вы указали при добавлении, и обратите внимание, что в команде должен быть тот же флаг addToBackStack(), а не тот, который используется в команде replace или add.

Пожалуйста ;)

Диего Рамирес Васкес
источник
Я протестировал «popBackStack (0,« A »)», и мое приложение возвращается к фрагменту A, но я хочу, чтобы только один фрагмент был удален из Back Stack ... Как я могу удалить фрагмент из Stack, не показывая на экране ??
KryNaC
1

Закрыть фрагмент внутри того же фрагмента

getActivity().onBackPressed();
Дино Санни
источник
-9

Почему бы просто:

getActivity () отделка ().

Будет
источник
4
Это завершит всю деятельность, а не только фрагмент.
Муртадха С.