Фрагменты: удалить все фрагменты в виде
Сценарий, с которым я столкнулся, находится в моем приложении. У меня есть одноуровневая и двухслойная макет. Вместо того, чтобы индивидуально обрабатывать каждую возможную операцию навигации между экранами, для каждого другого стиля макета, я использую функцию, которая правильно настраивает макет при заданном экране.
Это в основном оператор 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);
}
Вам придется немного изменить ситуацию, чтобы соответствовать вашим данным.