Фрагменты: удалить все фрагменты в виде

Сценарий, с которым я столкнулся, находится в моем приложении. У меня есть одноуровневая и двухслойная макет. Вместо того, чтобы индивидуально обрабатывать каждую возможную операцию навигации между экранами, для каждого другого стиля макета, я использую функцию, которая правильно настраивает макет при заданном экране.

Это в основном оператор 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();

Как вы можете видеть, с тем, с чем я борюсь, является , как сделать второй шаг. Как удалить все Fragment из заданного View, не зная точно, какие из них вы хотите удалить? Самое близкое, что я нашел, это FragmentTransaction.replace(), который успешно делает это для каждого случая , но, когда оказывается, что вы заменяете Fragment на тот же фрагмент. В этом случае он не удаляет все, а затем добавляет (как предлагает документация), он просто удаляется. Это проблема с использованием библиотек совместимости или это не способ использования FragmentTransaction.replace()?

В любом случае, как я должен это делать? Должен ли я кодировать функцию removeAllFragments(), чтобы проходить через каждый фрагмент и отсоединять его, или есть способ сделать первую половину того, что заявляет функция "два в одном" FragmentTransaction.replace()?

Ответ 1

Типичным механизмом является использование FragmentManager.findFragmentByTag(). Вы используете это и добавляете теги к своим фрагментам (или альтернативу для идентификаторов). Таким образом, вы можете определить, какие фрагменты в настоящее время управляются. Затем, когда у вас есть дескриптор существующего фрагмента (findFragmentByTag возвращает ненулевое значение), вы можете использовать FragmentManager.beginTransaction() чтобы запустить FragmentTransaction и удалить/добавить необходимые фрагменты. Работая таким образом, вы избежите процесса "повторного добавления" фрагмента, который хотите сохранить.

То, что я, вероятно, сделал бы, это иметь код, подобный так:

Fragment pane1 = FragmentManager.findFragmentByTag("myFragmentPane1");
Fragment pane2 = FragmentManager.findFragmentByTag("myFragmentPane2");

setupScreen(pane1, pane2);

Вы должны также рассмотреть подклассы своего класса вместо того, чтобы иметь "все в одном классе". У вас есть довольно очевидный случай, когда Мартин Фаулер заменил условное на подкласс. В противном случае, боюсь, что это будет невероятно сложно для менеджера, когда вы добавите еще один экран.

Ответ 2

Ни один из других ответов не работал у меня. Вот что я сделал:

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

Кроме того, если вы удаляете все фрагменты из представления несколько раз, вы можете проверить, является ли текущий фрагмент равным null или isDetached() или isRemoving(), или вы можете получить NullPointerExceptions.

Обновить. Документация для getSupportFragmentManger().getFragments() теперь, по-видимому, скрыта, но по-прежнему отлично работает в моем код. Вот скриншот документации:

enter image description here

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

Обновление 8-4-15. Если вы не используете библиотеку поддержки для фрагментов, к сожалению, нет getFragments(), но есть еще пара, более неудобные варианты.

  • Дайте каждому fragment a tag или id при создании и проведите через них, чтобы обработать каждый fragment по желанию.
  • Создайте прослушиватель с помощью onAttachListener, поэтому каждый раз, когда к activity присоединяется новый fragment, вы можете сохранить это fragment, а затем перебирать эту структуру данных для обработки каждого fragment по желанию.

Если вы не используете getSupportFragmentManager(), для обработки транзакции вам нужно будет использовать getFragmentManager().

Ответ 3

Если вы используете android.support.v4.app.Fragment, вы можете сделать это:

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

Ответ 4

Выключается FragmentTransaction.replace() - это правильная операция и должна работать правильно. Он работает только при использовании ActionBarSherlock и SherlockFragmentActivity, поэтому я могу только предположить, что это ошибка в этой библиотеке совместимости.

Я подтвердил это с помощью приведенного ниже кода, чтобы реализовать желаемое поведение через Android на API11 +, библиотеку совместимости Android и ActionBarSherlock. Он только разбивается в последнем случае.

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

}

Ответ 5

Это более или менее, как я справился с этим. Попросите все ваши фрагменты реализовать интерфейс примерно так:

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

Вам придется немного изменить ситуацию, чтобы соответствовать вашим данным.