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


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

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

12 88

12 ответов:

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

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

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

в следующем разделе у разработчиков Android описывается механизм связи создание обратных вызовов событий для действия. Процитируем строчку из него:

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

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

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

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

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

вещи, чтобы иметь в виду. 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'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);
}

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

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

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

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

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

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

я использовал перечисление 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;
}