Как реализовать backstack при использовании Framework KitKat Transitions

Я использую новый API Kitaat Переходы на Android. Я создал два объекта Scene, используя два макета. Я живу от Scene 1 до Scene 2 внутри a Fragment. Я хочу автоматически вернуться к предыдущему Scene, когда пользователь нажимает кнопку "Назад".

Есть ли какой-то встроенный механизм Backstack при использовании Transitions, или мне нужно катить его?

Достаточно просто вызвать TransitionManager.go(scene1), но я действительно не хочу реализовывать прослушиватель onBackPressed() во всех моих фрагментах, у которых есть анимации Scene.

Ответ 1

Я закончил свое собственное решение.

Попросите Activity выполнить этот

public interface SceneBackstackHandler {

    public void addBackstackListener(BackstackListener listener);

    public void removeBackstackListener(BackstackListener listener);

    public void removeAllBackstackListeners();

    public interface BackstackListener {
        public boolean onBackPressed();
    }
}

активность

private final Object mBackstackListenerLock = new Object();
private List<BackstackListener> mBackstackListeners = new ArrayList<>();

@Override
public void onBackPressed() {
    synchronized (mBackstackListenerLock) {
        for (BackstackListener mBackstackListener : mBackstackListeners) {
            if (mBackstackListener.onBackPressed()) {
                // handled by fragment
                return;
            }
        }
        super.onBackPressed();
    }
}

@Override
protected void onPause() {
    super.onPause();
    removeAllBackstackListeners();
}

@Override
public void addBackstackListener(BackstackListener listener) {
    synchronized (mBackstackListenerLock) {
        mBackstackListeners.add(listener);
    }
}

@Override
public void removeBackstackListener(BackstackListener listener) {
    synchronized (mBackstackListenerLock) {
        mBackstackListeners.remove(listener);
    }
}

@Override
public void removeAllBackstackListeners() {
    synchronized (mBackstackListenerLock) {
        mBackstackListeners.clear();
    }
}

Детский фрагмент:

public class MySceneFragment extends Fragment
        implements SceneBackstackHandler.BackstackListener {

    private Scene mCurrentScene;

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        mBackstackHandler = (SceneBackstackHandler) activity;
        mBackstackHandler.addBackstackListener(this);
    }

    @Override
    public void onDetach() {
        super.onDetach();
        mBackstackHandler.removeBackstackListener(this);
    }

    @Override
    public boolean onBackPressed() {
        if (mCurrentScene != null && mCurrentScene.equals(mMyScene)) {
            removeMyScene();
            return true;
        }
        return false;
    }

    private void changeScene(Scene scene) {
        TransitionManager.go(scene);
        mCurrentScene = scene;
    }
}

Ответ 2

Я использую шину событий Otto для связи между моими Activity и Fragment s. Управляющий Activity поддерживает свой собственный Stack пользовательских событий возврата, каждый из которых содержит обратное действие Runnable, то есть какое действие следует предпринять при нажатии кнопки "Назад".

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

Вот пример кода, который даст вам представление о том, как это сделать.

Фрагмент (ы)

Фрагмент сигнализирует о своем намерении завладеть следующим обратным нажатием размещения a BackStackRequestEvent на шину событий Otto и поставкой Runnable действие, которое должно выполняться, когда событие выгружается из Activity настраиваемого стека. Когда фрагмент отсоединен, он отправляет ClearBackStackEvent на шину, чтобы удалить любые из Fragment обратных действий из пользовательского стека действий.

public class MyFragment extends Fragment {

    private final String BACK_STACK_ID = "MY_FRAGMENT";

    ...

    public class BackStackRequestEvent {
        private Runnable action;
        private String id;

        public BackStackRequestEvent(Runnable action, String id) {
            this.action = action;
            this.id = id;
        }

        public void goBack() {
            action.run();
        }

        public String getId() {
            return id;
        }
    }

    public class ClearBackStackEvent {
        private String id;

        public ClearBackStackEvent(String id) {
            this.id = id;
        }

        public String getId() {
            return id;
        }
    }

    ...

    @Override
    public void onDetach() {
        super.onDetach();
        // Get your Otto singleton and notify Activity that this
        // Fragment back actions are no longer needed
        // The Fragment lifecycle stage in which you do this might vary
        // based on your needs
        EventBus.getInstance().post(new ClearBackStackEvent(BACK_STACK_ID));
    }

    ...

    public void someChangeInFragment() {
        // Notify the Activity that we want to intercept the next onBackPressed
        EventBus.getInstance().post(new BackStackRequestEvent(new Runnable()
        {
            @Override
            public void run() {
                // Reverse what we did
                doBackAction();
            }
        }, BACK_STACK_ID)); // constant used later to remove items from Stack
    }
}

Деятельность

Активность регистрирует/отменит свою заинтересованность в мы определили выше в onStart() и onStop(). Когда он получает новый BackStackRequestEvent, он добавляет его в свой собственный задний стек. Когда вызывается onBackPressed(), он выталкивает задний стек и вызывает обратное действие с помощью BackStackRequestEvent.goBack(), который, в свою очередь, запускает фрагмент Runnable. Если в стеке нет ничего, следует соблюдать нормальное обратное поведение.

Когда фрагмент отсоединен, Activity получает ClearBackStackEvent и удаляет все элементы из поставляемого id из Stack.

public class MyActivity extends Activity {

    private Stack<MyFragment.BackStackRequestEvent> customBackStack = new Stack<>();

    ...

    @Override
    protected void onStart() {
        super.onStart();
        EventBus.getInstance().register(this);
    }

    @Override
    protected void onStop() {
        super.onStop();
        EventBus.getInstance().unregister(this);
    }

    @Subscribe // Annotation indicating that we want to intercept this Otto event
    public void backStackRequested(MyFragment.BackStackRequestEvent request) {
        customBackStack.push(request);
    }

    @Override
    public void onBackPressed() {
        if (customBackStack.empty()) {
            // No custom actions so default behaviour followed
            super.onBackPressed();
        }
        else {
            // Pop the custom action and call its goBack() action
            MyFragment.BackStackRequestEvent back = customBackStack.pop();
            back.goBack();
        }
    }

    @Subscribe
    public void clearBackStackRequested(MyFragment.ClearBackStackEvent request) {
        String id = request.getId();
        for (MyFragment.BackStackRequestEvent backItem : customBackStack) {
            if (backItem.getId().contentEquals(id)) {
                customBackStack.remove(backItem);
            }
        }
    }
}