it-swarm.com.ru

Фрагменты на выходе из заднего стека

Я использую пакет совместимости для использования фрагментов с Android 2.2. При использовании фрагментов и добавлении переходов между ними в backstack я хотел бы добиться того же поведения onResume действия, т. Е. Всякий раз, когда фрагмент выводится на «передний план» (видимый для пользователя) после всплывающего окна. Вне backstack я бы хотел, чтобы внутри фрагмента была активирована какая-то функция обратного вызова (например, для выполнения определенных изменений в общем ресурсе пользовательского интерфейса).

Я видел, что в рамках фрагмента нет встроенного обратного вызова. Есть ли хорошая практика для достижения этой цели?

91
oriharel

Из-за отсутствия лучшего решения у меня получилось так: Предположим, у меня есть 1 действие (MyActivity) и несколько фрагментов, которые заменяют друг друга (только один виден одновременно).

В MyActivity добавьте этот слушатель:

getSupportFragmentManager().addOnBackStackChangedListener(getListener());

(Как вы можете видеть, я использую пакет совместимости).

реализация getListener:

private OnBackStackChangedListener getListener()
    {
        OnBackStackChangedListener result = new OnBackStackChangedListener()
        {
            public void onBackStackChanged() 
            {                   
                FragmentManager manager = getSupportFragmentManager();

                if (manager != null)
                {
                    MyFragment currFrag = (MyFragment) manager.findFragmentById(R.id.fragmentItem);

                    currFrag.onFragmentResume();
                }                   
            }
        };

        return result;
    }

MyFragment.onFragmentResume() будет вызываться после нажатия кнопки «Назад». несколько предостережений, хотя:

  1. Предполагается, что вы добавили все транзакции В backstack (используя FragmentTransaction.addToBackStack())
  2. Он будет активирован при каждом изменении стека .__ (вы можете хранить другие вещи в Заднем стеке, например анимации), так что вы можете получить несколько вызовов для одного и того же экземпляра фрагмента
108
oriharel

Я немного изменил предложенное решение. У меня лучше работает вот так:

private OnBackStackChangedListener getListener() {
    OnBackStackChangedListener result = new OnBackStackChangedListener() {
        public void onBackStackChanged() {
            FragmentManager manager = getSupportFragmentManager();
            if (manager != null) {
                int backStackEntryCount = manager.getBackStackEntryCount();
                if (backStackEntryCount == 0) {
                    finish();
                }
                Fragment fragment = manager.getFragments()
                                           .get(backStackEntryCount - 1);
                fragment.onResume();
            }
        }
    };
    return result;
}
31
Brotoo25

После popStackBack() вы можете использовать следующий обратный вызов: onHiddenChanged(boolean hidden) внутри вашего фрагмента

5
user2230304

В следующем разделе в Android Developers описывается механизм связи Создание обратных вызовов событий для действия . Чтобы процитировать строку из этого:

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

Edit: Фрагмент имеет функцию onStart(...), которая вызывается, когда фрагмент виден пользователю. Аналогично функция onResume(...), когда она видна и активно работает. Они привязаны к своим коллегам по активности . Короче: используйте onResume()

3
PJL

Немного улучшен и обернут в решение менеджера.

Вещи, чтобы иметь в виду. FragmentManager не является синглтоном, он управляет только Фрагментами в Деятельности, поэтому в каждом действии он будет новым. Кроме того, это решение до сих пор не учитывает ViewPager, который вызывает метод setUserVisibleHint (), помогающий контролировать видимость фрагментов.

Не стесняйтесь использовать следующие классы при решении этой проблемы (использует инъекцию Dagger2). Позвоните в деятельности:

//inject FragmentBackstackStateManager instance to myFragmentBackstackStateManager
FragmentManager fragmentManager = getSupportFragmentManager(); 
myFragmentBackstackStateManager.apply(fragmentManager);

FragmentBackstackStateManager.Java:

@Singleton
public class FragmentBackstackStateManager {

    private FragmentManager fragmentManager;

    @Inject
    public FragmentBackstackStateManager() {
    }

    private BackstackCallback backstackCallbackImpl = new BackstackCallback() {
        @Override
        public void onFragmentPushed(Fragment parentFragment) {
            parentFragment.onPause();
        }

        @Override
        public void onFragmentPopped(Fragment parentFragment) {
            parentFragment.onResume();
        }
    };

    public FragmentBackstackChangeListenerImpl getListener() {
        return new FragmentBackstackChangeListenerImpl(fragmentManager, backstackCallbackImpl);
    }

    public void apply(FragmentManager fragmentManager) {
        this.fragmentManager = fragmentManager;
        fragmentManager.addOnBackStackChangedListener(getListener());
    }
}

FragmentBackstackChangeListenerImpl.Java:

public class FragmentBackstackChangeListenerImpl implements FragmentManager.OnBackStackChangedListener {

    private int lastBackStackEntryCount = 0;
    private final FragmentManager fragmentManager;
    private final BackstackCallback backstackChangeListener;

    public FragmentBackstackChangeListenerImpl(FragmentManager fragmentManager, BackstackCallback backstackChangeListener) {
        this.fragmentManager = fragmentManager;
        this.backstackChangeListener = backstackChangeListener;
        lastBackStackEntryCount = fragmentManager.getBackStackEntryCount();
    }

    private boolean wasPushed(int backStackEntryCount) {
        return lastBackStackEntryCount < backStackEntryCount;
    }

    private boolean wasPopped(int backStackEntryCount) {
        return lastBackStackEntryCount > backStackEntryCount;
    }

