Как реализовать 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);
}
}
}
}