react-native-navigation的迁移库

ScreenStack.java 18KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466
  1. package com.reactnativenavigation.screens;
  2. import android.os.Bundle;
  3. import android.support.annotation.Nullable;
  4. import android.support.v7.app.AppCompatActivity;
  5. import android.util.Log;
  6. import android.view.View;
  7. import android.widget.RelativeLayout;
  8. import android.widget.RelativeLayout.LayoutParams;
  9. import com.facebook.react.bridge.Callback;
  10. import com.facebook.react.bridge.Promise;
  11. import com.reactnativenavigation.NavigationApplication;
  12. import com.reactnativenavigation.params.ContextualMenuParams;
  13. import com.reactnativenavigation.params.FabParams;
  14. import com.reactnativenavigation.params.ScreenParams;
  15. import com.reactnativenavigation.params.StyleParams;
  16. import com.reactnativenavigation.params.TitleBarButtonParams;
  17. import com.reactnativenavigation.params.TitleBarLeftButtonParams;
  18. import com.reactnativenavigation.utils.KeyboardVisibilityDetector;
  19. import com.reactnativenavigation.utils.Task;
  20. import com.reactnativenavigation.views.LeftButtonOnClickListener;
  21. import java.util.List;
  22. import java.util.Stack;
  23. public class ScreenStack {
  24. private static final String TAG = "ScreenStack";
  25. public interface OnScreenPop {
  26. void onScreenPopAnimationEnd();
  27. }
  28. private final AppCompatActivity activity;
  29. private RelativeLayout parent;
  30. private LeftButtonOnClickListener leftButtonOnClickListener;
  31. private Stack<Screen> stack = new Stack<>();
  32. private final KeyboardVisibilityDetector keyboardVisibilityDetector;
  33. private boolean isStackVisible = false;
  34. private final String navigatorId;
  35. public String getNavigatorId() {
  36. return navigatorId;
  37. }
  38. public ScreenStack(AppCompatActivity activity,
  39. RelativeLayout parent,
  40. String navigatorId,
  41. LeftButtonOnClickListener leftButtonOnClickListener) {
  42. this.activity = activity;
  43. this.parent = parent;
  44. this.navigatorId = navigatorId;
  45. this.leftButtonOnClickListener = leftButtonOnClickListener;
  46. keyboardVisibilityDetector = new KeyboardVisibilityDetector(parent);
  47. }
  48. public void newStack(final ScreenParams params, LayoutParams layoutParams) {
  49. final Screen nextScreen = ScreenFactory.create(activity, params, leftButtonOnClickListener);
  50. final Screen previousScreen = stack.peek();
  51. if (isStackVisible) {
  52. pushScreenToVisibleStack(layoutParams, nextScreen, previousScreen, null, new Screen.OnDisplayListener() {
  53. @Override
  54. public void onDisplay() {
  55. removeElementsBelowTop();
  56. }
  57. });
  58. } else {
  59. pushScreenToInvisibleStack(layoutParams, nextScreen, previousScreen, null);
  60. removeElementsBelowTop();
  61. }
  62. }
  63. private void removeElementsBelowTop() {
  64. while (stack.size() > 1) {
  65. Screen screen = stack.get(0);
  66. parent.removeView(screen);
  67. screen.destroy();
  68. stack.remove(0);
  69. }
  70. }
  71. public void pushInitialModalScreenWithAnimation(final ScreenParams initialScreenParams, LayoutParams params) {
  72. isStackVisible = true;
  73. pushInitialScreen(initialScreenParams, params);
  74. final Screen screen = stack.peek();
  75. screen.setOnDisplayListener(new Screen.OnDisplayListener() {
  76. @Override
  77. public void onDisplay() {
  78. screen.show(initialScreenParams.animateScreenTransitions, NavigationType.ShowModal);
  79. screen.setStyle();
  80. }
  81. });
  82. }
  83. public void pushInitialScreen(ScreenParams initialScreenParams, LayoutParams params) {
  84. Screen initialScreen = ScreenFactory.create(activity, initialScreenParams, leftButtonOnClickListener);
  85. initialScreen.setVisibility(View.INVISIBLE);
  86. addScreen(initialScreen, params);
  87. }
  88. public void push(final ScreenParams params, LayoutParams layoutParams, Promise onPushComplete) {
  89. Screen nextScreen = ScreenFactory.create(activity, params, leftButtonOnClickListener);
  90. final Screen previousScreen = stack.peek();
  91. if (isStackVisible) {
  92. if (nextScreen.screenParams.sharedElementsTransitions.isEmpty()) {
  93. pushScreenToVisibleStack(layoutParams, nextScreen, previousScreen, onPushComplete);
  94. } else {
  95. pushScreenToVisibleStackWithSharedElementTransition(layoutParams, nextScreen, previousScreen, onPushComplete);
  96. }
  97. } else {
  98. pushScreenToInvisibleStack(layoutParams, nextScreen, previousScreen, onPushComplete);
  99. }
  100. }
  101. private void pushScreenToVisibleStack(LayoutParams layoutParams, final Screen nextScreen,
  102. final Screen previousScreen, Promise onPushComplete) {
  103. pushScreenToVisibleStack(layoutParams, nextScreen, previousScreen, onPushComplete, null);
  104. }
  105. private void pushScreenToVisibleStack(LayoutParams layoutParams,
  106. final Screen nextScreen,
  107. final Screen previousScreen,
  108. @Nullable final Promise onPushComplete,
  109. @Nullable final Screen.OnDisplayListener onDisplay) {
  110. nextScreen.setVisibility(View.INVISIBLE);
  111. addScreen(nextScreen, layoutParams);
  112. NavigationApplication.instance.getEventEmitter().sendWillDisappearEvent(previousScreen.getScreenParams(), NavigationType.Push);
  113. nextScreen.setOnDisplayListener(new Screen.OnDisplayListener() {
  114. @Override
  115. public void onDisplay() {
  116. nextScreen.show(nextScreen.screenParams.animateScreenTransitions, new Runnable() {
  117. @Override
  118. public void run() {
  119. if (onDisplay != null) onDisplay.onDisplay();
  120. if (onPushComplete != null) onPushComplete.resolve(null);
  121. NavigationApplication.instance.getEventEmitter().sendDidDisappearEvent(previousScreen.getScreenParams(), NavigationType.Push);
  122. parent.removeView(previousScreen);
  123. }
  124. }, NavigationType.Push);
  125. }
  126. });
  127. }
  128. private void pushScreenToVisibleStackWithSharedElementTransition(LayoutParams layoutParams, final Screen nextScreen,
  129. final Screen previousScreen, @Nullable final Promise onPushComplete) {
  130. nextScreen.setVisibility(View.INVISIBLE);
  131. nextScreen.setOnDisplayListener(new Screen.OnDisplayListener() {
  132. @Override
  133. public void onDisplay() {
  134. nextScreen.showWithSharedElementsTransitions(previousScreen.sharedElements.getToElements(), new Runnable() {
  135. @Override
  136. public void run() {
  137. if (onPushComplete != null) onPushComplete.resolve(null);
  138. parent.removeView(previousScreen);
  139. }
  140. });
  141. }
  142. });
  143. addScreen(nextScreen, layoutParams);
  144. }
  145. private void pushScreenToInvisibleStack(LayoutParams layoutParams, Screen nextScreen, Screen previousScreen,
  146. @Nullable final Promise onPushComplete) {
  147. nextScreen.setVisibility(View.INVISIBLE);
  148. nextScreen.setOnDisplayListener(new Screen.OnDisplayListener() {
  149. @Override
  150. public void onDisplay() {
  151. if (onPushComplete != null) onPushComplete.resolve(null);
  152. }
  153. });
  154. addScreen(nextScreen, layoutParams);
  155. parent.removeView(previousScreen);
  156. }
  157. private void addScreen(Screen screen, LayoutParams layoutParams) {
  158. addScreenBeforeSnackbarAndFabLayout(screen, layoutParams);
  159. stack.push(screen);
  160. }
  161. private void addScreenBeforeSnackbarAndFabLayout(Screen screen, LayoutParams layoutParams) {
  162. parent.addView(screen, parent.getChildCount() - 1, layoutParams);
  163. }
  164. public void pop(boolean animated, double jsPopTimestamp) {
  165. pop(animated, jsPopTimestamp, null);
  166. }
  167. public void pop(final boolean animated, final double jsPopTimestamp, @Nullable final OnScreenPop onScreenPop) {
  168. if (!canPop()) {
  169. return;
  170. }
  171. if (keyboardVisibilityDetector.isKeyboardVisible()) {
  172. keyboardVisibilityDetector.setKeyboardCloseListener(new Runnable() {
  173. @Override
  174. public void run() {
  175. keyboardVisibilityDetector.setKeyboardCloseListener(null);
  176. popInternal(animated, jsPopTimestamp, onScreenPop);
  177. }
  178. });
  179. keyboardVisibilityDetector.closeKeyboard();
  180. } else {
  181. popInternal(animated, jsPopTimestamp, onScreenPop);
  182. }
  183. }
  184. private void popInternal(final boolean animated, double jsPopTimestamp, @Nullable final OnScreenPop onScreenPop) {
  185. final Screen toRemove = stack.pop();
  186. final Screen previous = stack.peek();
  187. previous.screenParams.timestamp = jsPopTimestamp;
  188. swapScreens(animated, toRemove, previous, onScreenPop);
  189. }
  190. private void swapScreens(boolean animated, final Screen toRemove, Screen previous, OnScreenPop onScreenPop) {
  191. readdPrevious(previous);
  192. previous.setStyle();
  193. hideScreen(animated, toRemove, previous);
  194. if (onScreenPop != null) {
  195. onScreenPop.onScreenPopAnimationEnd();
  196. }
  197. }
  198. private void hideScreen(boolean animated, final Screen toRemove, final Screen previous) {
  199. NavigationApplication.instance.getEventEmitter().sendWillAppearEvent(previous.getScreenParams(), NavigationType.Pop);
  200. Runnable onAnimationEnd = new Runnable() {
  201. @Override
  202. public void run() {
  203. toRemove.destroy();
  204. parent.removeView(toRemove);
  205. NavigationApplication.instance.getEventEmitter().sendDidAppearEvent(previous.getScreenParams(), NavigationType.Pop);
  206. }
  207. };
  208. if (animated) {
  209. toRemove.animateHide(previous.sharedElements.getToElements(), onAnimationEnd, NavigationType.Pop);
  210. } else {
  211. toRemove.hide(previous.sharedElements.getToElements(), onAnimationEnd, NavigationType.Pop);
  212. }
  213. }
  214. public Screen peek() {
  215. return stack.peek();
  216. }
  217. private void readdPrevious(Screen previous) {
  218. previous.setVisibility(View.VISIBLE);
  219. parent.addView(previous, 0);
  220. }
  221. public void popToRoot(final boolean animated, final double jsPopTimestamp, @Nullable final OnScreenPop onScreenPop) {
  222. if (keyboardVisibilityDetector.isKeyboardVisible()) {
  223. keyboardVisibilityDetector.setKeyboardCloseListener(new Runnable() {
  224. @Override
  225. public void run() {
  226. keyboardVisibilityDetector.setKeyboardCloseListener(null);
  227. popToRootInternal(animated, jsPopTimestamp, onScreenPop);
  228. }
  229. });
  230. keyboardVisibilityDetector.closeKeyboard();
  231. } else {
  232. popToRootInternal(animated, jsPopTimestamp, onScreenPop);
  233. }
  234. }
  235. private void popToRootInternal(final boolean animated, double jsPopTimestamp, @Nullable final OnScreenPop onScreenPop) {
  236. while (canPop()) {
  237. if (stack.size() == 2) {
  238. popInternal(animated, jsPopTimestamp, onScreenPop);
  239. } else {
  240. popInternal(animated, jsPopTimestamp, null);
  241. }
  242. }
  243. }
  244. public void destroy() {
  245. for (Screen screen : stack) {
  246. screen.destroy();
  247. parent.removeView(screen);
  248. }
  249. stack.clear();
  250. }
  251. public boolean canPop() {
  252. return stack.size() > 1 && !isPreviousScreenAttachedToWindow();
  253. }
  254. private boolean isPreviousScreenAttachedToWindow() {
  255. Screen previousScreen = stack.get(stack.size() - 2);
  256. if (previousScreen.getParent() != null) {
  257. Log.w(TAG, "Can't pop stack. reason: previous screen is already attached");
  258. return true;
  259. }
  260. return false;
  261. }
  262. public void setScreenTopBarVisible(String screenInstanceId, final boolean visible, final boolean animate) {
  263. performOnScreen(screenInstanceId, new Task<Screen>() {
  264. @Override
  265. public void run(Screen param) {
  266. param.setTopBarVisible(visible, animate);
  267. }
  268. });
  269. }
  270. public void setScreenTitleBarTitle(String screenInstanceId, final String title) {
  271. performOnScreen(screenInstanceId, new Task<Screen>() {
  272. @Override
  273. public void run(Screen param) {
  274. param.setTitleBarTitle(title);
  275. }
  276. });
  277. }
  278. public void setScreenTitleBarSubtitle(String screenInstanceId, final String subtitle) {
  279. performOnScreen(screenInstanceId, new Task<Screen>() {
  280. @Override
  281. public void run(Screen param) {
  282. param.setTitleBarSubtitle(subtitle);
  283. }
  284. });
  285. }
  286. public void setScreenTitleBarRightButtons(String screenInstanceId, final String navigatorEventId, final List<TitleBarButtonParams> titleBarButtons) {
  287. performOnScreen(screenInstanceId, new Task<Screen>() {
  288. @Override
  289. public void run(Screen param) {
  290. param.setTitleBarRightButtons(navigatorEventId, titleBarButtons);
  291. }
  292. });
  293. }
  294. public void setScreenTitleBarLeftButton(String screenInstanceId, final String navigatorEventId, final TitleBarLeftButtonParams titleBarLeftButtonParams) {
  295. performOnScreen(screenInstanceId, new Task<Screen>() {
  296. @Override
  297. public void run(Screen screen) {
  298. screen.setTitleBarLeftButton(navigatorEventId, leftButtonOnClickListener, titleBarLeftButtonParams);
  299. }
  300. });
  301. }
  302. public void setFab(String screenInstanceId, final FabParams fabParams) {
  303. performOnScreen(screenInstanceId, new Task<Screen>() {
  304. @Override
  305. public void run(Screen screen) {
  306. screen.setFab(fabParams);
  307. }
  308. });
  309. }
  310. public void updateScreenStyle(String screenInstanceId, final Bundle styleParams) {
  311. performOnScreen(screenInstanceId, new Task<Screen>() {
  312. @Override
  313. public void run(Screen screen) {
  314. if (isScreenVisible(screen)) {
  315. screen.updateVisibleScreenStyle(styleParams);
  316. } else {
  317. screen.updateInvisibleScreenStyle(styleParams);
  318. }
  319. }
  320. });
  321. }
  322. private boolean isScreenVisible(Screen screen) {
  323. return isStackVisible && peek() == screen;
  324. }
  325. public void showContextualMenu(String screenInstanceId, final ContextualMenuParams params, final Callback onButtonClicked) {
  326. performOnScreen(screenInstanceId, new Task<Screen>() {
  327. @Override
  328. public void run(Screen screen) {
  329. screen.showContextualMenu(params, onButtonClicked);
  330. }
  331. });
  332. }
  333. public void dismissContextualMenu(String screenInstanceId) {
  334. performOnScreen(screenInstanceId, new Task<Screen>() {
  335. @Override
  336. public void run(Screen screen) {
  337. screen.dismissContextualMenu();
  338. }
  339. });
  340. }
  341. public void selectTopTabByTabIndex(String screenInstanceId, final int index) {
  342. performOnScreen(screenInstanceId, new Task<Screen>() {
  343. @Override
  344. public void run(Screen screen) {
  345. if (screen.screenParams.hasTopTabs()) {
  346. ((ViewPagerScreen) screen).selectTopTabByTabIndex(index);
  347. }
  348. }
  349. });
  350. }
  351. public void selectTopTabByScreen(final String screenInstanceId) {
  352. performOnScreen(screenInstanceId, new Task<Screen>() {
  353. @Override
  354. public void run(Screen screen) {
  355. ((ViewPagerScreen) screen).selectTopTabByTabByScreen(screenInstanceId);
  356. }
  357. });
  358. }
  359. public StyleParams getCurrentScreenStyleParams() {
  360. return stack.peek().getStyleParams();
  361. }
  362. public boolean handleBackPressInJs() {
  363. ScreenParams currentScreen = stack.peek().screenParams;
  364. if (currentScreen.overrideBackPressInJs) {
  365. NavigationApplication.instance.getEventEmitter().sendNavigatorEvent("backPress", currentScreen.getNavigatorEventId());
  366. return true;
  367. }
  368. return false;
  369. }
  370. private void performOnScreen(String screenInstanceId, Task<Screen> task) {
  371. if (stack.isEmpty()) {
  372. return;
  373. }
  374. for (Screen screen : stack) {
  375. if (screen.hasScreenInstance(screenInstanceId)) {
  376. task.run(screen);
  377. return;
  378. }
  379. }
  380. }
  381. public void show(NavigationType type) {
  382. isStackVisible = true;
  383. stack.peek().setStyle();
  384. stack.peek().setVisibility(View.VISIBLE);
  385. sendScreenAppearEvent(type, stack.peek());
  386. }
  387. private void sendScreenAppearEvent(final NavigationType type, final Screen screen) {
  388. if (type == NavigationType.InitialScreen) {
  389. sendInitialScreenAppearEvent(type, screen);
  390. } else {
  391. sendScreenAppearEvent(screen, type);
  392. }
  393. }
  394. private void sendInitialScreenAppearEvent(final NavigationType type, final Screen screen) {
  395. screen.setOnDisplayListener(new Screen.OnDisplayListener() {
  396. @Override
  397. public void onDisplay() {
  398. sendScreenAppearEvent(screen, type);
  399. }
  400. });
  401. }
  402. private void sendScreenAppearEvent(Screen screen, NavigationType type) {
  403. screen.getScreenParams().timestamp = System.currentTimeMillis();
  404. NavigationApplication.instance.getEventEmitter().sendWillAppearEvent(screen.getScreenParams(), type);
  405. NavigationApplication.instance.getEventEmitter().sendDidAppearEvent(screen.getScreenParams(), type);
  406. }
  407. public void hide(NavigationType type) {
  408. NavigationApplication.instance.getEventEmitter().sendWillDisappearEvent(stack.peek().getScreenParams(), type);
  409. NavigationApplication.instance.getEventEmitter().sendDidDisappearEvent(stack.peek().getScreenParams(), type);
  410. isStackVisible = false;
  411. stack.peek().setVisibility(View.INVISIBLE);
  412. }
  413. }