    private boolean haveFragments() {
        List<Fragment> fragmentList = fragmentManager.getFragments();
        return fragmentList != null && !fragmentList.isEmpty();
    }


    /**
     * If we Push a fragment to backstack then parent would be the one before => size - 2
     * If we pop a fragment from backstack logically it should be the last fragment in the list, but in Android popping a fragment just makes list entry null keeping list size intact, thus it's also size - 2
     *
     * @return fragment that is parent to the one that is pushed to or popped from back stack
     */
    private Fragment getParentFragment() {
        List<Fragment> fragmentList = fragmentManager.getFragments();
        return fragmentList.get(Math.max(0, fragmentList.size() - 2));
    }

    @Override
    public void onBackStackChanged() {
        int currentBackStackEntryCount = fragmentManager.getBackStackEntryCount();
        if (haveFragments()) {
            Fragment parentFragment = getParentFragment();

            //will be null if was just popped and was last in the stack
            if (parentFragment != null) {
                if (wasPushed(currentBackStackEntryCount)) {
                    backstackChangeListener.onFragmentPushed(parentFragment);
                } else if (wasPopped(currentBackStackEntryCount)) {
                    backstackChangeListener.onFragmentPopped(parentFragment);
                }
            }
        }

        lastBackStackEntryCount = currentBackStackEntryCount;
    }
}

BackstackCallback.Java:

public interface BackstackCallback {
    void onFragmentPushed(Fragment parentFragment);

    void onFragmentPopped(Fragment parentFragment);
}
2
AAverin

Если фрагмент помещается в backstack, Android просто разрушает его вид. Сам экземпляр фрагмента не уничтожен. Простой способ начать - прослушать событие onViewCreated, добавив туда логику onResume ().

boolean fragmentAlreadyLoaded = false;
    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.onViewCreated(view, savedInstanceState);

        if (savedInstanceState == null && !fragmentAlreadyLoaded) {
            fragmentAlreadyLoaded = true;

            // Code placed here will be executed once
        }

        //Code placed here will be executed even when the fragment comes from backstack
    }
1
Franjo

В моей деятельности onCreate ()

getSupportFragmentManager().addOnBackStackChangedListener(getListener());

Используйте этот метод для перехвата определенного фрагмента и вызова onResume ()

private FragmentManager.OnBackStackChangedListener getListener()
    {
        FragmentManager.OnBackStackChangedListener result = new FragmentManager.OnBackStackChangedListener()
        {
            public void onBackStackChanged()
            {
                Fragment currentFragment = getSupportFragmentManager().findFragmentById(R.id.fragment_container);
                if (currentFragment instanceof YOURFRAGMENT) {
                    currentFragment.onResume();
                }
            }
        };

        return result;
    }
1
Quan Nguyen

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

@Override
public void onResume() {
    super.onResume();
    // Get/Backup current title
    mTitle = ((ActionBarActivity) getActivity()).getSupportActionBar()
            .getTitle();
    // Set new title
    ((ActionBarActivity) getActivity()).getSupportActionBar()
        .setTitle(R.string.this_fragment_title);
}

@Override
public void onDestroy() {
    // Set title back
    ((ActionBarActivity) getActivity()).getSupportActionBar()
        .setTitle(mTitle);

    super.onDestroy();
}
0
Mahendran Candy

onResume () для фрагмента работает нормально ...

public class listBook extends Fragment {

    private String listbook_last_subtitle;
...

    @Override
       public void onCreate(Bundle savedInstanceState) {

        String thisFragSubtitle = (String) getActivity().getActionBar().getSubtitle();
        listbook_last_subtitle = thisFragSubtitle;
       }
...

    @Override
        public void onResume(){
            super.onResume();
            getActivity().getActionBar().setSubtitle(listbook_last_subtitle);
        }
...
0
Roshan Poudyal

Это правильный ответ, который вы можете вызвать onResume (), если фрагмент прикреплен к действию. В качестве альтернативы вы можете использовать onAttach и onDetach

0
iamlukeyb
public abstract class RootFragment extends Fragment implements OnBackPressListener {

 @Override
 public boolean onBackPressed() {
  return new BackPressImpl(this).onBackPressed();
 }

 public abstract void OnRefreshUI();

}


public class BackPressImpl implements OnBackPressListener {

 private Fragment parentFragment;

 public BackPressImpl(Fragment parentFragment) {
  this.parentFragment = parentFragment;
 }

 @Override
 public boolean onBackPressed() {
  ((RootFragment) parentFragment).OnRefreshUI();
 }
}

и, наконец, расширите свой фрагмент от корневого фрагмента, чтобы увидеть эффект

0
Luu Quoc Thang

Я использовал enum FragmentTags, чтобы определить все мои классы фрагментов.

TAG_FOR_FRAGMENT_A(A.class),
TAG_FOR_FRAGMENT_B(B.class),
TAG_FOR_FRAGMENT_C(C.class)

передать FragmentTags.TAG_FOR_FRAGMENT_A.name() в качестве тега фрагмента.

и теперь 

@Override
public void onBackPressed(){
   FragmentManager fragmentManager = getFragmentManager();
   Fragment current
   = fragmentManager.findFragmentById(R.id.fragment_container);
    FragmentTags fragmentTag = FragmentTags.valueOf(current.getTag());

  switch(fragmentTag){
    case TAG_FOR_FRAGMENT_A:
        finish();
        break;
   case TAG_FOR_FRAGMENT_B:
        fragmentManager.popBackStack();
        break;
   case default: 
   break;
}
0
Ali