react-native-navigation的迁移库

StackController.java 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424
  1. package com.reactnativenavigation.viewcontrollers.stack;
  2. import android.app.Activity;
  3. import android.view.View;
  4. import android.view.ViewGroup;
  5. import com.reactnativenavigation.anim.NavigationAnimator;
  6. import com.reactnativenavigation.parse.NestedAnimationsOptions;
  7. import com.reactnativenavigation.parse.Options;
  8. import com.reactnativenavigation.presentation.Presenter;
  9. import com.reactnativenavigation.presentation.StackPresenter;
  10. import com.reactnativenavigation.react.Constants;
  11. import com.reactnativenavigation.react.events.EventEmitter;
  12. import com.reactnativenavigation.utils.CommandListener;
  13. import com.reactnativenavigation.utils.CommandListenerAdapter;
  14. import com.reactnativenavigation.utils.CompatUtils;
  15. import com.reactnativenavigation.viewcontrollers.ChildControllersRegistry;
  16. import com.reactnativenavigation.viewcontrollers.IdStack;
  17. import com.reactnativenavigation.viewcontrollers.ParentController;
  18. import com.reactnativenavigation.viewcontrollers.ViewController;
  19. import com.reactnativenavigation.viewcontrollers.topbar.TopBarController;
  20. import com.reactnativenavigation.views.Component;
  21. import com.reactnativenavigation.views.Fab;
  22. import com.reactnativenavigation.views.FabMenu;
  23. import com.reactnativenavigation.views.StackLayout;
  24. import com.reactnativenavigation.views.stack.StackBehaviour;
  25. import com.reactnativenavigation.views.topbar.TopBar;
  26. import java.util.Collection;
  27. import java.util.Iterator;
  28. import java.util.List;
  29. import androidx.annotation.NonNull;
  30. import androidx.annotation.RestrictTo;
  31. import androidx.annotation.VisibleForTesting;
  32. import androidx.coordinatorlayout.widget.CoordinatorLayout;
  33. import androidx.viewpager.widget.ViewPager;
  34. import static com.reactnativenavigation.utils.CollectionUtils.*;
  35. import static com.reactnativenavigation.utils.CoordinatorLayoutUtils.matchParentWithBehaviour;
  36. import static com.reactnativenavigation.utils.CoordinatorLayoutUtils.updateBottomMargin;
  37. import static com.reactnativenavigation.utils.ObjectUtils.perform;
  38. public class StackController extends ParentController<StackLayout> {
  39. private IdStack<ViewController> stack = new IdStack<>();
  40. private final NavigationAnimator animator;
  41. private final EventEmitter eventEmitter;
  42. private TopBarController topBarController;
  43. private BackButtonHelper backButtonHelper;
  44. private final StackPresenter presenter;
  45. public StackController(Activity activity, List<ViewController> children, ChildControllersRegistry childRegistry, EventEmitter eventEmitter, TopBarController topBarController, NavigationAnimator animator, String id, Options initialOptions, BackButtonHelper backButtonHelper, StackPresenter stackPresenter, Presenter presenter) {
  46. super(activity, childRegistry, id, presenter, initialOptions);
  47. this.eventEmitter = eventEmitter;
  48. this.topBarController = topBarController;
  49. this.animator = animator;
  50. this.backButtonHelper = backButtonHelper;
  51. this.presenter = stackPresenter;
  52. stackPresenter.setButtonOnClickListener(this::onNavigationButtonPressed);
  53. for (ViewController child : children) {
  54. child.setParentController(this);
  55. stack.push(child.getId(), child);
  56. if (size() > 1) backButtonHelper.addToPushedChild(child);
  57. }
  58. }
  59. @Override
  60. public boolean isRendered() {
  61. if (isEmpty()) return false;
  62. if (getCurrentChild().isDestroyed()) return false;
  63. ViewGroup currentChild = getCurrentChild().getView();
  64. if (currentChild instanceof Component) {
  65. return super.isRendered() && presenter.isRendered(currentChild);
  66. }
  67. return super.isRendered();
  68. }
  69. @Override
  70. public void setDefaultOptions(Options defaultOptions) {
  71. super.setDefaultOptions(defaultOptions);
  72. presenter.setDefaultOptions(defaultOptions);
  73. }
  74. @Override
  75. protected ViewController getCurrentChild() {
  76. return stack.peek();
  77. }
  78. @Override
  79. public void onAttachToParent() {
  80. if (!isEmpty() && !getCurrentChild().isDestroyed() && !isViewShown()) {
  81. presenter.applyChildOptions(resolveCurrentOptions(), this, getCurrentChild());
  82. }
  83. }
  84. @Override
  85. public void mergeOptions(Options options) {
  86. presenter.mergeOptions(options, this, getCurrentChild());
  87. super.mergeOptions(options);
  88. }
  89. @Override
  90. public void applyChildOptions(Options options, ViewController child) {
  91. super.applyChildOptions(options, child);
  92. presenter.applyChildOptions(resolveCurrentOptions(), this, child);
  93. fabOptionsPresenter.applyOptions(this.options.fabOptions, child, getView());
  94. performOnParentController(parent ->
  95. parent.applyChildOptions(
  96. this.options.copy()
  97. .clearTopBarOptions()
  98. .clearAnimationOptions()
  99. .clearFabOptions()
  100. .clearTopTabOptions()
  101. .clearTopTabsOptions(),
  102. child
  103. )
  104. );
  105. }
  106. @Override
  107. public void mergeChildOptions(Options options, ViewController child) {
  108. super.mergeChildOptions(options, child);
  109. if (child.isViewShown() && peek() == child) {
  110. presenter.mergeChildOptions(options, resolveCurrentOptions(), this, child);
  111. if (options.fabOptions.hasValue()) {
  112. fabOptionsPresenter.mergeOptions(options.fabOptions, child, getView());
  113. }
  114. }
  115. performOnParentController(parent ->
  116. parent.mergeChildOptions(
  117. options.copy()
  118. .clearTopBarOptions()
  119. .clearAnimationOptions()
  120. .clearFabOptions()
  121. .clearTopTabOptions()
  122. .clearTopTabsOptions(),
  123. child
  124. )
  125. );
  126. }
  127. @Override
  128. public void onChildDestroyed(ViewController child) {
  129. super.onChildDestroyed(child);
  130. presenter.onChildDestroyed(child);
  131. }
  132. public void push(ViewController child, CommandListener listener) {
  133. if (findController(child.getId()) != null) {
  134. listener.onError("A stack can't contain two children with the same id");
  135. return;
  136. }
  137. final ViewController toRemove = stack.peek();
  138. if (size() > 0) backButtonHelper.addToPushedChild(child);
  139. child.setParentController(this);
  140. stack.push(child.getId(), child);
  141. Options resolvedOptions = resolveCurrentOptions(presenter.getDefaultOptions());
  142. addChildToStack(child, resolvedOptions);
  143. if (toRemove != null) {
  144. NestedAnimationsOptions animation = resolvedOptions.animations.push;
  145. if (animation.enabled.isTrueOrUndefined()) {
  146. if (animation.waitForRender.isTrue() || resolvedOptions.animations.push.sharedElements.hasValue()) {
  147. animator.push(child, toRemove, resolvedOptions, () -> onPushAnimationComplete(child, toRemove, listener));
  148. } else {
  149. animator.push(child, toRemove, resolvedOptions, () -> onPushAnimationComplete(child, toRemove, listener));
  150. }
  151. } else {
  152. getView().removeView(toRemove.getView());
  153. listener.onSuccess(child.getId());
  154. }
  155. } else {
  156. listener.onSuccess(child.getId());
  157. }
  158. }
  159. private void onPushAnimationComplete(ViewController toAdd, ViewController toRemove, CommandListener listener) {
  160. if (!peek().equals(toRemove)) getView().removeView(toRemove.getView());
  161. listener.onSuccess(toAdd.getId());
  162. }
  163. private void addChildToStack(ViewController child, Options resolvedOptions) {
  164. child.setWaitForRender(resolvedOptions.animations.push.waitForRender);
  165. if (size() == 1) presenter.applyInitialChildLayoutOptions(resolvedOptions);
  166. getView().addView(child.getView(), getView().getChildCount() - 1, matchParentWithBehaviour(new StackBehaviour(this)));
  167. }
  168. public void setRoot(List<ViewController> children, CommandListener listener) {
  169. animator.cancelPushAnimations();
  170. final ViewController toRemove = stack.peek();
  171. IdStack stackToDestroy = stack;
  172. stack = new IdStack<>();
  173. ViewController child = last(children);
  174. if (children.size() == 1) {
  175. backButtonHelper.clear(child);
  176. } else {
  177. backButtonHelper.addToPushedChild(child);
  178. }
  179. child.setParentController(this);
  180. stack.push(child.getId(), child);
  181. Options resolvedOptions = resolveCurrentOptions(presenter.getDefaultOptions());
  182. addChildToStack(child, resolvedOptions);
  183. CommandListener listenerAdapter = new CommandListenerAdapter() {
  184. @Override
  185. public void onSuccess(String childId) {
  186. destroyStack(stackToDestroy);
  187. if (children.size() > 1) {
  188. for (int i = 0; i < children.size() - 1; i++) {
  189. stack.set(children.get(i).getId(), children.get(i), i);
  190. children.get(i).setParentController(StackController.this);
  191. if (i == 0) {
  192. backButtonHelper.clear(children.get(i));
  193. } else {
  194. backButtonHelper.addToPushedChild(children.get(i));
  195. }
  196. }
  197. }
  198. listener.onSuccess(childId);
  199. }
  200. };
  201. if (toRemove != null && resolvedOptions.animations.setStackRoot.enabled.isTrueOrUndefined()) {
  202. if (resolvedOptions.animations.setStackRoot.waitForRender.isTrue()) {
  203. child.getView().setAlpha(0);
  204. child.addOnAppearedListener(() -> animator.push(
  205. child,
  206. toRemove,
  207. resolvedOptions,
  208. () -> listenerAdapter.onSuccess(child.getId())
  209. )
  210. );
  211. } else {
  212. animator.push(child, toRemove, resolvedOptions, () -> listenerAdapter.onSuccess(child.getId()));
  213. }
  214. } else {
  215. listenerAdapter.onSuccess(child.getId());
  216. }
  217. }
  218. private void destroyStack(IdStack stack) {
  219. for (String s : (Iterable<String>) stack) {
  220. ((ViewController) stack.get(s)).destroy();
  221. }
  222. }
  223. public void pop(Options mergeOptions, CommandListener listener) {
  224. if (!canPop()) {
  225. listener.onError("Nothing to pop");
  226. return;
  227. }
  228. peek().mergeOptions(mergeOptions);
  229. Options disappearingOptions = resolveCurrentOptions(presenter.getDefaultOptions());
  230. final ViewController disappearing = stack.pop();
  231. final ViewController appearing = stack.peek();
  232. disappearing.onViewWillDisappear();
  233. appearing.onViewWillAppear();
  234. ViewGroup appearingView = appearing.getView();
  235. if (appearingView.getLayoutParams() == null) {
  236. appearingView.setLayoutParams(matchParentWithBehaviour(new StackBehaviour(this)));
  237. }
  238. if (appearingView.getParent() == null) {
  239. getView().addView(appearingView, 0);
  240. }
  241. presenter.onChildWillAppear(this, appearing, disappearing);
  242. if (disappearingOptions.animations.pop.enabled.isTrueOrUndefined()) {
  243. animator.pop(disappearing.getView(), disappearingOptions.animations.pop, () -> finishPopping(disappearing, listener));
  244. } else {
  245. finishPopping(disappearing, listener);
  246. }
  247. }
  248. private void finishPopping(ViewController disappearing, CommandListener listener) {
  249. disappearing.destroy();
  250. listener.onSuccess(disappearing.getId());
  251. eventEmitter.emitScreenPoppedEvent(disappearing.getId());
  252. }
  253. public void popTo(ViewController viewController, Options mergeOptions, CommandListener listener) {
  254. if (!stack.containsId(viewController.getId()) || peek().equals(viewController)) {
  255. listener.onError("Nothing to pop");
  256. return;
  257. }
  258. animator.cancelPushAnimations();
  259. String currentControlId;
  260. for (int i = stack.size() - 2; i >= 0; i--) {
  261. currentControlId = stack.get(i).getId();
  262. if (currentControlId.equals(viewController.getId())) {
  263. break;
  264. }
  265. ViewController controller = stack.get(currentControlId);
  266. stack.remove(controller.getId());
  267. controller.destroy();
  268. }
  269. pop(mergeOptions, listener);
  270. }
  271. public void popToRoot(Options mergeOptions, CommandListener listener) {
  272. if (!canPop()) {
  273. listener.onError("Nothing to pop");
  274. return;
  275. }
  276. animator.cancelPushAnimations();
  277. Iterator<String> iterator = stack.iterator();
  278. iterator.next();
  279. while (stack.size() > 2) {
  280. ViewController controller = stack.get(iterator.next());
  281. if (!stack.isTop(controller.getId())) {
  282. stack.remove(iterator, controller.getId());
  283. controller.destroy();
  284. }
  285. }
  286. pop(mergeOptions, listener);
  287. }
  288. ViewController peek() {
  289. return stack.peek();
  290. }
  291. public int size() {
  292. return stack.size();
  293. }
  294. public boolean isEmpty() {
  295. return stack.isEmpty();
  296. }
  297. @Override
  298. public boolean handleBack(CommandListener listener) {
  299. if (canPop()) {
  300. pop(Options.EMPTY, listener);
  301. return true;
  302. }
  303. return false;
  304. }
  305. @VisibleForTesting()
  306. boolean canPop() {
  307. return stack.size() > 1;
  308. }
  309. @NonNull
  310. @Override
  311. protected StackLayout createView() {
  312. StackLayout stackLayout = new StackLayout(getActivity(), topBarController, getId());
  313. presenter.bindView(topBarController);
  314. addInitialChild(stackLayout);
  315. return stackLayout;
  316. }
  317. private void addInitialChild(StackLayout stackLayout) {
  318. if (isEmpty()) return;
  319. ViewGroup child = peek().getView();
  320. child.setId(CompatUtils.generateViewId());
  321. presenter.applyInitialChildLayoutOptions(resolveCurrentOptions());
  322. stackLayout.addView(child, 0, matchParentWithBehaviour(new StackBehaviour(this)));
  323. }
  324. private void onNavigationButtonPressed(String buttonId) {
  325. if (Constants.BACK_BUTTON_ID.equals(buttonId)) {
  326. pop(Options.EMPTY, new CommandListenerAdapter());
  327. } else {
  328. sendOnNavigationButtonPressed(buttonId);
  329. }
  330. }
  331. @Override
  332. public void sendOnNavigationButtonPressed(String buttonId) {
  333. peek().sendOnNavigationButtonPressed(buttonId);
  334. }
  335. @NonNull
  336. @Override
  337. public Collection<ViewController> getChildControllers() {
  338. return stack.values();
  339. }
  340. @Override
  341. public void setupTopTabsWithViewPager(ViewPager viewPager) {
  342. topBarController.initTopTabs(viewPager);
  343. }
  344. @Override
  345. public void clearTopTabs() {
  346. topBarController.clearTopTabs();
  347. }
  348. @Override
  349. public boolean onDependentViewChanged(CoordinatorLayout parent, ViewGroup child, View dependency) {
  350. perform(findController(child), controller -> {
  351. if (dependency instanceof TopBar) presenter.applyTopInsets(this, controller);
  352. if (dependency instanceof Fab || dependency instanceof FabMenu) updateBottomMargin(dependency, getBottomInset());
  353. });
  354. return false;
  355. }
  356. @Override
  357. public int getTopInset(ViewController child) {
  358. return presenter.getTopInset(resolveChildOptions(child));
  359. }
  360. @RestrictTo(RestrictTo.Scope.TESTS)
  361. public TopBar getTopBar() {
  362. return topBarController.getView();
  363. }
  364. @RestrictTo(RestrictTo.Scope.TESTS)
  365. public StackLayout getStackLayout() {
  366. return getView();
  367. }
  368. }