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

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

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

Ответ 1

Из-за отсутствия лучшего решения я получил это для меня: Предположим, что у меня есть 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() будет вызываться после нажатия "Назад". несколько предостережений:

  • Предполагается, что вы добавили все транзакции в backscack (используя FragmentTransaction.addToBackStack())
  • Он будет активирован на каждом стеке изменить (вы можете хранить другие вещи в задний стек, такой как анимация), поэтому вы можете получить несколько вызовов для тот же экземпляр фрагмента.

Ответ 2

Я немного изменил предлагаемое решение. Для меня это работает лучше:

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

Ответ 3

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

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

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

Ответ 4

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

Ответ 5

Если фрагмент помещен в 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
    }

Ответ 6

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

Вещи, о которых нужно помнить. FragmentManager не является одиночным, он управляет только фрагментами внутри Activity, поэтому в каждом действии он будет новым. Кроме того, это решение до сих пор не учитывает 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 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);
}

Ответ 7

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

Ответ 8

В моей деятельности 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;
    }

Ответ 9

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

Ответ 10

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

и в конечном итоге ваше Frament из RootFragment, чтобы увидеть эффект

Ответ 11

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

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

Ответ 12

Я использовал 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;
}