瀏覽代碼

V3 (#5372)

# Changes
* Support RN 0.60
* Migrate to AndroidX
* Improve draw behind StatusBar (Preparation for #4258)
* Don't push BottomTabs when keyboard is displayed (Fixes #4005, #3424)
    - It won't be needed to toggle the BottomTabs when Keyboard is visible
* BottomTab badge and dot indicator are not animated by default on Android (parity with iOS)

# Updating from v2
v3 is currently in alpha. To update simply npm install `3.0.0-alpha.11` - `npm install --save react-native-navigation@3.0.0-alpha.11`.
Breaking changes are outlined below.

## Layout system changes on **Android**
* Parent layouts (BottomsTabs, Stack, SideMenu) are always laid out behind the StatusBar.
* Components (`component` and `externalComponent`) are measured and offset according to the StatusBar.

In this release, We're changing the layout system in order to provide better support for immersive and full screen apps. In this release we've improved support for drawing behind the StatusBar,  next we'll address drawing behind the NavigationBar. 

Use the `drawBehind` and `translucent` options to control the StatusBar
```js
statusBar: {
  drawBehind: true,  // will draw a screen behind the StatusBar
  translucent: true // Usually you'll want to have drawBehind: true when this is true
}
```

While this isn't a breaking API change - there are a few breaking side effects.

### How will my app be effected
1. When the keyboard is opened, BottomTabs will now be drawn behind the keyboard and won't shift upwards. This is in parity with the current behaviour in iOS. For the most part, this isn't a breaking change. Toggling BottomTabs when TextInput's focus changes won't be needed anymore.

2. While parent controllers are drawn behind the StatusBar, their background isn't.
This means that when transitioning from a destinations drawn under the StatusBar to a destination drawn behind it, the application's default background color will be visible behind the StatusBar.
If you application's theme is dark, you might want to change the `windowBackground` property to mitigate this:

Add the following to your application's `style.xml`
```xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <item name="android:windowBackground">@color/backgroundColor</item>
    </style>

    <!--This is your application's default background color.
    It will be visible when the app is first opened (while the splash layout is visible)
    and when transitioning between a destination a screen drawn under the StatusBar to
    a destination drawn behind it-->
    <item name="backgroundColor" type="color">#f00</item>
</resources>
```

## AndroidX migration
We've migrated RNN to AndroidX, please follow migration instructions in the react-native repo.

## Removed SyncUiImplementation
[SyncUiImplementation](https://github.com/wix/react-native-navigation/blob/master/lib/android/app/src/reactNative57WixFork/java/com/reactnativenavigation/react/SyncUiImplementation.java) was used to overcome a bug in RN's UiImplementation. This workaround was added to RN's `UiImplementation` in RN 0.60 (thanks @SudoPlz) and can be removed from RNN.

If you're using `SyncUiImplementation` your app will fail to compile after upgrading to v3. Simply remove the following code from your `MainApplication.java`
```diff
- import com.facebook.react.uimanager.UIImplementationProvider;
- import com.reactnativenavigation.react.SyncUiImplementation;


- @Override
- protected UIImplementationProvider getUIImplementationProvider() {
-     return new SyncUiImplementation.Provider();
- }
```

## BottomTab badge and dot indicator are not animated by default on Android (parity with iOS)
Showing and hiding badge and dot indicator are now not animated by default. Badge animation is now controlled with the `bottomTab.animateBadge` property and dot indicator with `bottomTab.dotIndicator.animate` property.

#### The following option will show a badge with animation
```js
bottomTab: {
  badge: 'new,
  animateBadge: true
}
```

#### The following option will show a dot indicator with animation
```js
bottomTab: {
  dotIndicator: {
    visible: true,
    animate: true
  }
}
```

closes #5228
Guy Carmeli 5 年之前
父節點
當前提交
ef43070c26
No account linked to committer's email address
共有 100 個檔案被更改,包括 1168 行新增668 行删除
  1. 10
    17
      docs/docs/Installing.md
  2. 12
    10
      lib/android/app/build.gradle
  3. 3
    3
      lib/android/app/src/main/java/com/reactnativenavigation/NavigationActivity.java
  4. 2
    2
      lib/android/app/src/main/java/com/reactnativenavigation/NavigationApplication.java
  5. 3
    1
      lib/android/app/src/main/java/com/reactnativenavigation/anim/BaseAnimator.java
  6. 1
    1
      lib/android/app/src/main/java/com/reactnativenavigation/anim/FabCollapseBehaviour.java
  7. 1
    1
      lib/android/app/src/main/java/com/reactnativenavigation/anim/NavigationAnimator.java
  8. 44
    37
      lib/android/app/src/main/java/com/reactnativenavigation/anim/TopBarAnimator.java
  9. 2
    1
      lib/android/app/src/main/java/com/reactnativenavigation/anim/TopBarCollapseBehavior.java
  10. 10
    4
      lib/android/app/src/main/java/com/reactnativenavigation/parse/AnimationOptions.java
  11. 9
    1
      lib/android/app/src/main/java/com/reactnativenavigation/parse/BottomTabOptions.java
  12. 4
    2
      lib/android/app/src/main/java/com/reactnativenavigation/parse/DotIndicatorOptions.java
  13. 5
    2
      lib/android/app/src/main/java/com/reactnativenavigation/parse/LayoutFactory.java
  14. 0
    1
      lib/android/app/src/main/java/com/reactnativenavigation/parse/LayoutOptions.java
  15. 1
    1
      lib/android/app/src/main/java/com/reactnativenavigation/parse/ModalOptions.java
  16. 3
    3
      lib/android/app/src/main/java/com/reactnativenavigation/parse/Options.java
  17. 1
    1
      lib/android/app/src/main/java/com/reactnativenavigation/parse/OrientationOptions.java
  18. 13
    1
      lib/android/app/src/main/java/com/reactnativenavigation/parse/StatusBarOptions.java
  19. 1
    1
      lib/android/app/src/main/java/com/reactnativenavigation/parse/SubtitleOptions.java
  20. 1
    1
      lib/android/app/src/main/java/com/reactnativenavigation/parse/TitleOptions.java
  21. 1
    1
      lib/android/app/src/main/java/com/reactnativenavigation/parse/TopBarButtons.java
  22. 4
    0
      lib/android/app/src/main/java/com/reactnativenavigation/parse/TopBarOptions.java
  23. 1
    1
      lib/android/app/src/main/java/com/reactnativenavigation/parse/TopTabOptions.java
  24. 2
    2
      lib/android/app/src/main/java/com/reactnativenavigation/parse/TopTabsOptions.java
  25. 1
    1
      lib/android/app/src/main/java/com/reactnativenavigation/parse/Transitions.java
  26. 19
    1
      lib/android/app/src/main/java/com/reactnativenavigation/parse/ValueAnimationOptions.java
  27. 1
    1
      lib/android/app/src/main/java/com/reactnativenavigation/parse/params/Button.java
  28. 6
    1
      lib/android/app/src/main/java/com/reactnativenavigation/parse/params/Colour.java
  29. 1
    1
      lib/android/app/src/main/java/com/reactnativenavigation/parse/params/Orientation.java
  30. 1
    1
      lib/android/app/src/main/java/com/reactnativenavigation/parse/params/Text.java
  31. 1
    1
      lib/android/app/src/main/java/com/reactnativenavigation/parse/params/TitleDisplayMode.java
  32. 1
    1
      lib/android/app/src/main/java/com/reactnativenavigation/parse/parsers/LayoutNodeParser.java
  33. 63
    41
      lib/android/app/src/main/java/com/reactnativenavigation/presentation/BottomTabPresenter.java
  34. 18
    33
      lib/android/app/src/main/java/com/reactnativenavigation/presentation/BottomTabsPresenter.java
  35. 1
    1
      lib/android/app/src/main/java/com/reactnativenavigation/presentation/ComponentPresenter.java
  36. 23
    0
      lib/android/app/src/main/java/com/reactnativenavigation/presentation/ComponentPresenterBase.java
  37. 4
    0
      lib/android/app/src/main/java/com/reactnativenavigation/presentation/ExternalComponentPresenter.java
  38. 1
    1
      lib/android/app/src/main/java/com/reactnativenavigation/presentation/FabPresenter.java
  39. 4
    1
      lib/android/app/src/main/java/com/reactnativenavigation/presentation/OverlayManager.java
  40. 65
    32
      lib/android/app/src/main/java/com/reactnativenavigation/presentation/Presenter.java
  41. 10
    4
      lib/android/app/src/main/java/com/reactnativenavigation/presentation/RootPresenter.java
  42. 11
    3
      lib/android/app/src/main/java/com/reactnativenavigation/presentation/SideMenuPresenter.java
  43. 93
    83
      lib/android/app/src/main/java/com/reactnativenavigation/presentation/StackPresenter.java
  44. 8
    5
      lib/android/app/src/main/java/com/reactnativenavigation/react/NavigationModule.java
  45. 7
    7
      lib/android/app/src/main/java/com/reactnativenavigation/react/NavigationPackage.java
  46. 1
    1
      lib/android/app/src/main/java/com/reactnativenavigation/react/NavigationReactInitializer.java
  47. 1
    1
      lib/android/app/src/main/java/com/reactnativenavigation/react/ReactView.java
  48. 3
    3
      lib/android/app/src/main/java/com/reactnativenavigation/utils/ButtonPresenter.java
  49. 15
    3
      lib/android/app/src/main/java/com/reactnativenavigation/utils/CollectionUtils.java
  50. 2
    2
      lib/android/app/src/main/java/com/reactnativenavigation/utils/ColorUtils.java
  51. 1
    1
      lib/android/app/src/main/java/com/reactnativenavigation/utils/CommandListenerAdapter.java
  52. 17
    0
      lib/android/app/src/main/java/com/reactnativenavigation/utils/CoordinatorLayoutUtils.java
  53. 2
    2
      lib/android/app/src/main/java/com/reactnativenavigation/utils/ImageLoader.java
  54. 1
    1
      lib/android/app/src/main/java/com/reactnativenavigation/utils/ImageLoadingListenerAdapter.java
  55. 21
    0
      lib/android/app/src/main/java/com/reactnativenavigation/utils/LateInit.java
  56. 1
    1
      lib/android/app/src/main/java/com/reactnativenavigation/utils/NativeCommandListener.java
  57. 1
    1
      lib/android/app/src/main/java/com/reactnativenavigation/utils/ObjectUtils.java
  58. 1
    1
      lib/android/app/src/main/java/com/reactnativenavigation/utils/ReflectionUtils.java
  59. 30
    0
      lib/android/app/src/main/java/com/reactnativenavigation/utils/StatusBarUtils.java
  60. 1
    1
      lib/android/app/src/main/java/com/reactnativenavigation/utils/StringUtils.java
  61. 1
    1
      lib/android/app/src/main/java/com/reactnativenavigation/utils/TextViewUtils.java
  62. 1
    1
      lib/android/app/src/main/java/com/reactnativenavigation/utils/TypefaceLoader.java
  63. 2
    18
      lib/android/app/src/main/java/com/reactnativenavigation/utils/UiUtils.java
  64. 12
    2
      lib/android/app/src/main/java/com/reactnativenavigation/utils/ViewUtils.java
  65. 15
    0
      lib/android/app/src/main/java/com/reactnativenavigation/utils/WindowInsetsUtils.java
  66. 33
    8
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/ChildController.java
  67. 3
    1
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/ChildControllersRegistry.java
  68. 41
    8
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/ComponentViewController.java
  69. 1
    1
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/IdStack.java
  70. 59
    16
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/ParentController.java
  71. 4
    4
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/TitleBarButtonController.java
  72. 52
    11
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/ViewController.java
  73. 1
    1
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/YellowBoxDelegate.java
  74. 1
    1
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/YellowBoxHelper.java
  75. 10
    14
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/bottomtabs/AttachMode.java
  76. 1
    12
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/bottomtabs/BottomTabFinder.java
  77. 1
    1
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/bottomtabs/BottomTabsAttacher.java
  78. 42
    20
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/bottomtabs/BottomTabsController.java
  79. 2
    2
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/button/NavigationIconResolver.java
  80. 1
    1
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/externalcomponent/ExternalComponentCreator.java
  81. 36
    4
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/externalcomponent/ExternalComponentViewController.java
  82. 17
    5
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/modal/ModalPresenter.java
  83. 3
    2
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/modal/ModalStack.java
  84. 17
    19
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/navigator/Navigator.java
  85. 53
    52
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/sidemenu/SideMenuController.java
  86. 7
    7
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/stack/BackButtonHelper.java
  87. 41
    33
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/stack/StackController.java
  88. 3
    1
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/stack/StackControllerBuilder.java
  89. 66
    8
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/topbar/TopBarController.java
  90. 2
    2
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/toptabs/TopTabsAdapter.java
  91. 5
    6
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/toptabs/TopTabsController.java
  92. 19
    0
      lib/android/app/src/main/java/com/reactnativenavigation/views/BehaviourAdapter.java
  93. 24
    0
      lib/android/app/src/main/java/com/reactnativenavigation/views/BehaviourDelegate.java
  94. 10
    11
      lib/android/app/src/main/java/com/reactnativenavigation/views/BottomTabs.java
  95. 0
    4
      lib/android/app/src/main/java/com/reactnativenavigation/views/Component.java
  96. 5
    31
      lib/android/app/src/main/java/com/reactnativenavigation/views/ComponentLayout.java
  97. 2
    25
      lib/android/app/src/main/java/com/reactnativenavigation/views/ExternalComponentLayout.java
  98. 1
    1
      lib/android/app/src/main/java/com/reactnativenavigation/views/Fab.java
  99. 3
    2
      lib/android/app/src/main/java/com/reactnativenavigation/views/SideMenu.java
  100. 0
    0
      lib/android/app/src/main/java/com/reactnativenavigation/views/SideMenuRoot.java

+ 10
- 17
docs/docs/Installing.md 查看文件

@@ -45,18 +45,18 @@ $(SRCROOT)/../node_modules/react-native-navigation/lib/ios
45 45
 	{
46 46
 		NSURL *jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
47 47
 		[ReactNativeNavigation bootstrap:jsCodeLocation launchOptions:launchOptions];
48
-		
48
+
49 49
 		return YES;
50 50
 	}
51 51
 
52 52
 	@end
53 53
 	```
54 54
 
55
-3a. If, in Xcode, you see the following error message in `AppDelegate.m` next to `#import "RCTBundleURLProvider.h"`: 
55
+3a. If, in Xcode, you see the following error message in `AppDelegate.m` next to `#import "RCTBundleURLProvider.h"`:
56 56
 ```
57 57
 ! 'RCTBundleURLProvider.h' file not found
58 58
 ```
59
-This is because the `React` scheme is missing from your project. You can verify this by opening the `Product` menu and the `Scheme` submenu. 
59
+This is because the `React` scheme is missing from your project. You can verify this by opening the `Product` menu and the `Scheme` submenu.
60 60
 
61 61
 To make the `React` scheme available to your project, run `npm install -g react-native-git-upgrade` followed by `react-native-git-upgrade`. Once this is done, you can click back to the menu in Xcode: `Product -> Scheme -> Manage Schemes`, then click '+' to add a new scheme. From the `Target` menu, select "React", and click the checkbox to make the scheme `shared`. This should make the error disappear.
62 62
 
@@ -117,7 +117,6 @@ buildscript {
117 117
 allprojects {
118 118
 	repositories {
119 119
 +		google()
120
-+		mavenCentral()
121 120
 		mavenLocal()
122 121
 		jcenter()
123 122
 		maven {
@@ -133,8 +132,6 @@ allprojects {
133 132
 }
134 133
 
135 134
 ext {
136
--    buildToolsVersion = "26.0.3"
137
-+    buildToolsVersion = "27.0.3"
138 135
 -    minSdkVersion = 16
139 136
 +    minSdkVersion = 19
140 137
     compileSdkVersion = 26
@@ -171,17 +168,14 @@ android {
171 168
 
172 169
 dependencies {
173 170
 -    compile fileTree(dir: "libs", include: ["*.jar"])
174
--    compile "com.android.support:appcompat-v7:${rootProject.ext.supportLibVersion}"
175 171
 -    compile "com.facebook.react:react-native:+"  // From node_modules
176 172
 +    implementation fileTree(dir: "libs", include: ["*.jar"])
177
-+    implementation "com.android.support:appcompat-v7:${rootProject.ext.supportLibVersion}"
178 173
 +    implementation "com.facebook.react:react-native:+"  // From node_modules
179 174
 +    implementation project(':react-native-navigation')
180 175
 }
181 176
 ```
182 177
 
183 178
 ### 5 RNN and React Native version
184
-
185 179
 react-native-navigation supports multiple React Native versions. Target the React Native version required by your project by specifying the RNN build flavor in `android/app/build.gradle`.
186 180
 
187 181
 ```diff
@@ -205,11 +199,12 @@ android {
205 199
 >`reactNative55` - RN 0.55.x<Br>
206 200
 >`reactNative56` - RN 0.56.x<Br>
207 201
 >`reactNative57` - RN 0.57.0 - 0.57.4<Br>
208
->`reactNative57_5` - RN 0.57.5 and above<Br>
202
+>`reactNative57_5` - RN 0.57.5 - 0.59.9<Br>
203
+>`reactNative60` - RN 0.60.0 and above
209 204
 
210 205
 Now we need to instruct gradle how to build that flavor. To do so here two solutions:
211 206
 
212
-#### 5.1 Build app with gradle command 
207
+#### 5.1 Build app with gradle command
213 208
 
214 209
 **prefered solution** The RNN flavor you would like to build is specified in `app/build.gradle`. Therefore in order to compile only that flavor, instead of building your entire project using `./gradlew assembleDebug`, you should instruct gradle to build the app module: `./gradlew app:assembleDebug`. The easiest way is to add a package.json command to build and install your debug Android APK .
215 210
 
@@ -258,7 +253,7 @@ This file is located in `android/app/src/main/java/com/<yourproject>/MainActivit
258 253
 -import com.facebook.react.ReactActivity;
259 254
 +import com.reactnativenavigation.NavigationActivity;
260 255
 
261
--public class MainActivity extends ReactActivity { 
256
+-public class MainActivity extends ReactActivity {
262 257
 +public class MainActivity extends NavigationActivity {
263 258
 -    @Override
264 259
 -    protected String getMainComponentName() {
@@ -272,7 +267,7 @@ If you have any **react-native** related methods, you can safely delete them.
272 267
 ### 7. Update `MainApplication.java`
273 268
 
274 269
 This file is located in `android/app/src/main/java/com/<yourproject>/MainApplication.java`.
275
-	
270
+
276 271
 ```diff
277 272
 ...
278 273
 import android.app.Application;
@@ -292,7 +287,7 @@ import java.util.List;
292 287
 
293 288
 -public class MainApplication extends Application implements ReactApplication {
294 289
 +public class MainApplication extends NavigationApplication {
295
-+    
290
++
296 291
 +    @Override
297 292
 +    protected ReactGateway createReactGateway() {
298 293
 +        ReactNativeHost host = new NavigationReactNativeHost(this, isDebug(), createAdditionalReactPackages()) {
@@ -316,7 +311,7 @@ import java.util.List;
316 311
 +            // eg. new VectorIconsPackage()
317 312
 +        );
318 313
 +    }
319
-+  
314
++
320 315
 +    @Override
321 316
 +    public List<ReactPackage> createAdditionalReactPackages() {
322 317
 +        return getPackages();
@@ -365,8 +360,6 @@ dependencies {
365 360
 ## You can use react-native-navigation \o/
366 361
 
367 362
 Update `index.js` file
368
-
369
-
370 363
 ```diff
371 364
 +import { Navigation } from "react-native-navigation";
372 365
 -import {AppRegistry} from 'react-native';

+ 12
- 10
lib/android/app/build.gradle 查看文件

@@ -1,5 +1,4 @@
1 1
 apply plugin: 'com.android.library'
2
-apply from: '../prepare-robolectric.gradle'
3 2
 
4 3
 def safeExtGet(prop, fallback) {
5 4
     rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
@@ -7,7 +6,6 @@ def safeExtGet(prop, fallback) {
7 6
 
8 7
 def DEFAULT_COMPILE_SDK_VERSION = 28
9 8
 def DEFAULT_MIN_SDK_VERSION = 19
10
-def DEFAULT_SUPPORT_LIB_VERSION = '28.0.0'
11 9
 def DEFAULT_TARGET_SDK_VERSION = 28
12 10
 
13 11
 android {
@@ -32,6 +30,7 @@ android {
32 30
     }
33 31
 
34 32
     testOptions {
33
+        unitTests.includeAndroidResources = true
35 34
         unitTests.all { t ->
36 35
             reports {
37 36
                 html.enabled true
@@ -76,6 +75,10 @@ android {
76 75
             dimension "RNN.reactNativeVersion"
77 76
             buildConfigField("int", "REACT_NATVE_VERSION_MINOR", "57")
78 77
         }
78
+        reactNative60 {
79
+            dimension "RNN.reactNativeVersion"
80
+            buildConfigField("int", "REACT_NATVE_VERSION_MINOR", "60")
81
+        }
79 82
     }
80 83
 }
81 84
 
@@ -89,14 +92,13 @@ allprojects { p ->
89 92
     }
90 93
 }
91 94
 
92
-def supportLibVersion = safeExtGet('supportLibVersion', DEFAULT_SUPPORT_LIB_VERSION)
93
-
94 95
 dependencies {
95
-    implementation "com.android.support:design:${supportLibVersion}"
96
-    implementation "com.android.support:appcompat-v7:${supportLibVersion}"
97
-    implementation "com.android.support:support-v4:${supportLibVersion}"
96
+    implementation 'androidx.appcompat:appcompat:1.0.2'
97
+    implementation 'androidx.annotation:annotation:1.1.0'
98
+    implementation 'androidx.legacy:legacy-support-v4:1.0.0'
99
+    implementation 'com.google.android.material:material:1.1.0-alpha08'
98 100
 
99
-    implementation 'com.github.wix-playground:ahbottomnavigation:2.4.15'
101
+    implementation 'com.github.wix-playground:ahbottomnavigation:3.0.2'
100 102
     implementation 'com.github.wix-playground:reflow-animator:1.0.4'
101 103
     implementation 'com.github.clans:fab:1.6.4'
102 104
 
@@ -105,8 +107,8 @@ dependencies {
105 107
 
106 108
     // tests
107 109
     testImplementation 'junit:junit:4.12'
108
-    testImplementation 'org.robolectric:robolectric:3.5.1'
110
+    testImplementation "org.robolectric:robolectric:4.3-beta-1"
109 111
     testImplementation 'org.assertj:assertj-core:3.8.0'
110 112
     testImplementation 'com.squareup.assertj:assertj-android:1.1.1'
111
-    testImplementation 'org.mockito:mockito-core:2.13.0'
113
+    testImplementation 'org.mockito:mockito-core:2.28.2'
112 114
 }

+ 3
- 3
lib/android/app/src/main/java/com/reactnativenavigation/NavigationActivity.java 查看文件

@@ -5,9 +5,9 @@ import android.content.Intent;
5 5
 import android.graphics.Color;
6 6
 import android.os.Build;
7 7
 import android.os.Bundle;
8
-import android.support.annotation.NonNull;
9
-import android.support.annotation.Nullable;
10
-import android.support.v7.app.AppCompatActivity;
8
+import androidx.annotation.NonNull;
9
+import androidx.annotation.Nullable;
10
+import androidx.appcompat.app.AppCompatActivity;
11 11
 import android.view.KeyEvent;
12 12
 import android.view.View;
13 13
 

+ 2
- 2
lib/android/app/src/main/java/com/reactnativenavigation/NavigationApplication.java 查看文件

@@ -1,8 +1,8 @@
1 1
 package com.reactnativenavigation;
2 2
 
3 3
 import android.app.Application;
4
-import android.support.annotation.Nullable;
5
-import android.support.annotation.NonNull;
4
+import androidx.annotation.Nullable;
5
+import androidx.annotation.NonNull;
6 6
 
7 7
 import com.facebook.react.ReactApplication;
8 8
 import com.facebook.react.ReactNativeHost;

+ 3
- 1
lib/android/app/src/main/java/com/reactnativenavigation/anim/BaseAnimator.java 查看文件

@@ -5,7 +5,7 @@ import android.animation.AnimatorSet;
5 5
 import android.animation.ObjectAnimator;
6 6
 import android.animation.TimeInterpolator;
7 7
 import android.content.Context;
8
-import android.support.annotation.NonNull;
8
+import androidx.annotation.NonNull;
9 9
 import android.view.View;
10 10
 import android.view.animation.AccelerateDecelerateInterpolator;
11 11
 import android.view.animation.DecelerateInterpolator;
@@ -34,6 +34,8 @@ class BaseAnimator {
34 34
         set.setDuration(DURATION);
35 35
         ObjectAnimator translationY = ObjectAnimator.ofFloat(view, TRANSLATION_Y, this.translationY, 0);
36 36
         ObjectAnimator alpha = ObjectAnimator.ofFloat(view, ALPHA, 0, 1);
37
+        translationY.setDuration(DURATION);
38
+        alpha.setDuration(DURATION);
37 39
         set.playTogether(translationY, alpha);
38 40
         return set;
39 41
     }

+ 1
- 1
lib/android/app/src/main/java/com/reactnativenavigation/anim/FabCollapseBehaviour.java 查看文件

@@ -1,7 +1,7 @@
1 1
 package com.reactnativenavigation.anim;
2 2
 
3 3
 
4
-import android.support.annotation.NonNull;
4
+import androidx.annotation.NonNull;
5 5
 
6 6
 import com.reactnativenavigation.interfaces.ScrollEventListener;
7 7
 

+ 1
- 1
lib/android/app/src/main/java/com/reactnativenavigation/anim/NavigationAnimator.java 查看文件

@@ -4,7 +4,7 @@ import android.animation.Animator;
4 4
 import android.animation.AnimatorListenerAdapter;
5 5
 import android.animation.AnimatorSet;
6 6
 import android.content.Context;
7
-import android.support.annotation.RestrictTo;
7
+import androidx.annotation.RestrictTo;
8 8
 import android.view.View;
9 9
 import android.view.ViewGroup;
10 10
 

+ 44
- 37
lib/android/app/src/main/java/com/reactnativenavigation/anim/TopBarAnimator.java 查看文件

@@ -7,17 +7,16 @@ import android.animation.AnimatorSet;
7 7
 import android.animation.ObjectAnimator;
8 8
 import android.animation.TimeInterpolator;
9 9
 import android.view.View;
10
-import android.view.animation.AccelerateDecelerateInterpolator;
11 10
 import android.view.animation.DecelerateInterpolator;
12 11
 import android.view.animation.LinearInterpolator;
13 12
 
14 13
 import com.reactnativenavigation.parse.AnimationOptions;
15
-import com.reactnativenavigation.utils.ViewUtils;
14
+import com.reactnativenavigation.views.StackLayout;
16 15
 import com.reactnativenavigation.views.topbar.TopBar;
17 16
 
18
-import javax.annotation.Nullable;
19
-
20 17
 import static android.view.View.TRANSLATION_Y;
18
+import static com.reactnativenavigation.utils.ObjectUtils.perform;
19
+import static com.reactnativenavigation.utils.ViewUtils.getHeight;
21 20
 
22 21
 public class TopBarAnimator {
23 22
 
@@ -25,73 +24,67 @@ public class TopBarAnimator {
25 24
     private static final int DURATION = 300;
26 25
     private static final TimeInterpolator DECELERATE = new DecelerateInterpolator();
27 26
     private static final TimeInterpolator LINEAR = new LinearInterpolator();
28
-    private static final TimeInterpolator ACCELERATE_DECELERATE = new AccelerateDecelerateInterpolator();
29 27
 
30 28
     private TopBar topBar;
31 29
     private String stackId;
32 30
     private Animator hideAnimator;
33 31
     private Animator showAnimator;
34 32
 
35
-    public TopBarAnimator(TopBar topBar) {
33
+    public TopBarAnimator() {
34
+    }
35
+
36
+    TopBarAnimator(TopBar topBar) {
36 37
         this.topBar = topBar;
37 38
     }
38 39
 
39
-    public TopBarAnimator(TopBar topBar, @Nullable String stackId) {
40
+    public void bindView(TopBar topBar, StackLayout stack) {
40 41
         this.topBar = topBar;
41
-        this.stackId = stackId;
42
+        stackId = stack.getStackId();
42 43
     }
43 44
 
44
-    public void show(AnimationOptions options) {
45
+    public void show(AnimationOptions options, int translationStartDy) {
45 46
         topBar.setVisibility(View.VISIBLE);
46 47
         if (options.hasValue() && (!options.id.hasValue() || options.id.get().equals(stackId))) {
48
+            options.setValueDy(TRANSLATION_Y, -translationStartDy, 0);
47 49
             showAnimator = options.getAnimation(topBar);
48 50
         } else {
49
-            showAnimator = getDefaultShowAnimator(-1 * ViewUtils.getHeight(topBar), DECELERATE, DURATION);
51
+            showAnimator = getDefaultShowAnimator(translationStartDy, DECELERATE, DURATION);
50 52
         }
51
-        show();
53
+        showInternal();
52 54
     }
53 55
 
54 56
     public void show(float startTranslation) {
55 57
         showAnimator = getDefaultShowAnimator(startTranslation, LINEAR, DEFAULT_COLLAPSE_DURATION);
56
-        show();
58
+        showInternal();
57 59
     }
58 60
 
59
-    private void show() {
61
+    private void showInternal() {
60 62
         showAnimator.addListener(new AnimatorListenerAdapter() {
61 63
             @Override
62 64
             public void onAnimationStart(Animator animation) {
63 65
                 topBar.setVisibility(View.VISIBLE);
64 66
             }
65 67
         });
66
-        topBar.resetAnimationOptions();
67 68
         if (isAnimatingHide()) hideAnimator.cancel();
68 69
         showAnimator.start();
69 70
     }
70 71
 
71
-    private AnimatorSet getDefaultShowAnimator(float startTranslation, TimeInterpolator interpolator, int duration) {
72
-        ObjectAnimator showAnimator = ObjectAnimator.ofFloat(topBar, TRANSLATION_Y, startTranslation, 0);
73
-        showAnimator.setInterpolator(interpolator);
74
-        showAnimator.setDuration(duration);
75
-        AnimatorSet set = new AnimatorSet();
76
-        set.play(showAnimator);
77
-        return set;
78
-    }
79
-
80
-    public void hide(AnimationOptions options, Runnable onAnimationEnd) {
72
+    public void hide(AnimationOptions options, Runnable onAnimationEnd, float translationStartDy, float translationEndDy) {
81 73
         if (options.hasValue() && (!options.id.hasValue() || options.id.get().equals(stackId))) {
74
+            options.setValueDy(TRANSLATION_Y, translationStartDy, -translationEndDy);
82 75
             hideAnimator = options.getAnimation(topBar);
83 76
         } else {
84
-            hideAnimator = getDefaultHideAnimator(0, ACCELERATE_DECELERATE, DURATION);
77
+            hideAnimator = getDefaultHideAnimator(translationStartDy, translationEndDy, DECELERATE, DURATION);
85 78
         }
86
-        hide(onAnimationEnd);
79
+        hideInternal(onAnimationEnd);
87 80
     }
88 81
 
89
-    void hide(float startTranslation) {
90
-        hideAnimator = getDefaultHideAnimator(startTranslation, LINEAR, DEFAULT_COLLAPSE_DURATION);
91
-        hide(() -> {});
82
+    public void hide(float translationStart, float translationEndDy) {
83
+        hideAnimator = getDefaultHideAnimator(translationStart, translationEndDy, LINEAR, DEFAULT_COLLAPSE_DURATION);
84
+        hideInternal(() -> {});
92 85
     }
93 86
 
94
-    private void hide(Runnable onAnimationEnd) {
87
+    private void hideInternal(Runnable onAnimationEnd) {
95 88
         hideAnimator.addListener(new AnimatorListenerAdapter() {
96 89
             @Override
97 90
             public void onAnimationEnd(Animator animation) {
@@ -103,13 +96,6 @@ public class TopBarAnimator {
103 96
         hideAnimator.start();
104 97
     }
105 98
 
106
-    private Animator getDefaultHideAnimator(float startTranslation, TimeInterpolator interpolator, int duration) {
107
-        ObjectAnimator hideAnimator = ObjectAnimator.ofFloat(topBar, TRANSLATION_Y, startTranslation, -1 * topBar.getMeasuredHeight());
108
-        hideAnimator.setInterpolator(interpolator);
109
-        hideAnimator.setDuration(duration);
110
-        return hideAnimator;
111
-    }
112
-
113 99
     public boolean isAnimatingHide() {
114 100
         return hideAnimator != null && hideAnimator.isRunning();
115 101
     }
@@ -117,4 +103,25 @@ public class TopBarAnimator {
117 103
     public boolean isAnimatingShow() {
118 104
          return showAnimator != null && showAnimator.isRunning();
119 105
     }
106
+
107
+    public boolean isAnimating() {
108
+        return perform(showAnimator, false, Animator::isRunning) ||
109
+               perform(hideAnimator, false, Animator::isRunning);
110
+    }
111
+
112
+    private AnimatorSet getDefaultShowAnimator(float translationStart, TimeInterpolator interpolator, int duration) {
113
+        ObjectAnimator showAnimator = ObjectAnimator.ofFloat(topBar, TRANSLATION_Y, -getHeight(topBar) - translationStart, 0);
114
+        showAnimator.setInterpolator(interpolator);
115
+        showAnimator.setDuration(duration);
116
+        AnimatorSet set = new AnimatorSet();
117
+        set.play(showAnimator);
118
+        return set;
119
+    }
120
+
121
+    private Animator getDefaultHideAnimator(float translationStart, float translationEndDy, TimeInterpolator interpolator, int duration) {
122
+        ObjectAnimator hideAnimator = ObjectAnimator.ofFloat(topBar, TRANSLATION_Y, translationStart, -topBar.getMeasuredHeight() - translationEndDy);
123
+        hideAnimator.setInterpolator(interpolator);
124
+        hideAnimator.setDuration(duration);
125
+        return hideAnimator;
126
+    }
120 127
 }

+ 2
- 1
lib/android/app/src/main/java/com/reactnativenavigation/anim/TopBarCollapseBehavior.java 查看文件

@@ -2,6 +2,7 @@ package com.reactnativenavigation.anim;
2 2
 
3 3
 
4 4
 import android.view.View;
5
+import android.view.ViewGroup;
5 6
 
6 7
 import com.reactnativenavigation.interfaces.ScrollEventListener;
7 8
 import com.reactnativenavigation.views.topbar.TopBar;
@@ -58,6 +59,6 @@ public class TopBarCollapseBehavior implements ScrollEventListener.OnScrollListe
58 59
 
59 60
     @Override
60 61
     public void onHide() {
61
-        animator.hide(topBar.getTranslationY());
62
+        animator.hide(topBar.getTranslationY(), ((ViewGroup.MarginLayoutParams) topBar.getLayoutParams()).topMargin);
62 63
     }
63 64
 }

+ 10
- 4
lib/android/app/src/main/java/com/reactnativenavigation/parse/AnimationOptions.java 查看文件

@@ -20,8 +20,9 @@ import java.util.HashSet;
20 20
 import java.util.Iterator;
21 21
 import java.util.List;
22 22
 
23
-public class AnimationOptions {
23
+import static com.reactnativenavigation.utils.CollectionUtils.*;
24 24
 
25
+public class AnimationOptions {
25 26
     public static AnimationOptions parse(JSONObject json) {
26 27
         AnimationOptions options = new AnimationOptions();
27 28
         if (json == null) return options;
@@ -78,9 +79,7 @@ public class AnimationOptions {
78 79
         if (!hasAnimation()) return defaultAnimation;
79 80
         AnimatorSet animationSet = new AnimatorSet();
80 81
         List<Animator> animators = new ArrayList<>();
81
-        for (ValueAnimationOptions options : valueOptions) {
82
-            animators.add(options.getAnimation(view));
83
-        }
82
+        forEach(valueOptions, options -> animators.add(options.getAnimation(view)));
84 83
         animationSet.playTogether(animators);
85 84
         return animationSet;
86 85
     }
@@ -110,4 +109,11 @@ public class AnimationOptions {
110 109
     public boolean hasAnimation() {
111 110
         return !valueOptions.isEmpty();
112 111
     }
112
+
113
+    public void setValueDy(Property<View, Float> animation, float fromDelta, float toDelta) {
114
+        first(valueOptions, o -> o.equals(animation), param -> {
115
+            param.setFromDelta(fromDelta);
116
+            param.setToDelta(toDelta);
117
+        });
118
+    }
113 119
 }

+ 9
- 1
lib/android/app/src/main/java/com/reactnativenavigation/parse/BottomTabOptions.java 查看文件

@@ -1,14 +1,16 @@
1 1
 package com.reactnativenavigation.parse;
2 2
 
3 3
 import android.graphics.Typeface;
4
-import android.support.annotation.Nullable;
5 4
 
5
+import com.reactnativenavigation.parse.params.Bool;
6 6
 import com.reactnativenavigation.parse.params.Colour;
7
+import com.reactnativenavigation.parse.params.NullBool;
7 8
 import com.reactnativenavigation.parse.params.NullColor;
8 9
 import com.reactnativenavigation.parse.params.NullNumber;
9 10
 import com.reactnativenavigation.parse.params.NullText;
10 11
 import com.reactnativenavigation.parse.params.Number;
11 12
 import com.reactnativenavigation.parse.params.Text;
13
+import com.reactnativenavigation.parse.parsers.BoolParser;
12 14
 import com.reactnativenavigation.parse.parsers.ColorParser;
13 15
 import com.reactnativenavigation.parse.parsers.NumberParser;
14 16
 import com.reactnativenavigation.parse.parsers.TextParser;
@@ -16,6 +18,8 @@ import com.reactnativenavigation.utils.TypefaceLoader;
16 18
 
17 19
 import org.json.JSONObject;
18 20
 
21
+import androidx.annotation.Nullable;
22
+
19 23
 public class BottomTabOptions {
20 24
 
21 25
     public static BottomTabOptions parse(TypefaceLoader typefaceManager, JSONObject json) {
@@ -30,6 +34,7 @@ public class BottomTabOptions {
30 34
         options.selectedIconColor = ColorParser.parse(json, "selectedIconColor");
31 35
         options.badge = TextParser.parse(json, "badge");
32 36
         options.badgeColor = ColorParser.parse(json, "badgeColor");
37
+        options.animateBadge = BoolParser.parse(json, "animateBadge");
33 38
         options.testId = TextParser.parse(json, "testID");
34 39
         options.fontFamily = typefaceManager.getTypeFace(json.optString("fontFamily", ""));
35 40
         options.fontSize = NumberParser.parse(json, "fontSize");
@@ -47,6 +52,7 @@ public class BottomTabOptions {
47 52
     public Text testId = new NullText();
48 53
     public Text badge = new NullText();
49 54
     public Colour badgeColor = new NullColor();
55
+    public Bool animateBadge = new NullBool();
50 56
     public DotIndicatorOptions dotIndicator = new DotIndicatorOptions();
51 57
     public Number fontSize = new NullNumber();
52 58
     public Number selectedFontSize = new NullNumber();
@@ -62,6 +68,7 @@ public class BottomTabOptions {
62 68
         if (other.selectedIconColor.hasValue()) selectedIconColor = other.selectedIconColor;
63 69
         if (other.badge.hasValue()) badge = other.badge;
64 70
         if (other.badgeColor.hasValue()) badgeColor = other.badgeColor;
71
+        if (other.animateBadge.hasValue()) animateBadge = other.animateBadge;
65 72
         if (other.testId.hasValue()) testId = other.testId;
66 73
         if (other.fontSize.hasValue()) fontSize = other.fontSize;
67 74
         if (other.selectedFontSize.hasValue()) selectedFontSize = other.selectedFontSize;
@@ -78,6 +85,7 @@ public class BottomTabOptions {
78 85
         if (!selectedIconColor.hasValue()) selectedIconColor = defaultOptions.selectedIconColor;
79 86
         if (!badge.hasValue()) badge = defaultOptions.badge;
80 87
         if (!badgeColor.hasValue()) badgeColor = defaultOptions.badgeColor;
88
+        if (!animateBadge.hasValue()) animateBadge = defaultOptions.animateBadge;
81 89
         if (!fontSize.hasValue()) fontSize = defaultOptions.fontSize;
82 90
         if (!selectedFontSize.hasValue()) selectedFontSize = defaultOptions.selectedFontSize;
83 91
         if (fontFamily == null) fontFamily = defaultOptions.fontFamily;

+ 4
- 2
lib/android/app/src/main/java/com/reactnativenavigation/parse/DotIndicatorOptions.java 查看文件

@@ -1,7 +1,5 @@
1 1
 package com.reactnativenavigation.parse;
2 2
 
3
-import android.support.annotation.Nullable;
4
-
5 3
 import com.reactnativenavigation.parse.params.Bool;
6 4
 import com.reactnativenavigation.parse.params.Colour;
7 5
 import com.reactnativenavigation.parse.params.NullBool;
@@ -14,6 +12,8 @@ import com.reactnativenavigation.parse.parsers.NumberParser;
14 12
 
15 13
 import org.json.JSONObject;
16 14
 
15
+import androidx.annotation.Nullable;
16
+
17 17
 public class DotIndicatorOptions {
18 18
     public static DotIndicatorOptions parse(@Nullable JSONObject json) {
19 19
         DotIndicatorOptions options = new DotIndicatorOptions();
@@ -22,6 +22,7 @@ public class DotIndicatorOptions {
22 22
         options.color = ColorParser.parse(json, "color");
23 23
         options.size = NumberParser.parse(json, "size");
24 24
         options.visible = BoolParser.parse(json, "visible");
25
+        options.animate = BoolParser.parse(json, "animate");
25 26
 
26 27
         return options;
27 28
     }
@@ -29,6 +30,7 @@ public class DotIndicatorOptions {
29 30
     public Colour color = new NullColor();
30 31
     public Number size = new NullNumber();
31 32
     public Bool visible = new NullBool();
33
+    public Bool animate = new NullBool();
32 34
 
33 35
     public boolean hasValue() {
34 36
         return visible.hasValue();

+ 5
- 2
lib/android/app/src/main/java/com/reactnativenavigation/parse/LayoutFactory.java 查看文件

@@ -1,13 +1,12 @@
1 1
 package com.reactnativenavigation.parse;
2 2
 
3 3
 import android.app.Activity;
4
-import android.support.annotation.NonNull;
5
-import android.support.annotation.RestrictTo;
6 4
 
7 5
 import com.facebook.react.ReactInstanceManager;
8 6
 import com.reactnativenavigation.presentation.BottomTabPresenter;
9 7
 import com.reactnativenavigation.presentation.BottomTabsPresenter;
10 8
 import com.reactnativenavigation.presentation.ComponentPresenter;
9
+import com.reactnativenavigation.presentation.ExternalComponentPresenter;
11 10
 import com.reactnativenavigation.presentation.Presenter;
12 11
 import com.reactnativenavigation.presentation.RenderChecker;
13 12
 import com.reactnativenavigation.presentation.SideMenuPresenter;
@@ -37,6 +36,9 @@ import java.util.ArrayList;
37 36
 import java.util.List;
38 37
 import java.util.Map;
39 38
 
39
+import androidx.annotation.NonNull;
40
+import androidx.annotation.RestrictTo;
41
+
40 42
 import static com.reactnativenavigation.parse.Options.parse;
41 43
 
42 44
 public class LayoutFactory {
@@ -168,6 +170,7 @@ public class LayoutFactory {
168 170
                 externalComponentCreators.get(externalComponent.name.get()),
169 171
                 reactInstanceManager,
170 172
                 new EventEmitter(reactInstanceManager.getCurrentReactContext()),
173
+                new ExternalComponentPresenter(),
171 174
                 parse(typefaceManager, node.getOptions())
172 175
         );
173 176
     }

+ 0
- 1
lib/android/app/src/main/java/com/reactnativenavigation/parse/LayoutOptions.java 查看文件

@@ -35,7 +35,6 @@ public class LayoutOptions {
35 35
         if (other.topMargin.hasValue()) topMargin = other.topMargin;
36 36
         if (other.orientation.hasValue()) orientation = other.orientation;
37 37
         if (other.direction.hasValue()) direction = other.direction;
38
-
39 38
     }
40 39
 
41 40
     public void mergeWithDefault(LayoutOptions defaultOptions) {

+ 1
- 1
lib/android/app/src/main/java/com/reactnativenavigation/parse/ModalOptions.java 查看文件

@@ -1,6 +1,6 @@
1 1
 package com.reactnativenavigation.parse;
2 2
 
3
-import android.support.annotation.NonNull;
3
+import androidx.annotation.NonNull;
4 4
 
5 5
 import com.reactnativenavigation.parse.params.Bool;
6 6
 import com.reactnativenavigation.parse.params.NullBool;

+ 3
- 3
lib/android/app/src/main/java/com/reactnativenavigation/parse/Options.java 查看文件

@@ -1,8 +1,5 @@
1 1
 package com.reactnativenavigation.parse;
2 2
 
3
-import android.support.annotation.CheckResult;
4
-import android.support.annotation.NonNull;
5
-
6 3
 import com.reactnativenavigation.parse.params.NullBool;
7 4
 import com.reactnativenavigation.parse.params.NullNumber;
8 5
 import com.reactnativenavigation.parse.params.NullText;
@@ -10,6 +7,9 @@ import com.reactnativenavigation.utils.TypefaceLoader;
10 7
 
11 8
 import org.json.JSONObject;
12 9
 
10
+import androidx.annotation.CheckResult;
11
+import androidx.annotation.NonNull;
12
+
13 13
 public class Options {
14 14
     public static final Options EMPTY = new Options();
15 15
 

+ 1
- 1
lib/android/app/src/main/java/com/reactnativenavigation/parse/OrientationOptions.java 查看文件

@@ -1,6 +1,6 @@
1 1
 package com.reactnativenavigation.parse;
2 2
 
3
-import android.support.annotation.CheckResult;
3
+import androidx.annotation.CheckResult;
4 4
 
5 5
 import com.reactnativenavigation.parse.params.Orientation;
6 6
 

+ 13
- 1
lib/android/app/src/main/java/com/reactnativenavigation/parse/StatusBarOptions.java 查看文件

@@ -1,6 +1,6 @@
1 1
 package com.reactnativenavigation.parse;
2 2
 
3
-import android.support.annotation.Nullable;
3
+import androidx.annotation.Nullable;
4 4
 
5 5
 import com.reactnativenavigation.parse.params.Bool;
6 6
 import com.reactnativenavigation.parse.params.Colour;
@@ -46,6 +46,7 @@ public class StatusBarOptions {
46 46
         result.textColorScheme = TextColorScheme.fromString(json.optString("style"));
47 47
         result.visible = BoolParser.parse(json, "visible");
48 48
         result.drawBehind = BoolParser.parse(json, "drawBehind");
49
+        result.translucent = BoolParser.parse(json, "translucent");
49 50
 
50 51
         return result;
51 52
     }
@@ -54,12 +55,14 @@ public class StatusBarOptions {
54 55
     public TextColorScheme textColorScheme = TextColorScheme.None;
55 56
     public Bool visible = new NullBool();
56 57
     public Bool drawBehind = new NullBool();
58
+    public Bool translucent = new NullBool();
57 59
 
58 60
     public void mergeWith(StatusBarOptions other) {
59 61
         if (other.backgroundColor.hasValue()) backgroundColor = other.backgroundColor;
60 62
         if (other.textColorScheme.hasValue()) textColorScheme = other.textColorScheme;
61 63
         if (other.visible.hasValue()) visible = other.visible;
62 64
         if (other.drawBehind.hasValue()) drawBehind = other.drawBehind;
65
+        if (other.translucent.hasValue()) translucent = other.translucent;
63 66
     }
64 67
 
65 68
     public void mergeWithDefault(StatusBarOptions defaultOptions) {
@@ -67,5 +70,14 @@ public class StatusBarOptions {
67 70
         if (!textColorScheme.hasValue()) textColorScheme = defaultOptions.textColorScheme;
68 71
         if (!visible.hasValue()) visible = defaultOptions.visible;
69 72
         if (!drawBehind.hasValue()) drawBehind = defaultOptions.drawBehind;
73
+        if (!translucent.hasValue()) translucent = defaultOptions.translucent;
74
+    }
75
+
76
+    public boolean isHiddenOrDrawBehind() {
77
+        return drawBehind.isTrue() || visible.isFalse();
78
+    }
79
+
80
+    public boolean hasTransparency() {
81
+        return translucent.isTrue() || visible.isFalse() || backgroundColor.hasTransparency();
70 82
     }
71 83
 }

+ 1
- 1
lib/android/app/src/main/java/com/reactnativenavigation/parse/SubtitleOptions.java 查看文件

@@ -1,7 +1,7 @@
1 1
 package com.reactnativenavigation.parse;
2 2
 
3 3
 import android.graphics.Typeface;
4
-import android.support.annotation.Nullable;
4
+import androidx.annotation.Nullable;
5 5
 
6 6
 import com.reactnativenavigation.parse.params.Colour;
7 7
 import com.reactnativenavigation.parse.params.Fraction;

+ 1
- 1
lib/android/app/src/main/java/com/reactnativenavigation/parse/TitleOptions.java 查看文件

@@ -1,7 +1,7 @@
1 1
 package com.reactnativenavigation.parse;
2 2
 
3 3
 import android.graphics.Typeface;
4
-import android.support.annotation.Nullable;
4
+import androidx.annotation.Nullable;
5 5
 
6 6
 import com.reactnativenavigation.parse.params.Colour;
7 7
 import com.reactnativenavigation.parse.params.Fraction;

+ 1
- 1
lib/android/app/src/main/java/com/reactnativenavigation/parse/TopBarButtons.java 查看文件

@@ -1,6 +1,6 @@
1 1
 package com.reactnativenavigation.parse;
2 2
 
3
-import android.support.annotation.Nullable;
3
+import androidx.annotation.Nullable;
4 4
 
5 5
 import com.reactnativenavigation.parse.params.Button;
6 6
 import com.reactnativenavigation.utils.CollectionUtils;

+ 4
- 0
lib/android/app/src/main/java/com/reactnativenavigation/parse/TopBarOptions.java 查看文件

@@ -134,4 +134,8 @@ public class TopBarOptions {
134 134
             subtitle.text = new NullText();
135 135
         }
136 136
     }
137
+
138
+    public boolean isHiddenOrDrawBehind() {
139
+        return drawBehind.isTrue() || visible.isFalse();
140
+    }
137 141
 }

+ 1
- 1
lib/android/app/src/main/java/com/reactnativenavigation/parse/TopTabOptions.java 查看文件

@@ -1,7 +1,7 @@
1 1
 package com.reactnativenavigation.parse;
2 2
 
3 3
 import android.graphics.Typeface;
4
-import android.support.annotation.Nullable;
4
+import androidx.annotation.Nullable;
5 5
 
6 6
 import com.reactnativenavigation.parse.params.NullText;
7 7
 import com.reactnativenavigation.parse.params.Text;

+ 2
- 2
lib/android/app/src/main/java/com/reactnativenavigation/parse/TopTabsOptions.java 查看文件

@@ -1,7 +1,7 @@
1 1
 package com.reactnativenavigation.parse;
2 2
 
3
-import android.support.annotation.NonNull;
4
-import android.support.annotation.Nullable;
3
+import androidx.annotation.NonNull;
4
+import androidx.annotation.Nullable;
5 5
 
6 6
 import com.reactnativenavigation.parse.params.Bool;
7 7
 import com.reactnativenavigation.parse.params.Colour;

+ 1
- 1
lib/android/app/src/main/java/com/reactnativenavigation/parse/Transitions.java 查看文件

@@ -1,6 +1,6 @@
1 1
 package com.reactnativenavigation.parse;
2 2
 
3
-import android.support.annotation.RestrictTo;
3
+import androidx.annotation.RestrictTo;
4 4
 
5 5
 import org.json.JSONArray;
6 6
 import org.json.JSONObject;

+ 19
- 1
lib/android/app/src/main/java/com/reactnativenavigation/parse/ValueAnimationOptions.java 查看文件

@@ -35,14 +35,28 @@ public class ValueAnimationOptions {
35 35
     private Property<View, Float> animProp;
36 36
 
37 37
     private FloatParam from = new NullFloatParam();
38
+    private FloatParam fromDelta = new FloatParam(0f);
38 39
     private FloatParam to = new NullFloatParam();
40
+    private FloatParam toDelta = new FloatParam(0f);
39 41
     private Number duration = new NullNumber();
40 42
     private Number startDelay = new NullNumber();
41 43
     private Interpolation interpolation = Interpolation.NO_VALUE;
42 44
 
45
+    void setFromDelta(float fromDelta) {
46
+        this.fromDelta = new FloatParam(fromDelta);
47
+    }
48
+
49
+    void setToDelta(float toDelta) {
50
+        this.toDelta = new FloatParam(toDelta);
51
+    }
52
+
43 53
     Animator getAnimation(View view) {
44 54
         if (!from.hasValue() || !to.hasValue()) throw new IllegalArgumentException("Params 'from' and 'to' are mandatory");
45
-        ObjectAnimator animator = ObjectAnimator.ofFloat(view, animProp, from.get(), to.get());
55
+        ObjectAnimator animator = ObjectAnimator.ofFloat(view,
56
+                animProp,
57
+                from.get() + fromDelta.get(),
58
+                to.get() + toDelta.get()
59
+        );
46 60
         animator.setInterpolator(interpolation.getInterpolator());
47 61
         if (duration.hasValue()) animator.setDuration(duration.get());
48 62
         if (startDelay.hasValue()) animator.setStartDelay(startDelay.get());
@@ -56,6 +70,10 @@ public class ValueAnimationOptions {
56 70
         return animProp.equals(((ValueAnimationOptions) o).animProp);
57 71
     }
58 72
 
73
+    public boolean equals(Property<View, Float> animationProperty) {
74
+        return animProp.getName().equals(animationProperty.getName());
75
+    }
76
+
59 77
     @Override
60 78
     public int hashCode() {
61 79
         return animProp.hashCode();

+ 1
- 1
lib/android/app/src/main/java/com/reactnativenavigation/parse/params/Button.java 查看文件

@@ -1,7 +1,7 @@
1 1
 package com.reactnativenavigation.parse.params;
2 2
 
3 3
 import android.graphics.Typeface;
4
-import android.support.annotation.Nullable;
4
+import androidx.annotation.Nullable;
5 5
 import android.view.MenuItem;
6 6
 
7 7
 import com.reactnativenavigation.parse.Component;

+ 6
- 1
lib/android/app/src/main/java/com/reactnativenavigation/parse/params/Colour.java 查看文件

@@ -1,6 +1,7 @@
1 1
 package com.reactnativenavigation.parse.params;
2 2
 
3
-import android.support.annotation.ColorInt;
3
+import android.graphics.Color;
4
+import androidx.annotation.ColorInt;
4 5
 
5 6
 public class Colour extends Param<Integer>{
6 7
 
@@ -13,4 +14,8 @@ public class Colour extends Param<Integer>{
13 14
     public String toString() {
14 15
         return String.format("#%06X", (0xFFFFFF & get()));
15 16
     }
17
+
18
+    public boolean hasTransparency() {
19
+        return hasValue() && Color.alpha(value) < 1;
20
+    }
16 21
 }

+ 1
- 1
lib/android/app/src/main/java/com/reactnativenavigation/parse/params/Orientation.java 查看文件

@@ -1,7 +1,7 @@
1 1
 package com.reactnativenavigation.parse.params;
2 2
 
3 3
 import android.content.pm.ActivityInfo;
4
-import android.support.annotation.Nullable;
4
+import androidx.annotation.Nullable;
5 5
 
6 6
 public enum Orientation {
7 7
     Portrait("portrait", ActivityInfo.SCREEN_ORIENTATION_PORTRAIT),

+ 1
- 1
lib/android/app/src/main/java/com/reactnativenavigation/parse/params/Text.java 查看文件

@@ -1,6 +1,6 @@
1 1
 package com.reactnativenavigation.parse.params;
2 2
 
3
-import android.support.annotation.NonNull;
3
+import androidx.annotation.NonNull;
4 4
 
5 5
 public class Text extends Param<String> {
6 6
     public Text(String value) {

+ 1
- 1
lib/android/app/src/main/java/com/reactnativenavigation/parse/params/TitleDisplayMode.java 查看文件

@@ -1,6 +1,6 @@
1 1
 package com.reactnativenavigation.parse.params;
2 2
 
3
-import android.support.annotation.NonNull;
3
+import androidx.annotation.NonNull;
4 4
 
5 5
 import com.aurelhubert.ahbottomnavigation.AHBottomNavigation.TitleState;
6 6
 

+ 1
- 1
lib/android/app/src/main/java/com/reactnativenavigation/parse/parsers/LayoutNodeParser.java 查看文件

@@ -1,6 +1,6 @@
1 1
 package com.reactnativenavigation.parse.parsers;
2 2
 
3
-import android.support.annotation.NonNull;
3
+import androidx.annotation.NonNull;
4 4
 
5 5
 import com.reactnativenavigation.parse.LayoutNode;
6 6
 

+ 63
- 41
lib/android/app/src/main/java/com/reactnativenavigation/presentation/BottomTabPresenter.java 查看文件

@@ -2,7 +2,6 @@ package com.reactnativenavigation.presentation;
2 2
 
3 3
 import android.content.Context;
4 4
 import android.graphics.drawable.Drawable;
5
-import android.support.annotation.NonNull;
6 5
 
7 6
 import com.aurelhubert.ahbottomnavigation.notification.AHNotification;
8 7
 import com.reactnativenavigation.parse.BottomTabOptions;
@@ -10,13 +9,16 @@ import com.reactnativenavigation.parse.DotIndicatorOptions;
10 9
 import com.reactnativenavigation.parse.Options;
11 10
 import com.reactnativenavigation.utils.ImageLoader;
12 11
 import com.reactnativenavigation.utils.ImageLoadingListenerAdapter;
12
+import com.reactnativenavigation.utils.LateInit;
13 13
 import com.reactnativenavigation.viewcontrollers.ViewController;
14 14
 import com.reactnativenavigation.viewcontrollers.bottomtabs.BottomTabFinder;
15 15
 import com.reactnativenavigation.views.BottomTabs;
16
-import com.reactnativenavigation.views.Component;
17 16
 
18 17
 import java.util.List;
19 18
 
19
+import androidx.annotation.NonNull;
20
+
21
+import static com.reactnativenavigation.utils.CollectionUtils.*;
20 22
 import static com.reactnativenavigation.utils.UiUtils.dpToPx;
21 23
 
22 24
 public class BottomTabPresenter {
@@ -24,7 +26,7 @@ public class BottomTabPresenter {
24 26
     private ImageLoader imageLoader;
25 27
     private Options defaultOptions;
26 28
     private final BottomTabFinder bottomTabFinder;
27
-    private BottomTabs bottomTabs;
29
+    private LateInit<BottomTabs> bottomTabs = new LateInit<>();
28 30
     private final List<ViewController> tabs;
29 31
     private final int defaultDotIndicatorSize;
30 32
 
@@ -42,74 +44,94 @@ public class BottomTabPresenter {
42 44
     }
43 45
 
44 46
     public void bindView(BottomTabs bottomTabs) {
45
-        this.bottomTabs = bottomTabs;
47
+        this.bottomTabs.set(bottomTabs);
46 48
     }
47 49
 
48 50
     public void applyOptions() {
49
-        for (int i = 0; i < tabs.size(); i++) {
50
-            BottomTabOptions tab = tabs.get(i).resolveCurrentOptions(defaultOptions).bottomTabOptions;
51
-            bottomTabs.setTitleTypeface(i, tab.fontFamily);
52
-            bottomTabs.setIconActiveColor(i, tab.selectedIconColor.get(null));
53
-            bottomTabs.setIconInactiveColor(i, tab.iconColor.get(null));
54
-            bottomTabs.setTitleActiveColor(i, tab.selectedTextColor.get(null));
55
-            bottomTabs.setTitleInactiveColor(i, tab.textColor.get(null));
56
-            bottomTabs.setTitleInactiveTextSizeInSp(i, tab.fontSize.hasValue() ? Float.valueOf(tab.fontSize.get()) : null);
57
-            bottomTabs.setTitleActiveTextSizeInSp(i, tab.selectedFontSize.hasValue() ? Float.valueOf(tab.selectedFontSize.get()) : null);
58
-            if (tab.testId.hasValue()) bottomTabs.setTag(i, tab.testId.get());
59
-            if (shouldApplyDot(tab)) applyDotIndicator(i, tab.dotIndicator); else applyBadge(i, tab);
60
-        }
51
+        bottomTabs.perform(bottomTabs -> {
52
+            for (int i = 0; i < tabs.size(); i++) {
53
+                BottomTabOptions tab = tabs.get(i).resolveCurrentOptions(defaultOptions).bottomTabOptions;
54
+                bottomTabs.setTitleTypeface(i, tab.fontFamily);
55
+                bottomTabs.setIconActiveColor(i, tab.selectedIconColor.get(null));
56
+                bottomTabs.setIconInactiveColor(i, tab.iconColor.get(null));
57
+                bottomTabs.setTitleActiveColor(i, tab.selectedTextColor.get(null));
58
+                bottomTabs.setTitleInactiveColor(i, tab.textColor.get(null));
59
+                bottomTabs.setTitleInactiveTextSizeInSp(i, tab.fontSize.hasValue() ? Float.valueOf(tab.fontSize.get()) : null);
60
+                bottomTabs.setTitleActiveTextSizeInSp(i, tab.selectedFontSize.hasValue() ? Float.valueOf(tab.selectedFontSize.get()) : null);
61
+                if (tab.testId.hasValue()) bottomTabs.setTag(i, tab.testId.get());
62
+                if (shouldApplyDot(tab)) applyDotIndicator(i, tab.dotIndicator); else applyBadge(i, tab);
63
+            }
64
+        });
65
+    }
66
+
67
+    public void mergeOptions(Options options) {
68
+        bottomTabs.perform(bottomTabs -> {
69
+            bottomTabs.disableItemsCreation();
70
+            forEach(tabs, tab -> mergeChildOptions(options, tab));
71
+            bottomTabs.enableItemsCreation();
72
+        });
61 73
     }
62 74
 
63
-    public void mergeChildOptions(Options options, Component child) {
64
-        int index = bottomTabFinder.findByComponent(child);
65
-        if (index >= 0) {
66
-            BottomTabOptions tab = options.bottomTabOptions;
67
-            if (tab.fontFamily != null) bottomTabs.setTitleTypeface(index, tab.fontFamily);
68
-            if (tab.selectedIconColor.hasValue()) bottomTabs.setIconActiveColor(index, tab.selectedIconColor.get());
69
-            if (tab.iconColor.hasValue()) bottomTabs.setIconInactiveColor(index, tab.iconColor.get());
70
-            if (tab.selectedTextColor.hasValue()) bottomTabs.setTitleActiveColor(index, tab.selectedTextColor.get());
71
-            if (tab.textColor.hasValue()) bottomTabs.setTitleInactiveColor(index, tab.textColor.get());
72
-            if (tab.text.hasValue()) bottomTabs.setText(index, tab.text.get());
73
-            if (tab.icon.hasValue()) imageLoader.loadIcon(context, tab.icon.get(), new ImageLoadingListenerAdapter() {
74
-                @Override
75
-                public void onComplete(@NonNull Drawable drawable) {
76
-                    bottomTabs.setIcon(index, drawable);
77
-                }
78
-            });
79
-            if (tab.testId.hasValue()) bottomTabs.setTag(index, tab.testId.get());
80
-            if (shouldApplyDot(tab)) mergeDotIndicator(index, tab.dotIndicator); else mergeBadge(index, tab);
81
-        }
75
+    public void mergeChildOptions(Options options, ViewController child) {
76
+        bottomTabs.perform(bottomTabs -> {
77
+            int index = bottomTabFinder.findByControllerId(child.getId());
78
+            if (index >= 0) {
79
+                BottomTabOptions tab = options.bottomTabOptions;
80
+                if (tab.fontFamily != null) bottomTabs.setTitleTypeface(index, tab.fontFamily);
81
+                if (tab.selectedIconColor.hasValue()) bottomTabs.setIconActiveColor(index, tab.selectedIconColor.get());
82
+                if (tab.iconColor.hasValue()) bottomTabs.setIconInactiveColor(index, tab.iconColor.get());
83
+                if (tab.selectedTextColor.hasValue()) bottomTabs.setTitleActiveColor(index, tab.selectedTextColor.get());
84
+                if (tab.textColor.hasValue()) bottomTabs.setTitleInactiveColor(index, tab.textColor.get());
85
+                if (tab.text.hasValue()) bottomTabs.setText(index, tab.text.get());
86
+                if (tab.icon.hasValue()) imageLoader.loadIcon(context, tab.icon.get(), new ImageLoadingListenerAdapter() {
87
+                    @Override
88
+                    public void onComplete(@NonNull Drawable drawable) {
89
+                        bottomTabs.setIcon(index, drawable);
90
+                    }
91
+                });
92
+                if (tab.testId.hasValue()) bottomTabs.setTag(index, tab.testId.get());
93
+                if (shouldApplyDot(tab)) mergeDotIndicator(index, tab.dotIndicator); else mergeBadge(index, tab);
94
+            }
95
+        });
82 96
     }
83 97
 
84 98
     private void applyDotIndicator(int tabIndex, DotIndicatorOptions dotIndicator) {
85 99
         AHNotification.Builder builder = new AHNotification.Builder()
86 100
                 .setText("")
87 101
                 .setBackgroundColor(dotIndicator.color.get(null))
88
-                .setSize(dotIndicator.size.get(defaultDotIndicatorSize));
89
-        bottomTabs.setNotification(builder.build(), tabIndex);
102
+                .setSize(dotIndicator.size.get(defaultDotIndicatorSize))
103
+                .animate(dotIndicator.animate.get(false));
104
+        bottomTabs.perform(bottomTabs -> bottomTabs.setNotification(builder.build(), tabIndex));
90 105
     }
91 106
 
92 107
     private void applyBadge(int tabIndex, BottomTabOptions tab) {
108
+        if (bottomTabs == null) return;
93 109
         AHNotification.Builder builder = new AHNotification.Builder()
94 110
                 .setText(tab.badge.get(""))
95
-                .setBackgroundColor(tab.badgeColor.get(null));
96
-        bottomTabs.setNotification(builder.build(), tabIndex);
111
+                .setBackgroundColor(tab.badgeColor.get(null))
112
+                .animate(tab.animateBadge.get(false));
113
+        bottomTabs.perform(bottomTabs -> bottomTabs.setNotification(builder.build(), tabIndex));
97 114
     }
98 115
 
99 116
     private void mergeBadge(int index, BottomTabOptions tab) {
117
+        if (bottomTabs == null) return;
100 118
         if (!tab.badge.hasValue()) return;
101 119
         AHNotification.Builder builder = new AHNotification.Builder();
102 120
         if (tab.badge.hasValue()) builder.setText(tab.badge.get());
103 121
         if (tab.badgeColor.hasValue()) builder.setBackgroundColor(tab.badgeColor.get());
104
-        bottomTabs.setNotification(builder.build(), index);
122
+        if (tab.badgeColor.hasValue()) builder.setBackgroundColor(tab.badgeColor.get());
123
+        if (tab.animateBadge.hasValue()) builder.animate(tab.animateBadge.get());
124
+        bottomTabs.perform(bottomTabs -> bottomTabs.setNotification(builder.build(), index));
105 125
     }
106 126
 
107 127
     private void mergeDotIndicator(int index, DotIndicatorOptions dotIndicator) {
128
+        if (bottomTabs == null) return;
108 129
         AHNotification.Builder builder = new AHNotification.Builder();
109 130
         if (dotIndicator.color.hasValue()) builder.setBackgroundColor(dotIndicator.color.get());
110 131
         builder.setSize(dotIndicator.visible.isFalse() ? 0 : dotIndicator.size.get(defaultDotIndicatorSize));
132
+        if (dotIndicator.animate.hasValue()) builder.animate(dotIndicator.animate.get());
111 133
         AHNotification notification = builder.build();
112
-        if (notification.hasValue()) bottomTabs.setNotification(notification, index);
134
+        if (notification.hasValue()) bottomTabs.perform(bottomTabs -> bottomTabs.setNotification(notification, index));
113 135
     }
114 136
 
115 137
     private boolean shouldApplyDot(BottomTabOptions tab) {

+ 18
- 33
lib/android/app/src/main/java/com/reactnativenavigation/presentation/BottomTabsPresenter.java 查看文件

@@ -1,9 +1,7 @@
1 1
 package com.reactnativenavigation.presentation;
2 2
 
3 3
 import android.graphics.Color;
4
-import android.support.annotation.IntRange;
5 4
 import android.view.ViewGroup;
6
-import android.view.ViewGroup.MarginLayoutParams;
7 5
 
8 6
 import com.aurelhubert.ahbottomnavigation.AHBottomNavigation.TitleState;
9 7
 import com.reactnativenavigation.anim.BottomTabsAnimator;
@@ -14,11 +12,10 @@ import com.reactnativenavigation.viewcontrollers.ViewController;
14 12
 import com.reactnativenavigation.viewcontrollers.bottomtabs.BottomTabFinder;
15 13
 import com.reactnativenavigation.viewcontrollers.bottomtabs.TabSelector;
16 14
 import com.reactnativenavigation.views.BottomTabs;
17
-import com.reactnativenavigation.views.Component;
18 15
 
19 16
 import java.util.List;
20 17
 
21
-import static com.reactnativenavigation.utils.ViewUtils.getHeight;
18
+import androidx.annotation.IntRange;
22 19
 
23 20
 public class BottomTabsPresenter {
24 21
     private final BottomTabFinder bottomTabFinder;
@@ -44,11 +41,6 @@ public class BottomTabsPresenter {
44 41
         animator = new BottomTabsAnimator(bottomTabs);
45 42
     }
46 43
 
47
-    public void applyLayoutParamsOptions(Options options, int tabIndex) {
48
-        Options withDefaultOptions = options.copy().withDefaultOptions(defaultOptions);
49
-        applyDrawBehind(withDefaultOptions.bottomTabsOptions, tabIndex);
50
-    }
51
-
52 44
     public void mergeOptions(Options options) {
53 45
         mergeBottomTabsOptions(options);
54 46
     }
@@ -57,19 +49,18 @@ public class BottomTabsPresenter {
57 49
         applyBottomTabsOptions(options.copy().withDefaultOptions(defaultOptions));
58 50
     }
59 51
 
60
-    public void applyChildOptions(Options options, Component child) {
61
-        int tabIndex = bottomTabFinder.findByComponent(child);
52
+    public void applyChildOptions(Options options, ViewController child) {
53
+        int tabIndex = bottomTabFinder.findByControllerId(child.getId());
62 54
         if (tabIndex >= 0) {
63
-            Options withDefaultOptions = options.copy().withDefaultOptions(defaultOptions);
64
-            applyBottomTabsOptions(withDefaultOptions);
65
-            applyDrawBehind(withDefaultOptions.bottomTabsOptions, tabIndex);
55
+            applyBottomTabsOptions(options.copy().withDefaultOptions(defaultOptions));
56
+            applyDrawBehind(tabIndex);
66 57
         }
67 58
     }
68 59
 
69
-    public void mergeChildOptions(Options options, Component child) {
60
+    public void mergeChildOptions(Options options, ViewController child) {
70 61
         mergeBottomTabsOptions(options);
71
-        int tabIndex = bottomTabFinder.findByComponent(child);
72
-        if (tabIndex >= 0) mergeDrawBehind(options.bottomTabsOptions, tabIndex);
62
+        int tabIndex = bottomTabFinder.findByControllerId(child.getId());
63
+        if (tabIndex >= 0) mergeDrawBehind(tabIndex);
73 64
     }
74 65
 
75 66
     private void mergeBottomTabsOptions(Options options) {
@@ -110,24 +101,12 @@ public class BottomTabsPresenter {
110 101
         }
111 102
     }
112 103
 
113
-    private void applyDrawBehind(BottomTabsOptions options, @IntRange(from = 0) int tabIndex) {
114
-        ViewGroup tab = tabs.get(tabIndex).getView();
115
-        MarginLayoutParams lp = (MarginLayoutParams) tab.getLayoutParams();
116
-        if (options.drawBehind.isTrue()) {
117
-            lp.bottomMargin = 0;
118
-        } else if (options.visible.isTrueOrUndefined()) {
119
-            lp.bottomMargin = getHeight(bottomTabs);
120
-        }
104
+    private void applyDrawBehind(@IntRange(from = 0) int tabIndex) {
105
+        tabs.get(tabIndex).applyBottomInset();
121 106
     }
122 107
 
123
-    private void mergeDrawBehind(BottomTabsOptions options, int tabIndex) {
124
-        ViewGroup tab = tabs.get(tabIndex).getView();
125
-        MarginLayoutParams lp = (MarginLayoutParams) tab.getLayoutParams();
126
-        if (options.drawBehind.isTrue()) {
127
-            lp.bottomMargin = 0;
128
-        } else if (options.visible.isTrue() && options.drawBehind.isFalse()) {
129
-            lp.bottomMargin = getHeight(bottomTabs);
130
-        }
108
+    private void mergeDrawBehind(int tabIndex) {
109
+        tabs.get(tabIndex).applyBottomInset();
131 110
     }
132 111
 
133 112
     private void applyBottomTabsOptions(Options options) {
@@ -164,4 +143,10 @@ public class BottomTabsPresenter {
164 143
             bottomTabs.setUseElevation(true, bottomTabsOptions.elevation.get().floatValue());
165 144
         }
166 145
     }
146
+
147
+    public void applyBottomInset(int bottomInset) {
148
+        ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) bottomTabs.getLayoutParams();
149
+        lp.bottomMargin = bottomInset;
150
+        bottomTabs.requestLayout();
151
+    }
167 152
 }

+ 1
- 1
lib/android/app/src/main/java/com/reactnativenavigation/presentation/ComponentPresenter.java 查看文件

@@ -3,7 +3,7 @@ package com.reactnativenavigation.presentation;
3 3
 import com.reactnativenavigation.parse.Options;
4 4
 import com.reactnativenavigation.views.ComponentLayout;
5 5
 
6
-public class ComponentPresenter {
6
+public class ComponentPresenter extends ComponentPresenterBase {
7 7
     public Options defaultOptions;
8 8
 
9 9
     public ComponentPresenter(Options defaultOptions) {

+ 23
- 0
lib/android/app/src/main/java/com/reactnativenavigation/presentation/ComponentPresenterBase.java 查看文件

@@ -0,0 +1,23 @@
1
+package com.reactnativenavigation.presentation;
2
+
3
+import androidx.annotation.NonNull;
4
+import android.view.View;
5
+import android.view.ViewGroup.MarginLayoutParams;
6
+
7
+public class ComponentPresenterBase {
8
+    public void applyTopInsets(@NonNull View view, int topInsets) {
9
+        MarginLayoutParams lp = (MarginLayoutParams) view.getLayoutParams();
10
+        if (lp.topMargin != topInsets) {
11
+            lp.topMargin = topInsets;
12
+            view.requestLayout();
13
+        }
14
+    }
15
+
16
+    public void applyBottomInset(@NonNull View view, int bottomInset) {
17
+        MarginLayoutParams lp = (MarginLayoutParams) view.getLayoutParams();
18
+        if (lp.bottomMargin!= bottomInset) {
19
+            lp.bottomMargin = bottomInset;
20
+            view.requestLayout();
21
+        }
22
+    }
23
+}

+ 4
- 0
lib/android/app/src/main/java/com/reactnativenavigation/presentation/ExternalComponentPresenter.java 查看文件

@@ -0,0 +1,4 @@
1
+package com.reactnativenavigation.presentation;
2
+
3
+public class ExternalComponentPresenter extends ComponentPresenterBase {
4
+}

+ 1
- 1
lib/android/app/src/main/java/com/reactnativenavigation/presentation/FabPresenter.java 查看文件

@@ -1,7 +1,7 @@
1 1
 package com.reactnativenavigation.presentation;
2 2
 
3 3
 
4
-import android.support.annotation.NonNull;
4
+import androidx.annotation.NonNull;
5 5
 import android.view.Gravity;
6 6
 import android.view.View;
7 7
 import android.view.ViewGroup;

+ 4
- 1
lib/android/app/src/main/java/com/reactnativenavigation/presentation/OverlayManager.java 查看文件

@@ -4,16 +4,19 @@ import android.view.ViewGroup;
4 4
 
5 5
 import com.reactnativenavigation.utils.CommandListener;
6 6
 import com.reactnativenavigation.viewcontrollers.ViewController;
7
+import com.reactnativenavigation.views.BehaviourDelegate;
7 8
 
8 9
 import java.util.HashMap;
9 10
 
11
+import static com.reactnativenavigation.utils.CoordinatorLayoutUtils.matchParentWithBehaviour;
12
+
10 13
 public class OverlayManager {
11 14
     private final HashMap<String, ViewController> overlayRegistry = new HashMap<>();
12 15
 
13 16
     public void show(ViewGroup overlaysContainer, ViewController overlay, CommandListener listener) {
14 17
         overlayRegistry.put(overlay.getId(), overlay);
15 18
         overlay.addOnAppearedListener(() -> listener.onSuccess(overlay.getId()));
16
-        overlaysContainer.addView(overlay.getView());
19
+        overlaysContainer.addView(overlay.getView(), matchParentWithBehaviour(new BehaviourDelegate(overlay)));
17 20
     }
18 21
 
19 22
     public void dismiss(String componentId, CommandListener listener) {

+ 65
- 32
lib/android/app/src/main/java/com/reactnativenavigation/presentation/Presenter.java 查看文件

@@ -2,16 +2,25 @@ package com.reactnativenavigation.presentation;
2 2
 
3 3
 import android.app.Activity;
4 4
 import android.graphics.Color;
5
+import android.graphics.drawable.ColorDrawable;
6
+import android.graphics.drawable.Drawable;
7
+import android.graphics.drawable.LayerDrawable;
5 8
 import android.os.Build;
6 9
 import android.view.View;
7 10
 import android.view.ViewGroup.MarginLayoutParams;
11
+import android.view.Window;
8 12
 
9 13
 import com.reactnativenavigation.parse.Options;
10 14
 import com.reactnativenavigation.parse.OrientationOptions;
11 15
 import com.reactnativenavigation.parse.StatusBarOptions;
12 16
 import com.reactnativenavigation.parse.StatusBarOptions.TextColorScheme;
13 17
 import com.reactnativenavigation.parse.params.Bool;
14
-import com.reactnativenavigation.utils.UiUtils;
18
+import com.reactnativenavigation.utils.StatusBarUtils;
19
+import com.reactnativenavigation.viewcontrollers.ParentController;
20
+import com.reactnativenavigation.viewcontrollers.ViewController;
21
+import com.reactnativenavigation.viewcontrollers.navigator.Navigator;
22
+
23
+import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
15 24
 
16 25
 @SuppressWarnings("FieldCanBeLocal")
17 26
 public class Presenter {
@@ -32,32 +41,25 @@ public class Presenter {
32 41
         mergeStatusBarOptions(view, options.statusBar);
33 42
     }
34 43
 
35
-    public void applyOptions(View view, Options options) {
44
+    public void applyOptions(ViewController view, Options options) {
36 45
         Options withDefaultOptions = options.copy().withDefaultOptions(defaultOptions);
37 46
         applyOrientation(withDefaultOptions.layout.orientation);
38 47
         applyViewOptions(view, withDefaultOptions);
39
-        applyStatusBarOptions(view, withDefaultOptions.statusBar);
40
-    }
41
-
42
-    public void applyRootOptions(View view, Options options) {
43
-        Options withDefaultOptions = options.copy().withDefaultOptions(defaultOptions);
44
-        setDrawBehindStatusBar(view, withDefaultOptions.statusBar);
48
+        applyStatusBarOptions(withDefaultOptions);
45 49
     }
46 50
 
47
-    public void onViewBroughtToFront(View view, Options options) {
51
+    public void onViewBroughtToFront(Options options) {
48 52
         Options withDefaultOptions = options.copy().withDefaultOptions(defaultOptions);
49
-        applyStatusBarOptions(view, withDefaultOptions.statusBar);
53
+        applyStatusBarOptions(withDefaultOptions);
50 54
     }
51 55
 
52 56
     private void applyOrientation(OrientationOptions options) {
53 57
         activity.setRequestedOrientation(options.getValue());
54 58
     }
55 59
 
56
-    private void applyViewOptions(View view, Options options) {
57
-        if (options.layout.backgroundColor.hasValue()) {
58
-            view.setBackgroundColor(options.layout.backgroundColor.get());
59
-        }
60
-        applyTopMargin(view, options);
60
+    private void applyViewOptions(ViewController view, Options options) {
61
+        applyBackgroundColor(view, options);
62
+        applyTopMargin(view.getView(), options);
61 63
     }
62 64
 
63 65
     private void applyTopMargin(View view, Options options) {
@@ -66,23 +68,52 @@ public class Presenter {
66 68
         }
67 69
     }
68 70
 
69
-    private void applyStatusBarOptions(View view, StatusBarOptions statusBar) {
70
-        setStatusBarBackgroundColor(statusBar);
71
-        setTextColorScheme(statusBar.textColorScheme);
72
-        setStatusBarVisible(view, statusBar.visible, statusBar.drawBehind);
71
+    private void applyBackgroundColor(ViewController view, Options options) {
72
+        if (options.layout.backgroundColor.hasValue()) {
73
+            if (view instanceof Navigator) return;
74
+
75
+            LayerDrawable ld = new LayerDrawable(new Drawable[]{new ColorDrawable(options.layout.backgroundColor.get())});
76
+            int top = view.resolveCurrentOptions().statusBar.drawBehind.isTrue() ? 0 : StatusBarUtils.getStatusBarHeight(view.getActivity());
77
+            if (!(view instanceof ParentController)) {
78
+                MarginLayoutParams lp = (MarginLayoutParams) view.getView().getLayoutParams();
79
+                if (lp.topMargin != 0) top = 0;
80
+            }
81
+            ld.setLayerInset(0, 0, top, 0, 0);
82
+            view.getView().setBackground(ld);
83
+        }
73 84
     }
74 85
 
75
-    private void setStatusBarVisible(View view, Bool visible, Bool drawBehind) {
86
+    private void applyStatusBarOptions(Options options) {
87
+        setStatusBarBackgroundColor(options.statusBar);
88
+        setTextColorScheme(options.statusBar.textColorScheme);
89
+        setTranslucent(options.statusBar);
90
+        setStatusBarVisible(options.statusBar.visible);
91
+    }
92
+
93
+    private void setTranslucent(StatusBarOptions options) {
94
+        Window window = activity.getWindow();
95
+        if (options.translucent.isTrue()) {
96
+            window.setFlags(FLAG_TRANSLUCENT_STATUS, FLAG_TRANSLUCENT_STATUS);
97
+        } else {
98
+            window.clearFlags(FLAG_TRANSLUCENT_STATUS);
99
+        }
100
+    }
101
+
102
+    private void setStatusBarVisible(Bool visible) {
103
+        View decorView = activity.getWindow().getDecorView();
104
+        int flags = decorView.getSystemUiVisibility();
76 105
         if (visible.isFalse()) {
77
-            view.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_FULLSCREEN);
78
-        } else if (drawBehind.isTrue()) {
79
-            view.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
106
+            flags |= View.SYSTEM_UI_FLAG_FULLSCREEN;
107
+        } else {
108
+            flags &= ~View.SYSTEM_UI_FLAG_FULLSCREEN;
80 109
         }
110
+        decorView.setSystemUiVisibility(flags);
81 111
     }
82 112
 
83 113
     private void setStatusBarBackgroundColor(StatusBarOptions statusBar) {
84 114
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
85
-            activity.getWindow().setStatusBarColor(statusBar.backgroundColor.get(Color.BLACK));
115
+            int defaultColor = statusBar.visible.isTrueOrUndefined() ? Color.BLACK : Color.TRANSPARENT;
116
+            activity.getWindow().setStatusBarColor(statusBar.backgroundColor.get(defaultColor));
86 117
         }
87 118
     }
88 119
 
@@ -105,17 +136,10 @@ public class Presenter {
105 136
         view.setSystemUiVisibility(flags);
106 137
     }
107 138
 
108
-    private void setDrawBehindStatusBar(View view, StatusBarOptions statusBar) {
109
-        if (statusBar.visible.isFalse()) {
110
-            ((MarginLayoutParams) view.getLayoutParams()).topMargin = statusBar.drawBehind.isTrue() ?
111
-                    0 : UiUtils.getStatusBarHeight(activity);
112
-        }
113
-    }
114
-
115
-
116 139
     private void mergeStatusBarOptions(View view, StatusBarOptions statusBar) {
117 140
         mergeStatusBarBackgroundColor(statusBar);
118 141
         mergeTextColorScheme(statusBar.textColorScheme);
142
+        mergeTranslucent(statusBar);
119 143
         mergeStatusBarVisible(view, statusBar.visible, statusBar.drawBehind);
120 144
     }
121 145
 
@@ -137,6 +161,15 @@ public class Presenter {
137 161
         }
138 162
     }
139 163
 
164
+    private void mergeTranslucent(StatusBarOptions options) {
165
+        Window window = activity.getWindow();
166
+        if (options.translucent.isTrue()) {
167
+            window.setFlags(FLAG_TRANSLUCENT_STATUS, FLAG_TRANSLUCENT_STATUS);
168
+        } else if (options.translucent.isFalse()) {
169
+            window.clearFlags(FLAG_TRANSLUCENT_STATUS);
170
+        }
171
+    }
172
+
140 173
     private void mergeStatusBarVisible(View view, Bool visible, Bool drawBehind) {
141 174
         if (visible.hasValue()) {
142 175
             int flags = view.getSystemUiVisibility();

+ 10
- 4
lib/android/app/src/main/java/com/reactnativenavigation/presentation/RootPresenter.java 查看文件

@@ -1,21 +1,26 @@
1 1
 package com.reactnativenavigation.presentation;
2 2
 
3 3
 import android.content.Context;
4
-import android.widget.FrameLayout;
5 4
 
6 5
 import com.facebook.react.ReactInstanceManager;
7 6
 import com.reactnativenavigation.anim.NavigationAnimator;
8 7
 import com.reactnativenavigation.parse.Options;
9 8
 import com.reactnativenavigation.utils.CommandListener;
10 9
 import com.reactnativenavigation.viewcontrollers.ViewController;
10
+import com.reactnativenavigation.views.BehaviourDelegate;
11 11
 import com.reactnativenavigation.views.element.ElementTransitionManager;
12 12
 
13
+import androidx.annotation.VisibleForTesting;
14
+import androidx.coordinatorlayout.widget.CoordinatorLayout;
15
+
16
+import static com.reactnativenavigation.utils.CoordinatorLayoutUtils.matchParentWithBehaviour;
17
+
13 18
 public class RootPresenter {
14 19
     private NavigationAnimator animator;
20
+    private CoordinatorLayout rootLayout;
15 21
     private LayoutDirectionApplier layoutDirectionApplier;
16
-    private FrameLayout rootLayout;
17 22
 
18
-    public void setRootContainer(FrameLayout rootLayout) {
23
+    public void setRootContainer(CoordinatorLayout rootLayout) {
19 24
         this.rootLayout = rootLayout;
20 25
     }
21 26
 
@@ -23,6 +28,7 @@ public class RootPresenter {
23 28
         this(new NavigationAnimator(context, new ElementTransitionManager()), new LayoutDirectionApplier());
24 29
     }
25 30
 
31
+    @VisibleForTesting
26 32
     public RootPresenter(NavigationAnimator animator, LayoutDirectionApplier layoutDirectionApplier) {
27 33
         this.animator = animator;
28 34
         this.layoutDirectionApplier = layoutDirectionApplier;
@@ -30,7 +36,7 @@ public class RootPresenter {
30 36
 
31 37
     public void setRoot(ViewController root, Options defaultOptions, CommandListener listener, ReactInstanceManager reactInstanceManager) {
32 38
         layoutDirectionApplier.apply(root, defaultOptions, reactInstanceManager);
33
-        rootLayout.addView(root.getView());
39
+        rootLayout.addView(root.getView(), matchParentWithBehaviour(new BehaviourDelegate(root)));
34 40
         Options options = root.resolveCurrentOptions(defaultOptions);
35 41
         root.setWaitForRender(options.animations.setRoot.waitForRender);
36 42
         if (options.animations.setRoot.waitForRender.isTrue()) {

+ 11
- 3
lib/android/app/src/main/java/com/reactnativenavigation/presentation/SideMenuPresenter.java 查看文件

@@ -1,16 +1,19 @@
1 1
 package com.reactnativenavigation.presentation;
2 2
 
3
-import android.support.v4.widget.DrawerLayout;
4 3
 import android.view.Gravity;
5 4
 
6 5
 import com.reactnativenavigation.parse.Options;
7 6
 import com.reactnativenavigation.parse.SideMenuRootOptions;
7
+import com.reactnativenavigation.views.SideMenu;
8
+
9
+import androidx.annotation.RestrictTo;
10
+import androidx.drawerlayout.widget.DrawerLayout;
8 11
 
9 12
 public class SideMenuPresenter {
10 13
 
11
-    private DrawerLayout sideMenu;
14
+    private SideMenu sideMenu;
12 15
 
13
-    public void bindView(DrawerLayout sideMenu) {
16
+    public void bindView(SideMenu sideMenu) {
14 17
         this.sideMenu = sideMenu;
15 18
     }
16 19
 
@@ -76,4 +79,9 @@ public class SideMenuPresenter {
76 79
             sideMenu.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED, Gravity.RIGHT);
77 80
         }
78 81
     }
82
+
83
+    @RestrictTo(RestrictTo.Scope.TESTS)
84
+    public SideMenu getSideMenu() {
85
+        return sideMenu;
86
+    }
79 87
 }

+ 93
- 83
lib/android/app/src/main/java/com/reactnativenavigation/presentation/StackPresenter.java 查看文件

@@ -2,9 +2,6 @@ package com.reactnativenavigation.presentation;
2 2
 
3 3
 import android.app.Activity;
4 4
 import android.graphics.Color;
5
-import android.support.annotation.Nullable;
6
-import android.support.annotation.RestrictTo;
7
-import android.support.v7.widget.Toolbar;
8 5
 import android.view.Gravity;
9 6
 import android.view.View;
10 7
 import android.view.ViewGroup.LayoutParams;
@@ -26,6 +23,7 @@ import com.reactnativenavigation.utils.ButtonPresenter;
26 23
 import com.reactnativenavigation.utils.CollectionUtils;
27 24
 import com.reactnativenavigation.utils.ImageLoader;
28 25
 import com.reactnativenavigation.utils.ObjectUtils;
26
+import com.reactnativenavigation.utils.StatusBarUtils;
29 27
 import com.reactnativenavigation.utils.UiUtils;
30 28
 import com.reactnativenavigation.viewcontrollers.IReactView;
31 29
 import com.reactnativenavigation.viewcontrollers.ReactViewCreator;
@@ -33,8 +31,9 @@ import com.reactnativenavigation.viewcontrollers.TitleBarButtonController;
33 31
 import com.reactnativenavigation.viewcontrollers.TitleBarReactViewController;
34 32
 import com.reactnativenavigation.viewcontrollers.ViewController;
35 33
 import com.reactnativenavigation.viewcontrollers.button.NavigationIconResolver;
34
+import com.reactnativenavigation.viewcontrollers.stack.StackController;
36 35
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarBackgroundViewController;
37
-import com.reactnativenavigation.views.Component;
36
+import com.reactnativenavigation.viewcontrollers.topbar.TopBarController;
38 37
 import com.reactnativenavigation.views.titlebar.TitleBarReactViewCreator;
39 38
 import com.reactnativenavigation.views.topbar.TopBar;
40 39
 import com.reactnativenavigation.views.topbar.TopBarBackgroundViewCreator;
@@ -46,6 +45,10 @@ import java.util.LinkedHashMap;
46 45
 import java.util.List;
47 46
 import java.util.Map;
48 47
 
48
+import androidx.annotation.Nullable;
49
+import androidx.annotation.RestrictTo;
50
+import androidx.appcompat.widget.Toolbar;
51
+
49 52
 import static com.reactnativenavigation.utils.CollectionUtils.*;
50 53
 import static com.reactnativenavigation.utils.ObjectUtils.perform;
51 54
 
@@ -59,6 +62,7 @@ public class StackPresenter {
59 62
     private final Activity activity;
60 63
 
61 64
     private TopBar topBar;
65
+    private TopBarController topBarController;
62 66
     private final TitleBarReactViewCreator titleViewCreator;
63 67
     private TitleBarButtonController.OnClickListener onClickListener;
64 68
     private final ImageLoader imageLoader;
@@ -66,11 +70,12 @@ public class StackPresenter {
66 70
     private final TopBarBackgroundViewCreator topBarBackgroundViewCreator;
67 71
     private final ReactViewCreator buttonCreator;
68 72
     private Options defaultOptions;
69
-    private Map<Component, TitleBarReactViewController> titleControllers = new HashMap();
70
-    private Map<Component, TopBarBackgroundViewController> backgroundControllers = new HashMap();
71
-    private Map<Component, Map<String, TitleBarButtonController>> componentRightButtons = new HashMap();
72
-    private Map<Component, Map<String, TitleBarButtonController>> componentLeftButtons = new HashMap();
73
+
73 74
     private List<TitleBarButtonController> currentRightButtons = new ArrayList<>();
75
+    private Map<View, TitleBarReactViewController> titleControllers = new HashMap();
76
+    private Map<View, TopBarBackgroundViewController> backgroundControllers = new HashMap();
77
+    private Map<View, Map<String, TitleBarButtonController>> componentRightButtons = new HashMap();
78
+    private Map<View, Map<String, TitleBarButtonController>> componentLeftButtons = new HashMap();
74 79
 
75 80
     public StackPresenter(Activity activity,
76 81
                           TitleBarReactViewCreator titleViewCreator,
@@ -102,32 +107,22 @@ public class StackPresenter {
102 107
         return defaultOptions;
103 108
     }
104 109
 
105
-    public void bindView(TopBar topBar) {
106
-        this.topBar = topBar;
110
+    public void bindView(TopBarController topBarController) {
111
+        this.topBarController = topBarController;
112
+        topBar = topBarController.getView();
107 113
     }
108 114
 
109
-    public boolean isRendered(Component component) {
115
+    public boolean isRendered(View component) {
110 116
         ArrayList<ViewController> controllers = new ArrayList<>(perform(componentRightButtons.get(component), new ArrayList<>(), Map::values));
111 117
         controllers.add(backgroundControllers.get(component));
112 118
         controllers.add(titleControllers.get(component));
113 119
         return renderChecker.areRendered(filter(controllers, ObjectUtils::notNull));
114 120
     }
115 121
 
116
-    public void applyLayoutParamsOptions(Options options, View view) {
117
-        Options withDefault = options.copy().withDefaultOptions(defaultOptions);
118
-        if (view instanceof Component) {
119
-            if (withDefault.topBar.drawBehind.isTrue() && !withDefault.layout.topMargin.hasValue()) {
120
-                ((Component) view).drawBehindTopBar();
121
-            } else if (options.topBar.drawBehind.isFalseOrUndefined()) {
122
-                ((Component) view).drawBelowTopBar(topBar);
123
-            }
124
-        }
125
-    }
126
-
127
-    public void mergeOptions(Options options, Component currentChild) {
122
+    public void mergeOptions(Options options, StackController stack, ViewController currentChild) {
128 123
         mergeOrientation(options.layout.orientation);
129 124
 //        mergeButtons(topBar, withDefault.topBar.buttons, child);
130
-        mergeTopBarOptions(options, currentChild);
125
+        mergeTopBarOptions(options, stack, currentChild);
131 126
         mergeTopTabsOptions(options.topTabs);
132 127
         mergeTopTabOptions(options.topTabOptions);
133 128
     }
@@ -137,11 +132,11 @@ public class StackPresenter {
137 132
         setInitialTopBarVisibility(withDefault.topBar);
138 133
     }
139 134
 
140
-    public void applyChildOptions(Options options, Component child) {
135
+    public void applyChildOptions(Options options, StackController stack, ViewController child) {
141 136
         Options withDefault = options.copy().withDefaultOptions(defaultOptions);
142 137
         applyOrientation(withDefault.layout.orientation);
143 138
         applyButtons(withDefault.topBar, child);
144
-        applyTopBarOptions(withDefault, child, options);
139
+        applyTopBarOptions(withDefault, stack, child, options);
145 140
         applyTopTabsOptions(withDefault.topTabs);
146 141
         applyTopTabOptions(withDefault.topTabOptions);
147 142
     }
@@ -151,23 +146,25 @@ public class StackPresenter {
151 146
         ((Activity) topBar.getContext()).setRequestedOrientation(withDefaultOptions.getValue());
152 147
     }
153 148
 
154
-    public void onChildDestroyed(Component child) {
155
-        perform(titleControllers.remove(child), TitleBarReactViewController::destroy);
156
-        perform(backgroundControllers.remove(child), TopBarBackgroundViewController::destroy);
157
-        destroyButtons(componentRightButtons.get(child));
158
-        destroyButtons(componentLeftButtons.get(child));
159
-        componentRightButtons.remove(child);
160
-        componentLeftButtons.remove(child);
149
+    public void onChildDestroyed(ViewController child) {
150
+        perform(titleControllers.remove(child.getView()), TitleBarReactViewController::destroy);
151
+        perform(backgroundControllers.remove(child.getView()), TopBarBackgroundViewController::destroy);
152
+        destroyButtons(componentRightButtons.get(child.getView()));
153
+        destroyButtons(componentLeftButtons.get(child.getView()));
154
+        componentRightButtons.remove(child.getView());
155
+        componentLeftButtons.remove(child.getView());
161 156
     }
162 157
 
163 158
     private void destroyButtons(@Nullable Map<String, TitleBarButtonController> buttons) {
164 159
         if (buttons != null) forEach(buttons.values(), ViewController::destroy);
165 160
     }
166 161
 
167
-    private void applyTopBarOptions(Options options, Component component, Options componentOptions) {
162
+    private void applyTopBarOptions(Options options, StackController stack, ViewController child, Options componentOptions) {
163
+        final View component = child.getView();
168 164
         TopBarOptions topBarOptions = options.topBar;
169 165
         AnimationsOptions animationOptions = options.animations;
170 166
 
167
+        topBar.setTestId(topBarOptions.testId.get(""));
171 168
         topBar.setLayoutDirection(options.layout.direction);
172 169
         topBar.setHeight(topBarOptions.height.get(UiUtils.getTopBarHeightDp(activity)));
173 170
         topBar.setElevation(topBarOptions.elevation.get(DEFAULT_ELEVATION));
@@ -224,13 +221,7 @@ public class StackPresenter {
224 221
             topBar.clearBackgroundComponent();
225 222
         }
226 223
 
227
-        if (topBarOptions.testId.hasValue()) topBar.setTestId(topBarOptions.testId.get());
228
-        applyTopBarVisibility(topBarOptions, animationOptions, componentOptions);
229
-        if (topBarOptions.drawBehind.isTrue() && !componentOptions.layout.topMargin.hasValue()) {
230
-            component.drawBehindTopBar();
231
-        } else if (topBarOptions.drawBehind.isFalseOrUndefined()) {
232
-            component.drawBelowTopBar(topBar);
233
-        }
224
+        applyTopBarVisibility(topBarOptions, animationOptions, componentOptions, stack, child);
234 225
         if (topBarOptions.hideOnScroll.isTrue()) {
235 226
             if (component instanceof IReactView) {
236 227
                 topBar.enableCollapse(((IReactView) component).getScrollEventListener());
@@ -253,37 +244,38 @@ public class StackPresenter {
253 244
 
254 245
     private void setInitialTopBarVisibility(TopBarOptions options) {
255 246
         if (options.visible.isFalse()) {
256
-            topBar.hide();
247
+            topBarController.hide();
257 248
         }
258 249
         if (options.visible.isTrueOrUndefined()) {
259
-            topBar.show();
250
+            topBarController.show();
260 251
         }
261 252
     }
262 253
 
263
-    private void applyTopBarVisibility(TopBarOptions options, AnimationsOptions animationOptions, Options componentOptions) {
254
+    private void applyTopBarVisibility(TopBarOptions options, AnimationsOptions animationOptions, Options componentOptions, StackController stack, ViewController child) {
264 255
         if (options.visible.isFalse()) {
256
+            topBarController.resetViewProperties();
265 257
             if (options.animate.isTrueOrUndefined() && componentOptions.animations.push.enabled.isTrueOrUndefined()) {
266
-                topBar.hideAnimate(animationOptions.pop.topBar);
258
+                topBarController.hideAnimate(animationOptions.pop.topBar, 0, getTopBarTranslationAnimationDelta(stack, child));
267 259
             } else {
268
-                topBar.hide();
260
+                topBarController.hide();
269 261
             }
270
-        }
271
-        if (options.visible.isTrueOrUndefined()) {
262
+        } else if (options.visible.isTrueOrUndefined()) {
263
+            topBarController.resetViewProperties();
272 264
             if (options.animate.isTrueOrUndefined() && componentOptions.animations.push.enabled.isTrueOrUndefined()) {
273
-                topBar.showAnimate(animationOptions.push.topBar);
265
+                topBarController.showAnimate(animationOptions.push.topBar, getTopBarTranslationAnimationDelta(stack, child));
274 266
             } else {
275
-                topBar.show();
267
+                topBarController.show();
276 268
             }
277 269
         }
278 270
     }
279 271
 
280
-    private void applyButtons(TopBarOptions options, Component child) {
272
+    private void applyButtons(TopBarOptions options, ViewController child) {
281 273
         List<Button> rightButtons = mergeButtonsWithColor(options.buttons.right, options.rightButtonColor, options.rightButtonDisabledColor);
282 274
         List<Button> leftButtons = mergeButtonsWithColor(options.buttons.left, options.leftButtonColor, options.leftButtonDisabledColor);
283 275
 
284 276
         if (rightButtons != null) {
285
-            List<TitleBarButtonController> rightButtonControllers = getOrCreateButtonControllers(componentRightButtons.get(child), rightButtons);
286
-            componentRightButtons.put(child, keyBy(rightButtonControllers, TitleBarButtonController::getButtonInstanceId));
277
+            List<TitleBarButtonController> rightButtonControllers = getOrCreateButtonControllers(componentRightButtons.get(child.getView()), rightButtons);
278
+            componentRightButtons.put(child.getView(), keyBy(rightButtonControllers, TitleBarButtonController::getButtonInstanceId));
287 279
             if (!CollectionUtils.equals(currentRightButtons, rightButtonControllers)) {
288 280
                 currentRightButtons = rightButtonControllers;
289 281
                 topBar.setRightButtons(rightButtonControllers);
@@ -294,8 +286,8 @@ public class StackPresenter {
294 286
         }
295 287
 
296 288
         if (leftButtons != null) {
297
-            List<TitleBarButtonController> leftButtonControllers = getOrCreateButtonControllers(componentLeftButtons.get(child), leftButtons);
298
-            componentLeftButtons.put(child, keyBy(leftButtonControllers, TitleBarButtonController::getButtonInstanceId));
289
+            List<TitleBarButtonController> leftButtonControllers = getOrCreateButtonControllers(componentLeftButtons.get(child.getView()), leftButtons);
290
+            componentLeftButtons.put(child.getView(), keyBy(leftButtonControllers, TitleBarButtonController::getButtonInstanceId));
299 291
             topBar.setLeftButtons(leftButtonControllers);
300 292
         } else {
301 293
             topBar.clearLeftButtons();
@@ -341,21 +333,21 @@ public class StackPresenter {
341 333
         if (topTabOptions.fontFamily != null) topBar.setTopTabFontFamily(topTabOptions.tabIndex, topTabOptions.fontFamily);
342 334
     }
343 335
 
344
-    public void onChildWillAppear(Options appearing, Options disappearing) {
345
-        if (disappearing.topBar.visible.isTrueOrUndefined() && appearing.topBar.visible.isFalse()) {
346
-            if (disappearing.topBar.animate.isTrueOrUndefined() && disappearing.animations.pop.enabled.isTrueOrUndefined()) {
347
-                topBar.hideAnimate(disappearing.animations.pop.topBar);
336
+    public void onChildWillAppear(StackController parent, ViewController appearing, ViewController disappearing) {
337
+        if (disappearing.options.topBar.visible.isTrueOrUndefined() && appearing.options.topBar.visible.isFalse()) {
338
+            if (disappearing.options.topBar.animate.isTrueOrUndefined() && disappearing.options.animations.pop.enabled.isTrueOrUndefined()) {
339
+                topBarController.hideAnimate(disappearing.options.animations.pop.topBar, 0, getTopBarTranslationAnimationDelta(parent, appearing));
348 340
             } else {
349
-                topBar.hide();
341
+                topBarController.hide();
350 342
             }
351 343
         }
352 344
     }
353 345
 
354
-    public void mergeChildOptions(Options toMerge, Options resolvedOptions, Component child) {
346
+    public void mergeChildOptions(Options toMerge, Options resolvedOptions, StackController stack, ViewController child) {
355 347
         TopBarOptions topBar = toMerge.copy().mergeWith(resolvedOptions).withDefaultOptions(defaultOptions).topBar;
356 348
         mergeOrientation(toMerge.layout.orientation);
357
-        mergeButtons(topBar, toMerge.topBar.buttons, child);
358
-        mergeTopBarOptions(toMerge, child);
349
+        mergeButtons(topBar, toMerge.topBar.buttons, child.getView());
350
+        mergeTopBarOptions(toMerge, stack, child);
359 351
         mergeTopTabsOptions(toMerge.topTabs);
360 352
         mergeTopTabOptions(toMerge.topTabOptions);
361 353
     }
@@ -364,7 +356,7 @@ public class StackPresenter {
364 356
         if (orientationOptions.hasValue()) applyOrientation(orientationOptions);
365 357
     }
366 358
 
367
-    private void mergeButtons(TopBarOptions options, TopBarButtons buttons, Component child) {
359
+    private void mergeButtons(TopBarOptions options, TopBarButtons buttons, View child) {
368 360
         List<Button> rightButtons = mergeButtonsWithColor(buttons.right, options.rightButtonColor, options.rightButtonDisabledColor);
369 361
         List<Button> leftButtons = mergeButtonsWithColor(buttons.left, options.leftButtonColor, options.leftButtonDisabledColor);
370 362
 
@@ -407,10 +399,10 @@ public class StackPresenter {
407 399
         return result;
408 400
     }
409 401
 
410
-    private void mergeTopBarOptions(Options options, Component component) {
402
+    private void mergeTopBarOptions(Options options, StackController stack, ViewController child) {
403
+        AnimationsOptions animationsOptions = options.copy().withDefaultOptions(defaultOptions).animations;
411 404
         TopBarOptions topBarOptions = options.topBar;
412
-        AnimationsOptions animationsOptions = options.animations;
413
-
405
+        final View component = child.getView();
414 406
         if (options.layout.direction.hasValue()) topBar.setLayoutDirection(options.layout.direction);
415 407
         if (topBarOptions.height.hasValue()) topBar.setHeight(topBarOptions.height.get());
416 408
         if (topBarOptions.elevation.hasValue()) topBar.setElevation(topBarOptions.elevation.get());
@@ -459,26 +451,21 @@ public class StackPresenter {
459 451
 
460 452
         if (topBarOptions.testId.hasValue()) topBar.setTestId(topBarOptions.testId.get());
461 453
 
454
+        topBarController.resetViewProperties();
462 455
         if (topBarOptions.visible.isFalse()) {
463 456
             if (topBarOptions.animate.isTrueOrUndefined()) {
464
-                topBar.hideAnimate(animationsOptions.pop.topBar);
457
+                topBarController.hideAnimate(animationsOptions.pop.topBar, 0, getTopBarTranslationAnimationDelta(stack, child));
465 458
             } else {
466
-                topBar.hide();
459
+                topBarController.hide();
467 460
             }
468 461
         }
469 462
         if (topBarOptions.visible.isTrue()) {
470 463
             if (topBarOptions.animate.isTrueOrUndefined()) {
471
-                topBar.showAnimate(animationsOptions.push.topBar);
464
+                topBarController.showAnimate(animationsOptions.push.topBar, getTopBarTranslationAnimationDelta(stack, child));
472 465
             } else {
473
-                topBar.show();
466
+                topBarController.show();
474 467
             }
475 468
         }
476
-        if (topBarOptions.drawBehind.isTrue()) {
477
-            component.drawBehindTopBar();
478
-        }
479
-        if (topBarOptions.drawBehind.isFalse()) {
480
-            component.drawBelowTopBar(topBar);
481
-        }
482 469
         if (topBarOptions.hideOnScroll.isTrue() && component instanceof IReactView) {
483 470
             topBar.enableCollapse(((IReactView) component).getScrollEventListener());
484 471
         }
@@ -503,30 +490,53 @@ public class StackPresenter {
503 490
     }
504 491
 
505 492
     @RestrictTo(RestrictTo.Scope.TESTS)
506
-    public Map<Component, TitleBarReactViewController> getTitleComponents() {
493
+    public Map<View, TitleBarReactViewController> getTitleComponents() {
507 494
         return titleControllers;
508 495
     }
509 496
 
510 497
     @RestrictTo(RestrictTo.Scope.TESTS)
511
-    public Map<Component, TopBarBackgroundViewController> getBackgroundComponents() {
498
+    public Map<View, TopBarBackgroundViewController> getBackgroundComponents() {
512 499
         return backgroundControllers;
513 500
     }
514 501
 
515 502
     @RestrictTo(RestrictTo.Scope.TESTS)
516
-    public List<TitleBarButtonController> getComponentButtons(Component child) {
503
+    public List<TitleBarButtonController> getComponentButtons(View child) {
517 504
         return merge(getRightButtons(child), getLeftButtons(child), Collections.EMPTY_LIST);
518 505
     }
519 506
 
520 507
     @RestrictTo(RestrictTo.Scope.TESTS)
521
-    public List<TitleBarButtonController> getComponentButtons(Component child, List<TitleBarButtonController> defaultValue) {
508
+    public List<TitleBarButtonController> getComponentButtons(View child, List<TitleBarButtonController> defaultValue) {
522 509
         return merge(getRightButtons(child), getLeftButtons(child), defaultValue);
523 510
     }
524 511
 
525
-    private List<TitleBarButtonController> getRightButtons(Component child) {
512
+    public void applyTopInsets(StackController stack, ViewController child) {
513
+        if (stack.isCurrentChild(child)) applyStatusBarInsets(stack, child);
514
+        child.applyTopInset();
515
+    }
516
+
517
+    private List<TitleBarButtonController> getRightButtons(View child) {
526 518
         return componentRightButtons.containsKey(child) ? new ArrayList<>(componentRightButtons.get(child).values()) : null;
527 519
     }
528 520
 
529
-    private List<TitleBarButtonController> getLeftButtons(Component child) {
521
+    private List<TitleBarButtonController> getLeftButtons(View child) {
530 522
         return componentLeftButtons.containsKey(child) ? new ArrayList<>(componentLeftButtons.get(child).values()) : null;
531 523
     }
524
+
525
+    private void applyStatusBarInsets(StackController stack, ViewController child) {
526
+        MarginLayoutParams lp = (MarginLayoutParams) topBar.getLayoutParams();
527
+        lp.topMargin = getTopBarTopMargin(stack, child);
528
+        topBar.requestLayout();
529
+    }
530
+
531
+    private int getTopBarTranslationAnimationDelta(StackController stack, ViewController child) {
532
+        Options options = stack.resolveChildOptions(child).withDefaultOptions(defaultOptions);
533
+        return options.statusBar.hasTransparency() ? getTopBarTopMargin(stack, child) : 0;
534
+    }
535
+
536
+    private int getTopBarTopMargin(StackController stack, ViewController child) {
537
+        Options withDefault = stack.resolveChildOptions(child).withDefaultOptions(defaultOptions);
538
+        int topMargin = UiUtils.dpToPx(activity, withDefault.topBar.topMargin.get(0));
539
+        int statusBarInset = withDefault.statusBar.visible.isTrueOrUndefined() ? StatusBarUtils.getStatusBarHeight(child.getActivity()) : 0;
540
+        return topMargin + statusBarInset;
541
+    }
532 542
 }

+ 8
- 5
lib/android/app/src/main/java/com/reactnativenavigation/react/NavigationModule.java 查看文件

@@ -1,8 +1,5 @@
1 1
 package com.reactnativenavigation.react;
2 2
 
3
-import android.support.annotation.NonNull;
4
-import android.support.annotation.Nullable;
5
-
6 3
 import com.facebook.react.ReactInstanceManager;
7 4
 import com.facebook.react.bridge.Arguments;
8 5
 import com.facebook.react.bridge.Promise;
@@ -21,6 +18,7 @@ import com.reactnativenavigation.parse.parsers.JSONParser;
21 18
 import com.reactnativenavigation.parse.parsers.LayoutNodeParser;
22 19
 import com.reactnativenavigation.utils.NativeCommandListener;
23 20
 import com.reactnativenavigation.utils.Now;
21
+import com.reactnativenavigation.utils.StatusBarUtils;
24 22
 import com.reactnativenavigation.utils.TypefaceLoader;
25 23
 import com.reactnativenavigation.utils.UiThread;
26 24
 import com.reactnativenavigation.utils.UiUtils;
@@ -29,6 +27,11 @@ import com.reactnativenavigation.viewcontrollers.navigator.Navigator;
29 27
 
30 28
 import java.util.ArrayList;
31 29
 
30
+import androidx.annotation.NonNull;
31
+import androidx.annotation.Nullable;
32
+
33
+import static com.reactnativenavigation.utils.UiUtils.pxToDp;
34
+
32 35
 public class NavigationModule extends ReactContextBaseJavaModule {
33 36
     private static final String NAME = "RNNBridgeModule";
34 37
 
@@ -75,8 +78,8 @@ public class NavigationModule extends ReactContextBaseJavaModule {
75 78
         WritableMap constants = Arguments.createMap();
76 79
         constants.putString(Constants.BACK_BUTTON_JS_KEY, Constants.BACK_BUTTON_ID);
77 80
         constants.putDouble(Constants.BOTTOM_TABS_HEIGHT_KEY, Constants.BOTTOM_TABS_HEIGHT);
78
-        constants.putDouble(Constants.STATUS_BAR_HEIGHT_KEY, UiUtils.pxToDp(ctx, UiUtils.getStatusBarHeight(ctx)));
79
-        constants.putDouble(Constants.TOP_BAR_HEIGHT_KEY, UiUtils.pxToDp(ctx, UiUtils.getTopBarHeight(ctx)));
81
+        constants.putDouble(Constants.STATUS_BAR_HEIGHT_KEY, pxToDp(ctx, StatusBarUtils.getStatusBarHeight(ctx)));
82
+        constants.putDouble(Constants.TOP_BAR_HEIGHT_KEY, pxToDp(ctx, UiUtils.getTopBarHeight(ctx)));
80 83
         promise.resolve(constants);
81 84
     }
82 85
 

+ 7
- 7
lib/android/app/src/main/java/com/reactnativenavigation/react/NavigationPackage.java 查看文件

@@ -1,7 +1,5 @@
1 1
 package com.reactnativenavigation.react;
2 2
 
3
-import android.support.annotation.NonNull;
4
-
5 3
 import com.facebook.react.ReactNativeHost;
6 4
 import com.facebook.react.ReactPackage;
7 5
 import com.facebook.react.bridge.NativeModule;
@@ -9,14 +7,16 @@ import com.facebook.react.bridge.ReactApplicationContext;
9 7
 import com.facebook.react.uimanager.ViewManager;
10 8
 import com.reactnativenavigation.parse.LayoutFactory;
11 9
 
12
-import java.util.Collections;
13 10
 import java.util.List;
14 11
 
12
+import androidx.annotation.NonNull;
13
+
14
+import static java.util.Collections.singletonList;
15
+
15 16
 public class NavigationPackage implements ReactPackage {
16 17
 
17 18
     private ReactNativeHost reactNativeHost;
18 19
 
19
-    @SuppressWarnings("WeakerAccess")
20 20
     public NavigationPackage(final ReactNativeHost reactNativeHost) {
21 21
         this.reactNativeHost = reactNativeHost;
22 22
     }
@@ -24,7 +24,7 @@ public class NavigationPackage implements ReactPackage {
24 24
     @NonNull
25 25
     @Override
26 26
     public List<NativeModule> createNativeModules(@NonNull ReactApplicationContext reactContext) {
27
-        return Collections.singletonList(new NavigationModule(
27
+        return singletonList(new NavigationModule(
28 28
                         reactContext,
29 29
                         reactNativeHost.getReactInstanceManager(),
30 30
                         new LayoutFactory(reactNativeHost.getReactInstanceManager())
@@ -34,7 +34,7 @@ public class NavigationPackage implements ReactPackage {
34 34
 
35 35
     @NonNull
36 36
     @Override
37
-    public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
38
-        return Collections.singletonList(new ElementViewManager());
37
+    public List<ViewManager> createViewManagers(@NonNull ReactApplicationContext reactContext) {
38
+        return singletonList(new ElementViewManager());
39 39
     }
40 40
 }

+ 1
- 1
lib/android/app/src/main/java/com/reactnativenavigation/react/NavigationReactInitializer.java 查看文件

@@ -1,6 +1,6 @@
1 1
 package com.reactnativenavigation.react;
2 2
 
3
-import android.support.annotation.NonNull;
3
+import androidx.annotation.NonNull;
4 4
 
5 5
 import com.facebook.react.ReactInstanceManager;
6 6
 import com.facebook.react.bridge.ReactContext;

+ 1
- 1
lib/android/app/src/main/java/com/reactnativenavigation/react/ReactView.java 查看文件

@@ -3,7 +3,7 @@ package com.reactnativenavigation.react;
3 3
 import android.annotation.SuppressLint;
4 4
 import android.content.Context;
5 5
 import android.os.Bundle;
6
-import android.support.annotation.RestrictTo;
6
+import androidx.annotation.RestrictTo;
7 7
 import android.view.MotionEvent;
8 8
 
9 9
 import com.facebook.react.ReactInstanceManager;

+ 3
- 3
lib/android/app/src/main/java/com/reactnativenavigation/utils/ButtonPresenter.java 查看文件

@@ -5,9 +5,9 @@ import android.graphics.PorterDuff;
5 5
 import android.graphics.PorterDuffColorFilter;
6 6
 import android.graphics.Typeface;
7 7
 import android.graphics.drawable.Drawable;
8
-import android.support.annotation.NonNull;
9
-import android.support.v7.widget.ActionMenuView;
10
-import android.support.v7.widget.Toolbar;
8
+import androidx.annotation.NonNull;
9
+import androidx.appcompat.widget.ActionMenuView;
10
+import androidx.appcompat.widget.Toolbar;
11 11
 import android.text.Spannable;
12 12
 import android.text.SpannableString;
13 13
 import android.text.style.AbsoluteSizeSpan;

+ 15
- 3
lib/android/app/src/main/java/com/reactnativenavigation/utils/CollectionUtils.java 查看文件

@@ -1,8 +1,5 @@
1 1
 package com.reactnativenavigation.utils;
2 2
 
3
-import android.support.annotation.NonNull;
4
-import android.support.annotation.Nullable;
5
-import android.support.v4.util.Pair;
6 3
 
7 4
 import java.util.ArrayList;
8 5
 import java.util.Collection;
@@ -13,6 +10,10 @@ import java.util.List;
13 10
 import java.util.Map;
14 11
 import java.util.Objects;
15 12
 
13
+import androidx.annotation.NonNull;
14
+import androidx.annotation.Nullable;
15
+import androidx.core.util.Pair;
16
+
16 17
 public class CollectionUtils {
17 18
     public interface Apply<T> {
18 19
         void on(T t);
@@ -101,6 +102,17 @@ public class CollectionUtils {
101 102
         return null;
102 103
     }
103 104
 
105
+    public static @Nullable <T> T first(@Nullable Collection<T> items, Filter<T> by, Functions.Func1<T> apply) {
106
+        if (isNullOrEmpty(items)) return null;
107
+        for (T item : items) {
108
+            if (by.filter(item)) {
109
+                apply.run(item);
110
+                return item;
111
+            }
112
+        }
113
+        return null;
114
+    }
115
+
104 116
     public static <T> T last(@Nullable List<T> items) {
105 117
         return CollectionUtils.isNullOrEmpty(items) ? null : items.get(items.size() - 1);
106 118
     }

+ 2
- 2
lib/android/app/src/main/java/com/reactnativenavigation/utils/ColorUtils.java 查看文件

@@ -3,11 +3,11 @@ package com.reactnativenavigation.utils;
3 3
 public class ColorUtils {
4 4
     public static double[] colorToLAB(int color) {
5 5
         final double[] result = new double[3];
6
-        android.support.v4.graphics.ColorUtils.colorToLAB(color, result);
6
+        androidx.core.graphics.ColorUtils.colorToLAB(color, result);
7 7
         return result;
8 8
     }
9 9
 
10 10
     public static int labToColor(double[] lab) {
11
-        return android.support.v4.graphics.ColorUtils.LABToColor(lab[0], lab[1], lab[2]);
11
+        return androidx.core.graphics.ColorUtils.LABToColor(lab[0], lab[1], lab[2]);
12 12
     }
13 13
 }

+ 1
- 1
lib/android/app/src/main/java/com/reactnativenavigation/utils/CommandListenerAdapter.java 查看文件

@@ -1,6 +1,6 @@
1 1
 package com.reactnativenavigation.utils;
2 2
 
3
-import android.support.annotation.Nullable;
3
+import androidx.annotation.Nullable;
4 4
 
5 5
 public class CommandListenerAdapter implements CommandListener {
6 6
 

+ 17
- 0
lib/android/app/src/main/java/com/reactnativenavigation/utils/CoordinatorLayoutUtils.java 查看文件

@@ -0,0 +1,17 @@
1
+package com.reactnativenavigation.utils;
2
+
3
+import androidx.coordinatorlayout.widget.CoordinatorLayout;
4
+
5
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
6
+
7
+public class CoordinatorLayoutUtils {
8
+    public static CoordinatorLayout.LayoutParams matchParentLP() {
9
+        return new CoordinatorLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT);
10
+    }
11
+
12
+    public static CoordinatorLayout.LayoutParams matchParentWithBehaviour(CoordinatorLayout.Behavior behavior) {
13
+        CoordinatorLayout.LayoutParams lp = new CoordinatorLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT);
14
+        lp.setBehavior(behavior);
15
+        return lp;
16
+    }
17
+}

+ 2
- 2
lib/android/app/src/main/java/com/reactnativenavigation/utils/ImageLoader.java 查看文件

@@ -7,8 +7,8 @@ import android.graphics.drawable.BitmapDrawable;
7 7
 import android.graphics.drawable.Drawable;
8 8
 import android.net.Uri;
9 9
 import android.os.StrictMode;
10
-import android.support.annotation.NonNull;
11
-import android.support.annotation.Nullable;
10
+import androidx.annotation.NonNull;
11
+import androidx.annotation.Nullable;
12 12
 
13 13
 import com.facebook.react.views.imagehelper.ResourceDrawableIdHelper;
14 14
 import com.reactnativenavigation.NavigationApplication;

+ 1
- 1
lib/android/app/src/main/java/com/reactnativenavigation/utils/ImageLoadingListenerAdapter.java 查看文件

@@ -1,7 +1,7 @@
1 1
 package com.reactnativenavigation.utils;
2 2
 
3 3
 import android.graphics.drawable.Drawable;
4
-import android.support.annotation.NonNull;
4
+import androidx.annotation.NonNull;
5 5
 
6 6
 import java.util.List;
7 7
 

+ 21
- 0
lib/android/app/src/main/java/com/reactnativenavigation/utils/LateInit.java 查看文件

@@ -0,0 +1,21 @@
1
+package com.reactnativenavigation.utils;
2
+
3
+import com.reactnativenavigation.utils.Functions.Func1;
4
+
5
+import androidx.annotation.NonNull;
6
+
7
+public class LateInit<T> {
8
+    private T value;
9
+
10
+    public T get() {
11
+        return value;
12
+    }
13
+
14
+    public void set(@NonNull T value) {
15
+        this.value = value;
16
+    }
17
+
18
+    public void perform(Func1<T> task) {
19
+        if (value != null) task.run(value);
20
+    }
21
+}

+ 1
- 1
lib/android/app/src/main/java/com/reactnativenavigation/utils/NativeCommandListener.java 查看文件

@@ -1,6 +1,6 @@
1 1
 package com.reactnativenavigation.utils;
2 2
 
3
-import android.support.annotation.Nullable;
3
+import androidx.annotation.Nullable;
4 4
 
5 5
 import com.facebook.react.bridge.Promise;
6 6
 import com.reactnativenavigation.react.EventEmitter;

+ 1
- 1
lib/android/app/src/main/java/com/reactnativenavigation/utils/ObjectUtils.java 查看文件

@@ -1,6 +1,6 @@
1 1
 package com.reactnativenavigation.utils;
2 2
 
3
-import android.support.annotation.Nullable;
3
+import androidx.annotation.Nullable;
4 4
 
5 5
 import com.reactnativenavigation.utils.Functions.Func1;
6 6
 import com.reactnativenavigation.utils.Functions.FuncR1;

+ 1
- 1
lib/android/app/src/main/java/com/reactnativenavigation/utils/ReflectionUtils.java 查看文件

@@ -1,6 +1,6 @@
1 1
 package com.reactnativenavigation.utils;
2 2
 
3
-import android.support.annotation.Nullable;
3
+import androidx.annotation.Nullable;
4 4
 
5 5
 import java.lang.reflect.Field;
6 6
 

+ 30
- 0
lib/android/app/src/main/java/com/reactnativenavigation/utils/StatusBarUtils.java 查看文件

@@ -0,0 +1,30 @@
1
+package com.reactnativenavigation.utils;
2
+
3
+import android.content.Context;
4
+import android.content.res.Resources;
5
+import android.os.Build;
6
+
7
+import static com.reactnativenavigation.utils.UiUtils.dpToPx;
8
+
9
+public class StatusBarUtils {
10
+    private static final int STATUS_BAR_HEIGHT_M = 24;
11
+    private static final int STATUS_BAR_HEIGHT_L = 25;
12
+    private static int statusBarHeight = -1;
13
+
14
+    public static void saveStatusBarHeight(int height) {
15
+        statusBarHeight = height;
16
+    }
17
+
18
+    public static int getStatusBarHeight(Context context) {
19
+        if (statusBarHeight > 0) {
20
+            return statusBarHeight;
21
+        }
22
+        final Resources resources = context.getResources();
23
+        final int resourceId = resources.getIdentifier("status_bar_height", "dimen", "android");
24
+        statusBarHeight = resourceId > 0 ?
25
+                resources.getDimensionPixelSize(resourceId) :
26
+                dpToPx(context, Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? STATUS_BAR_HEIGHT_M : STATUS_BAR_HEIGHT_L);
27
+        return statusBarHeight;
28
+    }
29
+
30
+}

+ 1
- 1
lib/android/app/src/main/java/com/reactnativenavigation/utils/StringUtils.java 查看文件

@@ -1,6 +1,6 @@
1 1
 package com.reactnativenavigation.utils;
2 2
 
3
-import android.support.annotation.Nullable;
3
+import androidx.annotation.Nullable;
4 4
 
5 5
 public class StringUtils {
6 6
 

+ 1
- 1
lib/android/app/src/main/java/com/reactnativenavigation/utils/TextViewUtils.java 查看文件

@@ -1,7 +1,7 @@
1 1
 package com.reactnativenavigation.utils;
2 2
 
3 3
 import android.graphics.Color;
4
-import android.support.annotation.ColorInt;
4
+import androidx.annotation.ColorInt;
5 5
 import android.text.SpannableString;
6 6
 import android.text.Spanned;
7 7
 import android.text.SpannedString;

+ 1
- 1
lib/android/app/src/main/java/com/reactnativenavigation/utils/TypefaceLoader.java 查看文件

@@ -3,7 +3,7 @@ package com.reactnativenavigation.utils;
3 3
 import android.content.Context;
4 4
 import android.content.res.AssetManager;
5 5
 import android.graphics.Typeface;
6
-import android.support.annotation.Nullable;
6
+import androidx.annotation.Nullable;
7 7
 import android.text.TextUtils;
8 8
 
9 9
 import java.io.IOException;

+ 2
- 18
lib/android/app/src/main/java/com/reactnativenavigation/utils/UiUtils.java 查看文件

@@ -2,22 +2,18 @@ package com.reactnativenavigation.utils;
2 2
 
3 3
 import android.content.Context;
4 4
 import android.content.res.Resources;
5
-import android.os.Build;
6 5
 import android.os.Handler;
7 6
 import android.os.Looper;
8
-import android.support.annotation.NonNull;
9
-import android.support.annotation.Nullable;
7
+import androidx.annotation.NonNull;
8
+import androidx.annotation.Nullable;
10 9
 import android.util.DisplayMetrics;
11 10
 import android.view.View;
12 11
 import android.view.ViewTreeObserver;
13 12
 import android.view.WindowManager;
14 13
 
15 14
 public class UiUtils {
16
-    private static final int STATUS_BAR_HEIGHT_M = 24;
17
-    private static final int STATUS_BAR_HEIGHT_L = 25;
18 15
     private static final int DEFAULT_TOOLBAR_HEIGHT = 56;
19 16
 
20
-    private static int statusBarHeight = -1;
21 17
     private static int topBarHeight = -1;
22 18
 
23 19
     public static <T extends View> void runOnPreDrawOnce(@Nullable final T view, final Functions.Func1<T> task) {
@@ -75,18 +71,6 @@ public class UiUtils {
75 71
         return metrics;
76 72
     }
77 73
 
78
-    public static int getStatusBarHeight(Context context) {
79
-        if (statusBarHeight > 0) {
80
-            return statusBarHeight;
81
-        }
82
-        final Resources resources = context.getResources();
83
-        final int resourceId = resources.getIdentifier("status_bar_height", "dimen", "android");
84
-        statusBarHeight = resourceId > 0 ?
85
-                resources.getDimensionPixelSize(resourceId) :
86
-                dpToPx(context, Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? STATUS_BAR_HEIGHT_M : STATUS_BAR_HEIGHT_L);
87
-        return statusBarHeight;
88
-    }
89
-
90 74
     public static int getTopBarHeightDp(Context context) {
91 75
         return (int) UiUtils.pxToDp(context, getTopBarHeight(context));
92 76
     }

+ 12
- 2
lib/android/app/src/main/java/com/reactnativenavigation/utils/ViewUtils.java 查看文件

@@ -1,7 +1,7 @@
1 1
 package com.reactnativenavigation.utils;
2 2
 
3 3
 import android.graphics.Point;
4
-import android.support.annotation.Nullable;
4
+import androidx.annotation.Nullable;
5 5
 import android.view.View;
6 6
 import android.view.ViewGroup;
7 7
 import android.view.ViewManager;
@@ -14,6 +14,8 @@ import com.reactnativenavigation.utils.Functions.Func1;
14 14
 import java.util.ArrayList;
15 15
 import java.util.List;
16 16
 
17
+import static com.reactnativenavigation.utils.ObjectUtils.perform;
18
+
17 19
 public class ViewUtils {
18 20
     @Nullable
19 21
     public static <T extends View> T findChildByClass(ViewGroup root, Class<T> clazz) {
@@ -71,7 +73,7 @@ public class ViewUtils {
71 73
     }
72 74
 
73 75
     public static boolean isChildOf(ViewGroup parent, View child) {
74
-        if (parent == child) return true;
76
+        if (parent == child) return false;
75 77
 
76 78
         for (int i = 0; i < parent.getChildCount(); i++) {
77 79
             View view = parent.getChildAt(i);
@@ -138,4 +140,12 @@ public class ViewUtils {
138 140
             ((ViewManager) parent).removeView(view);
139 141
         }
140 142
     }
143
+
144
+    public static boolean isVisible(View view) {
145
+        return perform(view, false, v -> v.getVisibility() == View.VISIBLE);
146
+    }
147
+
148
+    public static int topMargin(View view) {
149
+        return ((ViewGroup.MarginLayoutParams) view.getLayoutParams()).topMargin;
150
+    }
141 151
 }

+ 15
- 0
lib/android/app/src/main/java/com/reactnativenavigation/utils/WindowInsetsUtils.java 查看文件

@@ -0,0 +1,15 @@
1
+package com.reactnativenavigation.utils;
2
+
3
+import androidx.core.view.WindowInsetsCompat;
4
+import android.util.Log;
5
+
6
+public class WindowInsetsUtils {
7
+    private static final String TAG = "GUYCA";
8
+
9
+    public static void log(WindowInsetsCompat i) {
10
+        Log.i(TAG, "t: " + i.getStableInsetTop() +
11
+                   " sysT: " + i.getSystemWindowInsetTop() +
12
+                   " b: " + i.getStableInsetBottom() +
13
+                   " sysB: " + i.getSystemWindowInsetBottom());
14
+    }
15
+}

+ 33
- 8
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/ChildController.java 查看文件

@@ -1,16 +1,20 @@
1 1
 package com.reactnativenavigation.viewcontrollers;
2 2
 
3 3
 import android.app.Activity;
4
-import android.support.annotation.CallSuper;
4
+import androidx.annotation.CallSuper;
5
+import androidx.core.view.ViewCompat;
6
+import androidx.core.view.WindowInsetsCompat;
7
+import android.view.View;
5 8
 import android.view.ViewGroup;
6 9
 
7 10
 import com.reactnativenavigation.parse.Options;
8 11
 import com.reactnativenavigation.presentation.Presenter;
12
+import com.reactnativenavigation.utils.StatusBarUtils;
9 13
 import com.reactnativenavigation.viewcontrollers.navigator.Navigator;
10 14
 import com.reactnativenavigation.views.Component;
11 15
 
12 16
 public abstract class ChildController<T extends ViewGroup> extends ViewController<T>  {
13
-    final Presenter presenter;
17
+    private final Presenter presenter;
14 18
     private final ChildControllersRegistry childRegistry;
15 19
 
16 20
     public ChildControllersRegistry getChildRegistry() {
@@ -23,6 +27,16 @@ public abstract class ChildController<T extends ViewGroup> extends ViewControlle
23 27
         this.childRegistry = childRegistry;
24 28
     }
25 29
 
30
+    @Override
31
+    public T getView() {
32
+        if (view == null) {
33
+            super.getView();
34
+            view.setFitsSystemWindows(true);
35
+            ViewCompat.setOnApplyWindowInsetsListener(view, this::onApplyWindowInsets);
36
+        }
37
+        return view;
38
+    }
39
+
26 40
     @Override
27 41
     @CallSuper
28 42
     public void setDefaultOptions(Options defaultOptions) {
@@ -42,17 +56,14 @@ public abstract class ChildController<T extends ViewGroup> extends ViewControlle
42 56
     }
43 57
 
44 58
     public void onViewBroughtToFront() {
45
-        presenter.onViewBroughtToFront(getView(), options);
59
+        presenter.onViewBroughtToFront(resolveCurrentOptions());
46 60
     }
47 61
 
48 62
     @Override
49 63
     public void applyOptions(Options options) {
50 64
         super.applyOptions(options);
51 65
         Options resolvedOptions = resolveCurrentOptions();
52
-        presenter.applyOptions(getView(), resolvedOptions);
53
-        if (isRoot()) {
54
-            presenter.applyRootOptions(getView(), resolvedOptions);
55
-        }
66
+        presenter.applyOptions(this, resolvedOptions);
56 67
     }
57 68
 
58 69
     @Override
@@ -65,7 +76,7 @@ public abstract class ChildController<T extends ViewGroup> extends ViewControlle
65 76
     @Override
66 77
     public void destroy() {
67 78
         if (!isDestroyed() && getView() instanceof Component) {
68
-            performOnParentController(parent -> parent.onChildDestroyed((Component) getView()));
79
+            performOnParentController(parent -> parent.onChildDestroyed(this));
69 80
         }
70 81
         super.destroy();
71 82
         childRegistry.onChildDestroyed(this);
@@ -76,4 +87,18 @@ public abstract class ChildController<T extends ViewGroup> extends ViewControlle
76 87
                 !(this instanceof Navigator) &&
77 88
                 getView().getParent() != null;
78 89
     }
90
+
91
+    private WindowInsetsCompat onApplyWindowInsets(View view, WindowInsetsCompat insets) {
92
+        StatusBarUtils.saveStatusBarHeight(insets.getSystemWindowInsetTop());
93
+        return applyWindowInsets(findController(view), insets);
94
+    }
95
+
96
+    protected WindowInsetsCompat applyWindowInsets(ViewController view, WindowInsetsCompat insets) {
97
+        return insets.replaceSystemWindowInsets(
98
+                insets.getSystemWindowInsetLeft(),
99
+                0,
100
+                insets.getSystemWindowInsetRight(),
101
+                insets.getSystemWindowInsetBottom()
102
+        );
103
+    }
79 104
 }

+ 3
- 1
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/ChildControllersRegistry.java 查看文件

@@ -2,6 +2,8 @@ package com.reactnativenavigation.viewcontrollers;
2 2
 
3 3
 import java.util.ArrayDeque;
4 4
 
5
+import static com.reactnativenavigation.utils.ObjectUtils.perform;
6
+
5 7
 public class ChildControllersRegistry {
6 8
     private ArrayDeque<ChildController> children = new ArrayDeque<>();
7 9
 
@@ -19,7 +21,7 @@ public class ChildControllersRegistry {
19 21
     }
20 22
 
21 23
     private boolean isTopChild(ChildController child) {
22
-        return children.peek().equals(child);
24
+        return perform(children.peek(), false, c -> c.equals(child));
23 25
     }
24 26
 
25 27
     public int size() {

+ 41
- 8
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/ComponentViewController.java 查看文件

@@ -1,21 +1,30 @@
1 1
 package com.reactnativenavigation.viewcontrollers;
2 2
 
3 3
 import android.app.Activity;
4
-import android.support.annotation.NonNull;
5 4
 import android.view.View;
6 5
 
7 6
 import com.reactnativenavigation.parse.Options;
8 7
 import com.reactnativenavigation.presentation.ComponentPresenter;
9 8
 import com.reactnativenavigation.presentation.Presenter;
9
+import com.reactnativenavigation.utils.StatusBarUtils;
10 10
 import com.reactnativenavigation.views.ComponentLayout;
11 11
 import com.reactnativenavigation.views.ReactComponent;
12 12
 
13
-public class ComponentViewController extends ChildController<ComponentLayout> {
13
+import androidx.annotation.NonNull;
14
+import androidx.core.view.ViewCompat;
15
+import androidx.core.view.WindowInsetsCompat;
16
+
17
+import static com.reactnativenavigation.utils.ObjectUtils.perform;
14 18
 
19
+public class ComponentViewController extends ChildController<ComponentLayout> {
15 20
     private final String componentName;
16 21
     private ComponentPresenter presenter;
17 22
     private final ReactViewCreator viewCreator;
18 23
 
24
+    ReactComponent getComponent() {
25
+        return view;
26
+    }
27
+
19 28
     public ComponentViewController(final Activity activity,
20 29
                                    final ChildControllersRegistry childRegistry,
21 30
                                    final String id,
@@ -39,12 +48,12 @@ public class ComponentViewController extends ChildController<ComponentLayout> {
39 48
     @Override
40 49
     public void onViewAppeared() {
41 50
         super.onViewAppeared();
42
-        view.sendComponentStart();
51
+        if (view != null) view.sendComponentStart();
43 52
     }
44 53
 
45 54
     @Override
46 55
     public void onViewDisappear() {
47
-        view.sendComponentStop();
56
+        if (view != null) view.sendComponentStop();
48 57
         super.onViewDisappear();
49 58
     }
50 59
 
@@ -55,6 +64,7 @@ public class ComponentViewController extends ChildController<ComponentLayout> {
55 64
 
56 65
     @Override
57 66
     public void applyOptions(Options options) {
67
+        if (isRoot()) applyTopInset();
58 68
         super.applyOptions(options);
59 69
         getView().applyOptions(options);
60 70
         presenter.applyOptions(getView(), resolveCurrentOptions(presenter.defaultOptions));
@@ -62,7 +72,7 @@ public class ComponentViewController extends ChildController<ComponentLayout> {
62 72
 
63 73
     @Override
64 74
     public boolean isViewShown() {
65
-        return super.isViewShown() && view.isReady();
75
+        return super.isViewShown() && view != null && view.isReady();
66 76
     }
67 77
 
68 78
     @NonNull
@@ -76,12 +86,35 @@ public class ComponentViewController extends ChildController<ComponentLayout> {
76 86
     public void mergeOptions(Options options) {
77 87
         if (options == Options.EMPTY) return;
78 88
         presenter.mergeOptions(getView(), options);
79
-        performOnParentController(parentController -> parentController.mergeChildOptions(options, this, getView()));
80 89
         super.mergeOptions(options);
90
+        performOnParentController(parentController -> parentController.mergeChildOptions(options, this));
81 91
     }
82 92
 
83
-    ReactComponent getComponent() {
84
-        return view;
93
+    @Override
94
+    public void applyTopInset() {
95
+        if (view != null) presenter.applyTopInsets(view, getTopInset());
96
+    }
97
+
98
+    @Override
99
+    public int getTopInset() {
100
+        int statusBarInset = resolveCurrentOptions().statusBar.isHiddenOrDrawBehind() ? 0 : StatusBarUtils.getStatusBarHeight(getActivity());
101
+        return statusBarInset + perform(getParentController(), 0, p -> p.getTopInset(this));
102
+    }
103
+
104
+    @Override
105
+    public void applyBottomInset() {
106
+        if (view != null) presenter.applyBottomInset(view, getBottomInset());
107
+    }
108
+
109
+    @Override
110
+    protected WindowInsetsCompat applyWindowInsets(ViewController view, WindowInsetsCompat insets) {
111
+        ViewCompat.onApplyWindowInsets(view.getView(), insets.replaceSystemWindowInsets(
112
+                insets.getSystemWindowInsetLeft(),
113
+                insets.getSystemWindowInsetTop(),
114
+                insets.getSystemWindowInsetRight(),
115
+                Math.max(insets.getSystemWindowInsetBottom() - getBottomInset(), 0)
116
+        ));
117
+        return insets;
85 118
     }
86 119
 
87 120
     @Override

+ 1
- 1
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/IdStack.java 查看文件

@@ -1,6 +1,6 @@
1 1
 package com.reactnativenavigation.viewcontrollers;
2 2
 
3
-import android.support.annotation.NonNull;
3
+import androidx.annotation.NonNull;
4 4
 
5 5
 import com.reactnativenavigation.utils.StringUtils;
6 6
 

+ 59
- 16
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/ParentController.java 查看文件

@@ -1,11 +1,7 @@
1 1
 package com.reactnativenavigation.viewcontrollers;
2 2
 
3 3
 import android.app.Activity;
4
-import android.support.annotation.CallSuper;
5
-import android.support.annotation.CheckResult;
6
-import android.support.annotation.NonNull;
7
-import android.support.annotation.Nullable;
8
-import android.support.v4.view.ViewPager;
4
+import android.view.View;
9 5
 import android.view.ViewGroup;
10 6
 
11 7
 import com.reactnativenavigation.parse.Options;
@@ -16,7 +12,14 @@ import com.reactnativenavigation.views.Component;
16 12
 
17 13
 import java.util.Collection;
18 14
 
19
-import static com.reactnativenavigation.utils.CollectionUtils.forEach;
15
+import androidx.annotation.CallSuper;
16
+import androidx.annotation.CheckResult;
17
+import androidx.annotation.NonNull;
18
+import androidx.annotation.Nullable;
19
+import androidx.viewpager.widget.ViewPager;
20
+
21
+import static com.reactnativenavigation.utils.CollectionUtils.*;
22
+import static com.reactnativenavigation.utils.ObjectUtils.perform;
20 23
 
21 24
 public abstract class ParentController<T extends ViewGroup> extends ChildController {
22 25
 
@@ -46,12 +49,24 @@ public abstract class ParentController<T extends ViewGroup> extends ChildControl
46 49
                 .withDefaultOptions(initialOptions);
47 50
     }
48 51
 
52
+    public Options resolveChildOptions(ViewController child) {
53
+	    if (child == this) return resolveCurrentOptions();
54
+        return child
55
+                .resolveCurrentOptions()
56
+                .copy()
57
+                .withDefaultOptions(initialOptions);
58
+    }
59
+
49 60
     @Override
50 61
     @CheckResult
51 62
     public Options resolveCurrentOptions(Options defaultOptions) {
52 63
         return resolveCurrentOptions().withDefaultOptions(defaultOptions);
53 64
     }
54 65
 
66
+    public boolean isCurrentChild(ViewController child) {
67
+        return getCurrentChild() == child;
68
+    }
69
+
55 70
     protected abstract ViewController getCurrentChild();
56 71
 
57 72
     @NonNull
@@ -81,6 +96,20 @@ public abstract class ParentController<T extends ViewGroup> extends ChildControl
81 96
 		return null;
82 97
 	}
83 98
 
99
+    @Nullable
100
+    @Override
101
+    public ViewController findController(View child) {
102
+        ViewController fromSuper = super.findController(child);
103
+        if (fromSuper != null) return fromSuper;
104
+
105
+        for (ViewController childController : getChildControllers()) {
106
+            ViewController fromChild = childController.findController(child);
107
+            if (fromChild != null) return fromChild;
108
+        }
109
+
110
+        return null;
111
+    }
112
+
84 113
     @Override
85 114
     public boolean containsComponent(Component component) {
86 115
         if (super.containsComponent(component)) {
@@ -93,27 +122,23 @@ public abstract class ParentController<T extends ViewGroup> extends ChildControl
93 122
     }
94 123
 
95 124
     @CallSuper
96
-    public void applyChildOptions(Options options, Component child) {
125
+    public void applyChildOptions(Options options, ViewController child) {
97 126
         this.options = initialOptions.mergeWith(options);
98
-        if (isRoot()) {
99
-            presenter.applyRootOptions(getView(), options);
100
-        }
101 127
     }
102 128
 
103 129
     @CallSuper
104
-    public void mergeChildOptions(Options options, ViewController childController, Component child) {
130
+    public void mergeChildOptions(Options options, ViewController childController) {
105 131
 
106 132
     }
107 133
 
108 134
 	@Override
109 135
 	public void destroy() {
110 136
 		super.destroy();
111
-		for (ViewController child : getChildControllers()) {
112
-			child.destroy();
113
-		}
137
+		forEach(getChildControllers(), ViewController::destroy);
114 138
 	}
115 139
 
116
-	@CallSuper
140
+	@SuppressWarnings("WeakerAccess")
141
+    @CallSuper
117 142
     protected void clearOptions() {
118 143
 	    performOnParentController(parent -> ((ParentController) parent).clearOptions());
119 144
         options = initialOptions.copy().clearOneTimeOptions();
@@ -132,7 +157,25 @@ public abstract class ParentController<T extends ViewGroup> extends ChildControl
132 157
         return getCurrentChild() != null && getCurrentChild().isRendered();
133 158
     }
134 159
 
135
-    public void onChildDestroyed(Component child) {
160
+    public void onChildDestroyed(ViewController child) {
161
+
162
+    }
163
+
164
+    @Override
165
+    public void applyTopInset() {
166
+	    forEach(getChildControllers(), ViewController::applyTopInset);
167
+    }
168
+
169
+    public int getTopInset(ViewController child) {
170
+        return perform(getParentController(), 0, p -> p.getTopInset(child));
171
+    }
172
+
173
+    @Override
174
+    public void applyBottomInset() {
175
+        forEach(getChildControllers(), ViewController::applyBottomInset);
176
+    }
136 177
 
178
+    public int getBottomInset(ViewController child) {
179
+        return perform(getParentController(), 0, p -> p.getBottomInset(child));
137 180
     }
138 181
 }

+ 4
- 4
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/TitleBarButtonController.java 查看文件

@@ -4,10 +4,10 @@ import android.annotation.SuppressLint;
4 4
 import android.app.Activity;
5 5
 import android.graphics.Color;
6 6
 import android.graphics.drawable.Drawable;
7
-import android.support.annotation.NonNull;
8
-import android.support.annotation.RestrictTo;
9
-import android.support.v7.widget.ActionMenuView;
10
-import android.support.v7.widget.Toolbar;
7
+import androidx.annotation.NonNull;
8
+import androidx.annotation.RestrictTo;
9
+import androidx.appcompat.widget.ActionMenuView;
10
+import androidx.appcompat.widget.Toolbar;
11 11
 import android.view.Menu;
12 12
 import android.view.MenuItem;
13 13
 import android.widget.ImageButton;

+ 52
- 11
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/ViewController.java 查看文件

@@ -1,11 +1,6 @@
1 1
 package com.reactnativenavigation.viewcontrollers;
2 2
 
3 3
 import android.app.Activity;
4
-import android.support.annotation.CallSuper;
5
-import android.support.annotation.CheckResult;
6
-import android.support.annotation.NonNull;
7
-import android.support.annotation.Nullable;
8
-import android.support.annotation.VisibleForTesting;
9 4
 import android.view.View;
10 5
 import android.view.ViewGroup;
11 6
 import android.view.ViewManager;
@@ -21,6 +16,7 @@ import com.reactnativenavigation.utils.StringUtils;
21 16
 import com.reactnativenavigation.utils.UiThread;
22 17
 import com.reactnativenavigation.utils.UiUtils;
23 18
 import com.reactnativenavigation.viewcontrollers.stack.StackController;
19
+import com.reactnativenavigation.views.BehaviourAdapter;
24 20
 import com.reactnativenavigation.views.Component;
25 21
 import com.reactnativenavigation.views.Renderable;
26 22
 import com.reactnativenavigation.views.element.Element;
@@ -29,9 +25,19 @@ import java.util.ArrayList;
29 25
 import java.util.Collections;
30 26
 import java.util.List;
31 27
 
32
-import static com.reactnativenavigation.utils.CollectionUtils.forEach;
28
+import androidx.annotation.CallSuper;
29
+import androidx.annotation.CheckResult;
30
+import androidx.annotation.NonNull;
31
+import androidx.annotation.Nullable;
32
+import androidx.annotation.VisibleForTesting;
33
+import androidx.coordinatorlayout.widget.CoordinatorLayout;
33 34
 
34
-public abstract class ViewController<T extends ViewGroup> implements ViewTreeObserver.OnGlobalLayoutListener, ViewGroup.OnHierarchyChangeListener {
35
+import static com.reactnativenavigation.utils.CollectionUtils.*;
36
+import static com.reactnativenavigation.utils.ObjectUtils.perform;
37
+
38
+public abstract class ViewController<T extends ViewGroup> implements ViewTreeObserver.OnGlobalLayoutListener,
39
+        ViewGroup.OnHierarchyChangeListener,
40
+        BehaviourAdapter<T> {
35 41
 
36 42
     private final List<Runnable> onAppearedListeners = new ArrayList();
37 43
     private boolean appearEventPosted;
@@ -136,6 +142,10 @@ public abstract class ViewController<T extends ViewGroup> implements ViewTreeObs
136 142
         return activity;
137 143
     }
138 144
 
145
+    public void performOnView(Func1<View> task) {
146
+        if (view != null) task.run(view);
147
+    }
148
+
139 149
     protected void performOnParentController(Func1<ParentController> task) {
140 150
         if (parentController != null) task.run(parentController);
141 151
     }
@@ -154,9 +164,7 @@ public abstract class ViewController<T extends ViewGroup> implements ViewTreeObs
154 164
             task.run((StackController) parentController);
155 165
         } else if (this instanceof StackController) {
156 166
             task.run((StackController) this);
157
-        } else if (parentController != null){
158
-            parentController.performOnParentStack(task);
159
-        }
167
+        } else performOnParentController(parent -> parent.performOnParentStack(task));
160 168
     }
161 169
 
162 170
     public T getView() {
@@ -194,6 +202,11 @@ public abstract class ViewController<T extends ViewGroup> implements ViewTreeObs
194 202
         return isSameId(id) ? this : null;
195 203
     }
196 204
 
205
+    @Nullable
206
+    public ViewController findController(View child) {
207
+        return view == child ? this : null;
208
+    }
209
+
197 210
     public boolean containsComponent(Component component) {
198 211
         return getView().equals(component);
199 212
     }
@@ -208,7 +221,7 @@ public abstract class ViewController<T extends ViewGroup> implements ViewTreeObs
208 221
         applyOptions(options);
209 222
         performOnParentController(parentController -> {
210 223
             parentController.clearOptions();
211
-            if (getView() instanceof Component) parentController.applyChildOptions(options, (Component) getView());
224
+            if (getView() instanceof Component) parentController.applyChildOptions(options, this);
212 225
         });
213 226
         if (!onAppearedListeners.isEmpty() && !appearEventPosted) {
214 227
             appearEventPosted = true;
@@ -310,4 +323,32 @@ public abstract class ViewController<T extends ViewGroup> implements ViewTreeObs
310 323
     public List<Element> getElements() {
311 324
         return getView() instanceof IReactView && view != null? ((IReactView) view).getElements() : Collections.EMPTY_LIST;
312 325
     }
326
+
327
+    @Override
328
+    @CallSuper
329
+    public boolean onMeasureChild(CoordinatorLayout parent, T child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) {
330
+        perform(findController(child), ViewController::applyTopInset);
331
+        return false;
332
+    }
333
+
334
+    @Override
335
+    public boolean onDependentViewChanged(CoordinatorLayout parent, T child, View dependency) {
336
+        return false;
337
+    }
338
+
339
+    public void applyTopInset() {
340
+
341
+    }
342
+
343
+    public int getTopInset() {
344
+        return 0;
345
+    }
346
+
347
+    public void applyBottomInset() {
348
+
349
+    }
350
+
351
+    public int getBottomInset() {
352
+        return perform(parentController, 0, p -> p.getBottomInset(this));
353
+    }
313 354
 }

+ 1
- 1
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/YellowBoxDelegate.java 查看文件

@@ -1,6 +1,6 @@
1 1
 package com.reactnativenavigation.viewcontrollers;
2 2
 
3
-import android.support.annotation.RestrictTo;
3
+import androidx.annotation.RestrictTo;
4 4
 import android.view.View;
5 5
 import android.view.ViewGroup;
6 6
 

+ 1
- 1
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/YellowBoxHelper.java 查看文件

@@ -1,6 +1,6 @@
1 1
 package com.reactnativenavigation.viewcontrollers;
2 2
 
3
-import android.support.annotation.NonNull;
3
+import androidx.annotation.NonNull;
4 4
 import android.view.View;
5 5
 import android.view.ViewGroup;
6 6
 

+ 10
- 14
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/bottomtabs/AttachMode.java 查看文件

@@ -1,24 +1,23 @@
1 1
 package com.reactnativenavigation.viewcontrollers.bottomtabs;
2 2
 
3
-import android.support.annotation.VisibleForTesting;
4
-import android.view.*;
5
-import android.widget.*;
3
+import androidx.annotation.VisibleForTesting;
4
+import android.view.View;
5
+import android.view.ViewGroup;
6 6
 
7
-import com.reactnativenavigation.parse.*;
8
-import com.reactnativenavigation.presentation.*;
9
-import com.reactnativenavigation.viewcontrollers.*;
7
+import com.reactnativenavigation.parse.Options;
8
+import com.reactnativenavigation.presentation.BottomTabsPresenter;
9
+import com.reactnativenavigation.viewcontrollers.ViewController;
10
+import com.reactnativenavigation.views.bottomtabs.BottomTabsBehaviour;
10 11
 
11
-import java.util.*;
12
+import java.util.List;
12 13
 
13
-import static android.view.ViewGroup.LayoutParams.*;
14
+import static com.reactnativenavigation.utils.CoordinatorLayoutUtils.matchParentWithBehaviour;
14 15
 
15 16
 public abstract class AttachMode {
16 17
     protected final ViewGroup parent;
17 18
     protected final BottomTabsPresenter presenter;
18 19
     protected final List<ViewController> tabs;
19 20
     final ViewController initialTab;
20
-    private final Options resolved;
21
-
22 21
 
23 22
     public static AttachMode get(ViewGroup parent, List<ViewController> tabs, BottomTabsPresenter presenter, Options resolved) {
24 23
         switch (resolved.bottomTabsOptions.tabsAttachMode) {
@@ -37,7 +36,6 @@ public abstract class AttachMode {
37 36
         this.parent = parent;
38 37
         this.tabs = tabs;
39 38
         this.presenter = presenter;
40
-        this.resolved = resolved;
41 39
         initialTab = tabs.get(resolved.bottomTabsOptions.currentTabIndex.get(0));
42 40
     }
43 41
 
@@ -54,9 +52,7 @@ public abstract class AttachMode {
54 52
     @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
55 53
     public void attach(ViewController tab) {
56 54
         ViewGroup view = tab.getView();
57
-        view.setLayoutParams(new RelativeLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT));
58
-        presenter.applyLayoutParamsOptions(resolved, tabs.indexOf(tab));
59 55
         view.setVisibility(tab == initialTab ? View.VISIBLE : View.INVISIBLE);
60
-        parent.addView(view);
56
+        parent.addView(view, matchParentWithBehaviour(new BottomTabsBehaviour(tab.getParentController())));
61 57
     }
62 58
 }

+ 1
- 12
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/bottomtabs/BottomTabFinder.java 查看文件

@@ -1,9 +1,8 @@
1 1
 package com.reactnativenavigation.viewcontrollers.bottomtabs;
2 2
 
3
-import android.support.annotation.IntRange;
3
+import androidx.annotation.IntRange;
4 4
 
5 5
 import com.reactnativenavigation.viewcontrollers.ViewController;
6
-import com.reactnativenavigation.views.Component;
7 6
 
8 7
 import java.util.List;
9 8
 
@@ -14,16 +13,6 @@ public class BottomTabFinder {
14 13
         this.tabs = tabs;
15 14
     }
16 15
 
17
-    @IntRange(from = -1)
18
-    public int findByComponent(Component component) {
19
-        for (int i = 0; i < tabs.size(); i++) {
20
-            if (tabs.get(i).containsComponent(component)) {
21
-                return i;
22
-            }
23
-        }
24
-        return -1;
25
-    }
26
-
27 16
     @IntRange(from = -1)
28 17
     public int findByControllerId(String id) {
29 18
         for (int i = 0; i < tabs.size(); i++) {

+ 1
- 1
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/bottomtabs/BottomTabsAttacher.java 查看文件

@@ -1,6 +1,6 @@
1 1
 package com.reactnativenavigation.viewcontrollers.bottomtabs;
2 2
 
3
-import android.support.annotation.VisibleForTesting;
3
+import androidx.annotation.VisibleForTesting;
4 4
 import android.view.ViewGroup;
5 5
 
6 6
 import com.reactnativenavigation.parse.Options;

+ 42
- 20
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/bottomtabs/BottomTabsController.java 查看文件

@@ -1,11 +1,9 @@
1 1
 package com.reactnativenavigation.viewcontrollers.bottomtabs;
2 2
 
3 3
 import android.app.Activity;
4
-import android.support.annotation.NonNull;
5
-import android.support.annotation.RestrictTo;
4
+import android.view.Gravity;
6 5
 import android.view.View;
7 6
 import android.view.ViewGroup;
8
-import android.widget.RelativeLayout;
9 7
 
10 8
 import com.aurelhubert.ahbottomnavigation.AHBottomNavigation;
11 9
 import com.aurelhubert.ahbottomnavigation.AHBottomNavigationItem;
@@ -21,18 +19,21 @@ import com.reactnativenavigation.viewcontrollers.ChildControllersRegistry;
21 19
 import com.reactnativenavigation.viewcontrollers.ParentController;
22 20
 import com.reactnativenavigation.viewcontrollers.ViewController;
23 21
 import com.reactnativenavigation.views.BottomTabs;
24
-import com.reactnativenavigation.views.Component;
22
+import com.reactnativenavigation.views.bottomtabs.BottomTabsLayout;
25 23
 
26 24
 import java.util.Collection;
27 25
 import java.util.List;
28 26
 
27
+import androidx.annotation.NonNull;
28
+import androidx.annotation.RestrictTo;
29
+import androidx.coordinatorlayout.widget.CoordinatorLayout;
30
+
29 31
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
30
-import static android.widget.RelativeLayout.ALIGN_PARENT_BOTTOM;
31
-import static com.reactnativenavigation.react.Constants.BOTTOM_TABS_HEIGHT;
32
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
32 33
 import static com.reactnativenavigation.utils.CollectionUtils.*;
33
-import static com.reactnativenavigation.utils.UiUtils.dpToPx;
34
+import static com.reactnativenavigation.utils.ObjectUtils.perform;
34 35
 
35
-public class BottomTabsController extends ParentController implements AHBottomNavigation.OnTabSelectedListener, TabSelector {
36
+public class BottomTabsController extends ParentController<BottomTabsLayout> implements AHBottomNavigation.OnTabSelectedListener, TabSelector {
36 37
 
37 38
 	private BottomTabs bottomTabs;
38 39
 	private List<ViewController> tabs;
@@ -50,7 +51,7 @@ public class BottomTabsController extends ParentController implements AHBottomNa
50 51
         this.tabsAttacher = tabsAttacher;
51 52
         this.presenter = bottomTabsPresenter;
52 53
         this.tabPresenter = bottomTabPresenter;
53
-        forEach(tabs, (tab) -> tab.setParentController(this));
54
+        forEach(tabs, tab -> tab.setParentController(this));
54 55
     }
55 56
 
56 57
     @Override
@@ -62,17 +63,19 @@ public class BottomTabsController extends ParentController implements AHBottomNa
62 63
 
63 64
     @NonNull
64 65
 	@Override
65
-	protected ViewGroup createView() {
66
-		RelativeLayout root = new RelativeLayout(getActivity());
67
-		bottomTabs = createBottomTabs();
66
+	protected BottomTabsLayout createView() {
67
+        BottomTabsLayout root = new BottomTabsLayout(getActivity());
68
+
69
+        bottomTabs = createBottomTabs();
68 70
         tabsAttacher.init(root, resolveCurrentOptions());
69 71
         presenter.bindView(bottomTabs, this);
70 72
         tabPresenter.bindView(bottomTabs);
71 73
         bottomTabs.setOnTabSelectedListener(this);
72
-		RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(MATCH_PARENT, dpToPx(getActivity(), BOTTOM_TABS_HEIGHT));
73
-		lp.addRule(ALIGN_PARENT_BOTTOM);
74
+        CoordinatorLayout.LayoutParams lp = new CoordinatorLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT);
75
+        lp.gravity = Gravity.BOTTOM;
74 76
 		root.addView(bottomTabs, lp);
75
-		bottomTabs.addItems(createTabs());
77
+
78
+        bottomTabs.addItems(createTabs());
76 79
         tabsAttacher.attach();
77 80
         return root;
78 81
 	}
@@ -96,13 +99,14 @@ public class BottomTabsController extends ParentController implements AHBottomNa
96 99
     @Override
97 100
     public void mergeOptions(Options options) {
98 101
         presenter.mergeOptions(options);
102
+        tabPresenter.mergeOptions(options);
99 103
         super.mergeOptions(options);
100 104
         this.options.bottomTabsOptions.clearOneTimeOptions();
101 105
         this.initialOptions.bottomTabsOptions.clearOneTimeOptions();
102 106
     }
103 107
 
104 108
     @Override
105
-    public void applyChildOptions(Options options, Component child) {
109
+    public void applyChildOptions(Options options, ViewController child) {
106 110
         super.applyChildOptions(options, child);
107 111
         presenter.applyChildOptions(resolveCurrentOptions(), child);
108 112
         performOnParentController(parentController ->
@@ -116,12 +120,12 @@ public class BottomTabsController extends ParentController implements AHBottomNa
116 120
     }
117 121
 
118 122
     @Override
119
-    public void mergeChildOptions(Options options, ViewController childController, Component child) {
120
-        super.mergeChildOptions(options, childController, child);
123
+    public void mergeChildOptions(Options options, ViewController child) {
124
+        super.mergeChildOptions(options, child);
121 125
         presenter.mergeChildOptions(options, child);
122 126
         tabPresenter.mergeChildOptions(options, child);
123 127
         performOnParentController(parentController ->
124
-                ((ParentController) parentController).mergeChildOptions(options.copy().clearBottomTabsOptions(), childController, child)
128
+                ((ParentController) parentController).mergeChildOptions(options.copy().clearBottomTabsOptions(), child)
125 129
         );
126 130
     }
127 131
 
@@ -164,7 +168,25 @@ public class BottomTabsController extends ParentController implements AHBottomNa
164 168
 		return bottomTabs.getCurrentItem();
165 169
 	}
166 170
 
167
-	@NonNull
171
+    @Override
172
+    public boolean onMeasureChild(CoordinatorLayout parent, ViewGroup child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) {
173
+        perform(findController(child), ViewController::applyBottomInset);
174
+        return super.onMeasureChild(parent, child, parentWidthMeasureSpec, widthUsed, parentHeightMeasureSpec, heightUsed);
175
+    }
176
+
177
+    @Override
178
+    public int getBottomInset(ViewController child) {
179
+        int bottomTabsInset = resolveChildOptions(child).bottomTabsOptions.drawBehind.isTrue() ? 0 : bottomTabs.getHeight();
180
+        return bottomTabsInset + perform(getParentController(), 0, p -> p.getBottomInset(this));
181
+    }
182
+
183
+    @Override
184
+    public void applyBottomInset() {
185
+        presenter.applyBottomInset(getBottomInset());
186
+        super.applyBottomInset();
187
+    }
188
+
189
+    @NonNull
168 190
 	@Override
169 191
 	public Collection<ViewController> getChildControllers() {
170 192
 		return tabs;

+ 2
- 2
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/button/NavigationIconResolver.java 查看文件

@@ -2,8 +2,8 @@ package com.reactnativenavigation.viewcontrollers.button;
2 2
 
3 3
 import android.content.Context;
4 4
 import android.graphics.drawable.Drawable;
5
-import android.support.annotation.NonNull;
6
-import android.support.v4.content.ContextCompat;
5
+import androidx.annotation.NonNull;
6
+import androidx.core.content.ContextCompat;
7 7
 import android.util.Log;
8 8
 import android.view.View;
9 9
 

+ 1
- 1
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/externalcomponent/ExternalComponentCreator.java 查看文件

@@ -1,6 +1,6 @@
1 1
 package com.reactnativenavigation.viewcontrollers.externalcomponent;
2 2
 
3
-import android.support.v4.app.FragmentActivity;
3
+import androidx.fragment.app.FragmentActivity;
4 4
 
5 5
 import com.facebook.react.ReactInstanceManager;
6 6
 

+ 36
- 4
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/externalcomponent/ExternalComponentViewController.java 查看文件

@@ -1,36 +1,47 @@
1 1
 package com.reactnativenavigation.viewcontrollers.externalcomponent;
2 2
 
3 3
 import android.app.Activity;
4
-import android.support.v4.app.FragmentActivity;
4
+import androidx.fragment.app.FragmentActivity;
5
+import androidx.core.view.ViewCompat;
6
+import android.view.View;
5 7
 
6 8
 import com.facebook.react.ReactInstanceManager;
7 9
 import com.reactnativenavigation.parse.ExternalComponent;
8 10
 import com.reactnativenavigation.parse.Options;
11
+import com.reactnativenavigation.presentation.ExternalComponentPresenter;
9 12
 import com.reactnativenavigation.react.EventEmitter;
13
+import com.reactnativenavigation.utils.CoordinatorLayoutUtils;
14
+import com.reactnativenavigation.utils.StatusBarUtils;
10 15
 import com.reactnativenavigation.viewcontrollers.NoOpYellowBoxDelegate;
11 16
 import com.reactnativenavigation.viewcontrollers.ViewController;
17
+import com.reactnativenavigation.views.BehaviourDelegate;
12 18
 import com.reactnativenavigation.views.ExternalComponentLayout;
13 19
 
20
+import static com.reactnativenavigation.utils.ObjectUtils.perform;
21
+
14 22
 public class ExternalComponentViewController extends ViewController<ExternalComponentLayout> {
15 23
     private final ExternalComponent externalComponent;
16 24
     private final ExternalComponentCreator componentCreator;
17 25
     private ReactInstanceManager reactInstanceManager;
18 26
     private final EventEmitter emitter;
27
+    private final ExternalComponentPresenter presenter;
19 28
 
20
-    public ExternalComponentViewController(Activity activity, String id, ExternalComponent externalComponent, ExternalComponentCreator componentCreator, ReactInstanceManager reactInstanceManager, EventEmitter emitter, Options initialOptions) {
29
+    public ExternalComponentViewController(Activity activity, String id, ExternalComponent externalComponent, ExternalComponentCreator componentCreator, ReactInstanceManager reactInstanceManager, EventEmitter emitter, ExternalComponentPresenter presenter, Options initialOptions) {
21 30
         super(activity, id, new NoOpYellowBoxDelegate(), initialOptions);
22 31
         this.externalComponent = externalComponent;
23 32
         this.componentCreator = componentCreator;
24 33
         this.reactInstanceManager = reactInstanceManager;
25 34
         this.emitter = emitter;
35
+        this.presenter = presenter;
26 36
     }
27 37
 
28 38
     @Override
29 39
     protected ExternalComponentLayout createView() {
30 40
         ExternalComponentLayout content = new ExternalComponentLayout(getActivity());
41
+        enableDrawingBehindStatusBar(content);
31 42
         content.addView(componentCreator
32 43
                 .create(getActivity(), reactInstanceManager, externalComponent.passProps)
33
-                .asView());
44
+                .asView(), CoordinatorLayoutUtils.matchParentWithBehaviour(new BehaviourDelegate(this)));
34 45
         return content;
35 46
     }
36 47
 
@@ -42,7 +53,7 @@ public class ExternalComponentViewController extends ViewController<ExternalComp
42 53
     @Override
43 54
     public void mergeOptions(Options options) {
44 55
         if (options == Options.EMPTY) return;
45
-        performOnParentController(parentController -> parentController.mergeChildOptions(options, this, getView()));
56
+        performOnParentController(parentController -> parentController.mergeChildOptions(options, this));
46 57
         super.mergeOptions(options);
47 58
     }
48 59
 
@@ -58,7 +69,28 @@ public class ExternalComponentViewController extends ViewController<ExternalComp
58 69
         emitter.emitComponentDidDisappear(getId(), externalComponent.name.get());
59 70
     }
60 71
 
72
+    @Override
73
+    public void applyTopInset() {
74
+        if (view != null) presenter.applyTopInsets(view, getTopInset());
75
+    }
76
+
77
+    @Override
78
+    public int getTopInset() {
79
+        int statusBarInset = resolveCurrentOptions().statusBar.drawBehind.isTrue() ? 0 : StatusBarUtils.getStatusBarHeight(getActivity());
80
+        return statusBarInset + perform(getParentController(), 0, p -> p.getTopInset(this));
81
+    }
82
+
83
+    @Override
84
+    public void applyBottomInset() {
85
+        if (view != null) presenter.applyBottomInset(view, getBottomInset());
86
+    }
87
+
61 88
     public FragmentActivity getActivity() {
62 89
         return (FragmentActivity) super.getActivity();
63 90
     }
91
+
92
+    private void enableDrawingBehindStatusBar(View view) {
93
+        view.setFitsSystemWindows(true);
94
+        ViewCompat.setOnApplyWindowInsetsListener(view, (v, insets) -> insets);
95
+    }
64 96
 }

+ 17
- 5
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/modal/ModalPresenter.java 查看文件

@@ -2,7 +2,6 @@ package com.reactnativenavigation.viewcontrollers.modal;
2 2
 
3 3
 import android.animation.Animator;
4 4
 import android.animation.AnimatorListenerAdapter;
5
-import android.support.annotation.Nullable;
6 5
 import android.view.ViewGroup;
7 6
 
8 7
 import com.reactnativenavigation.anim.ModalAnimator;
@@ -11,10 +10,15 @@ import com.reactnativenavigation.parse.Options;
11 10
 import com.reactnativenavigation.utils.CommandListener;
12 11
 import com.reactnativenavigation.viewcontrollers.ViewController;
13 12
 
13
+import androidx.annotation.Nullable;
14
+import androidx.coordinatorlayout.widget.CoordinatorLayout;
15
+
16
+import static com.reactnativenavigation.utils.CoordinatorLayoutUtils.matchParentLP;
17
+
14 18
 public class ModalPresenter {
15 19
 
16 20
     private ViewGroup rootLayout;
17
-    private ViewGroup modalsLayout;
21
+    private CoordinatorLayout modalsLayout;
18 22
     private ModalAnimator animator;
19 23
     private Options defaultOptions = new Options();
20 24
 
@@ -22,11 +26,11 @@ public class ModalPresenter {
22 26
         this.animator = animator;
23 27
     }
24 28
 
25
-    public void setRootLayout(ViewGroup rootLayout) {
29
+    void setRootLayout(ViewGroup rootLayout) {
26 30
         this.rootLayout = rootLayout;
27 31
     }
28 32
 
29
-    void setModalsLayout(ViewGroup modalsLayout) {
33
+    void setModalsLayout(CoordinatorLayout modalsLayout) {
30 34
         this.modalsLayout = modalsLayout;
31 35
     }
32 36
 
@@ -39,10 +43,13 @@ public class ModalPresenter {
39 43
             listener.onError("Can not show modal before activity is created");
40 44
             return;
41 45
         }
46
+
42 47
         Options options = toAdd.resolveCurrentOptions(defaultOptions);
43 48
         toAdd.setWaitForRender(options.animations.showModal.waitForRender);
44
-        modalsLayout.addView(toAdd.getView());
49
+        modalsLayout.addView(toAdd.getView(), matchParentLP());
50
+
45 51
         if (options.animations.showModal.enabled.isTrueOrUndefined()) {
52
+            toAdd.getView().setAlpha(0);
46 53
             if (options.animations.showModal.waitForRender.isTrue()) {
47 54
                 toAdd.addOnAppearedListener(() -> animateShow(toAdd, toRemove, listener, options));
48 55
             } else {
@@ -59,6 +66,11 @@ public class ModalPresenter {
59 66
 
60 67
     private void animateShow(ViewController toAdd, ViewController toRemove, CommandListener listener, Options options) {
61 68
         animator.show(toAdd.getView(), options.animations.showModal, new AnimatorListenerAdapter() {
69
+            @Override
70
+            public void onAnimationStart(Animator animation) {
71
+                toAdd.getView().setAlpha(1);
72
+            }
73
+
62 74
             @Override
63 75
             public void onAnimationEnd(Animator animation) {
64 76
                 onShowModalEnd(toAdd, toRemove, listener);

+ 3
- 2
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/modal/ModalStack.java 查看文件

@@ -1,7 +1,8 @@
1 1
 package com.reactnativenavigation.viewcontrollers.modal;
2 2
 
3 3
 import android.app.Activity;
4
-import android.support.annotation.RestrictTo;
4
+import androidx.annotation.RestrictTo;
5
+import androidx.coordinatorlayout.widget.CoordinatorLayout;
5 6
 import android.view.ViewGroup;
6 7
 
7 8
 import com.reactnativenavigation.anim.ModalAnimator;
@@ -35,7 +36,7 @@ public class ModalStack {
35 36
         this.presenter = presenter;
36 37
     }
37 38
 
38
-    public void setModalsLayout(ViewGroup modalsLayout) {
39
+    public void setModalsLayout(CoordinatorLayout modalsLayout) {
39 40
         presenter.setModalsLayout(modalsLayout);
40 41
     }
41 42
 

+ 17
- 19
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/navigator/Navigator.java 查看文件

@@ -1,12 +1,9 @@
1 1
 package com.reactnativenavigation.viewcontrollers.navigator;
2 2
 
3 3
 import android.app.Activity;
4
-import android.support.annotation.NonNull;
5
-import android.support.annotation.Nullable;
6
-import android.support.annotation.RestrictTo;
7 4
 import android.view.ViewGroup;
8
-import android.widget.FrameLayout;
9 5
 
6
+import com.facebook.react.ReactInstanceManager;
10 7
 import com.reactnativenavigation.parse.Options;
11 8
 import com.reactnativenavigation.presentation.OverlayManager;
12 9
 import com.reactnativenavigation.presentation.Presenter;
@@ -22,22 +19,27 @@ import com.reactnativenavigation.viewcontrollers.ViewController;
22 19
 import com.reactnativenavigation.viewcontrollers.modal.ModalStack;
23 20
 import com.reactnativenavigation.viewcontrollers.stack.StackController;
24 21
 
25
-import com.facebook.react.ReactInstanceManager;
26
-
27 22
 import java.util.Collection;
28 23
 import java.util.Collections;
29 24
 import java.util.List;
30 25
 
26
+import androidx.annotation.NonNull;
27
+import androidx.annotation.Nullable;
28
+import androidx.annotation.RestrictTo;
29
+import androidx.coordinatorlayout.widget.CoordinatorLayout;
30
+
31 31
 public class Navigator extends ParentController {
32 32
 
33 33
     private final ModalStack modalStack;
34 34
     private final OverlayManager overlayManager;
35 35
     private final RootPresenter rootPresenter;
36 36
     private ViewController root;
37
+
37 38
     private ViewController previousRoot;
38
-    private final FrameLayout rootLayout;
39
-    private final FrameLayout modalsLayout;
40
-    private final FrameLayout overlaysLayout;
39
+
40
+    private final CoordinatorLayout rootLayout;
41
+    private final CoordinatorLayout modalsLayout;
42
+    private final CoordinatorLayout overlaysLayout;
41 43
     private ViewGroup contentLayout;
42 44
     private Options defaultOptions = new Options();
43 45
 
@@ -52,7 +54,7 @@ public class Navigator extends ParentController {
52 54
         return defaultOptions;
53 55
     }
54 56
 
55
-    FrameLayout getRootLayout() {
57
+    CoordinatorLayout getRootLayout() {
56 58
         return rootLayout;
57 59
     }
58 60
 
@@ -72,9 +74,9 @@ public class Navigator extends ParentController {
72 74
         this.modalStack = modalStack;
73 75
         this.overlayManager = overlayManager;
74 76
         this.rootPresenter = rootPresenter;
75
-        rootLayout = new FrameLayout(getActivity());
76
-        modalsLayout = new FrameLayout(getActivity());
77
-        overlaysLayout = new FrameLayout(getActivity());
77
+        rootLayout = new CoordinatorLayout(getActivity());
78
+        modalsLayout = new CoordinatorLayout(getActivity());
79
+        overlaysLayout = new CoordinatorLayout(getActivity());
78 80
     }
79 81
 
80 82
     public void bindViews() {
@@ -143,14 +145,10 @@ public class Navigator extends ParentController {
143 145
         rootPresenter.setRoot(root, defaultOptions, new CommandListenerAdapter(commandListener) {
144 146
             @Override
145 147
             public void onSuccess(String childId) {
146
-                if (removeSplashView) removePreviousContentView();
148
+                if (removeSplashView) contentLayout.removeViewAt(0);
147 149
                 super.onSuccess(childId);
148 150
                 destroyPreviousRoot();
149 151
             }
150
-
151
-            private void removePreviousContentView() {
152
-                contentLayout.removeViewAt(0);
153
-            }
154 152
         }, reactInstanceManager);
155 153
     }
156 154
 
@@ -241,7 +239,7 @@ public class Navigator extends ParentController {
241 239
     }
242 240
 
243 241
     @RestrictTo(RestrictTo.Scope.TESTS)
244
-    FrameLayout getModalsLayout() {
242
+    CoordinatorLayout getModalsLayout() {
245 243
         return modalsLayout;
246 244
     }
247 245
 }

+ 53
- 52
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/sidemenu/SideMenuController.java 查看文件

@@ -1,16 +1,10 @@
1 1
 package com.reactnativenavigation.viewcontrollers.sidemenu;
2 2
 
3 3
 import android.app.Activity;
4
-import android.content.res.Resources;
5
-import android.support.annotation.NonNull;
6
-import android.support.v4.widget.DrawerLayout;
7
-import android.support.v4.widget.DrawerLayout.LayoutParams;
8
-import android.util.TypedValue;
9 4
 import android.view.Gravity;
10 5
 import android.view.View;
11 6
 
12 7
 import com.reactnativenavigation.parse.Options;
13
-import com.reactnativenavigation.parse.SideMenuOptions;
14 8
 import com.reactnativenavigation.parse.params.Bool;
15 9
 import com.reactnativenavigation.presentation.Presenter;
16 10
 import com.reactnativenavigation.presentation.SideMenuPresenter;
@@ -18,14 +12,19 @@ import com.reactnativenavigation.utils.CommandListener;
18 12
 import com.reactnativenavigation.viewcontrollers.ChildControllersRegistry;
19 13
 import com.reactnativenavigation.viewcontrollers.ParentController;
20 14
 import com.reactnativenavigation.viewcontrollers.ViewController;
21
-import com.reactnativenavigation.views.*;
15
+import com.reactnativenavigation.views.SideMenu;
16
+import com.reactnativenavigation.views.SideMenuRoot;
22 17
 
23 18
 import java.util.ArrayList;
24 19
 import java.util.Collection;
25 20
 
26
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
21
+import androidx.annotation.NonNull;
22
+import androidx.annotation.Nullable;
23
+import androidx.annotation.RestrictTo;
24
+import androidx.drawerlayout.widget.DrawerLayout;
25
+import androidx.drawerlayout.widget.DrawerLayout.LayoutParams;
27 26
 
28
-public class SideMenuController extends ParentController<DrawerLayout> implements DrawerLayout.DrawerListener {
27
+public class SideMenuController extends ParentController<SideMenuRoot> implements DrawerLayout.DrawerListener {
29 28
 
30 29
 	private ViewController center;
31 30
 	private ViewController left;
@@ -41,21 +40,26 @@ public class SideMenuController extends ParentController<DrawerLayout> implement
41 40
 
42 41
     @Override
43 42
     protected ViewController getCurrentChild() {
44
-	    if (getView().isDrawerOpen(Gravity.LEFT)) {
45
-            return left;
46
-        } else if (getView().isDrawerOpen(Gravity.RIGHT)) {
47
-            return right;
43
+        if (!isDestroyed()) {
44
+            if (getView().isDrawerOpen(Gravity.LEFT)) {
45
+                return left;
46
+            } else if (getView().isDrawerOpen(Gravity.RIGHT)) {
47
+                return right;
48
+            }
48 49
         }
49 50
         return center;
50 51
     }
51 52
 
52 53
     @NonNull
53 54
 	@Override
54
-	protected DrawerLayout createView() {
55
-        DrawerLayout sideMenu = new SideMenu(getActivity());
55
+	protected SideMenuRoot createView() {
56
+        SideMenu sideMenu = new SideMenu(getActivity());
56 57
         presenter.bindView(sideMenu);
57 58
         sideMenu.addDrawerListener(this);
58
-        return sideMenu;
59
+
60
+        SideMenuRoot root = new SideMenuRoot(getActivity());
61
+        root.addSideMenu(sideMenu, this);
62
+        return root;
59 63
 	}
60 64
 
61 65
     @Override
@@ -74,7 +78,7 @@ public class SideMenuController extends ParentController<DrawerLayout> implement
74 78
 	}
75 79
 
76 80
     @Override
77
-    public void applyChildOptions(Options options, Component child) {
81
+    public void applyChildOptions(Options options, ViewController child) {
78 82
         super.applyChildOptions(options, child);
79 83
         presenter.applyChildOptions(resolveCurrentOptions());
80 84
         performOnParentController(parentController ->
@@ -83,14 +87,21 @@ public class SideMenuController extends ParentController<DrawerLayout> implement
83 87
     }
84 88
 
85 89
     @Override
86
-    public void mergeChildOptions(Options options, ViewController childController, Component child) {
87
-        super.mergeChildOptions(options, childController, child);
90
+    public void mergeChildOptions(Options options, ViewController child) {
91
+        super.mergeChildOptions(options, child);
88 92
         presenter.mergeChildOptions(options.sideMenuRootOptions);
89 93
         performOnParentController(parentController ->
90
-                ((ParentController) parentController).mergeChildOptions(options.copy().clearSideMenuOptions(), childController, child)
94
+                ((ParentController) parentController).mergeChildOptions(options.copy().clearSideMenuOptions(), child)
91 95
         );
92 96
     }
93 97
 
98
+    @Override
99
+    public void onViewAppeared() {
100
+        super.onViewAppeared();
101
+        if (left != null) left.performOnView(view -> ((View) view).requestLayout());
102
+        if (right != null) right.performOnView(view -> ((View) view).requestLayout());
103
+    }
104
+
94 105
     @Override
95 106
     public void mergeOptions(Options options) {
96 107
         super.mergeOptions(options);
@@ -100,15 +111,15 @@ public class SideMenuController extends ParentController<DrawerLayout> implement
100 111
     @Override
101 112
     public Options resolveCurrentOptions() {
102 113
         Options options = super.resolveCurrentOptions();
103
-        if (getView().isDrawerOpen(Gravity.LEFT) || getView().isDrawerOpen(Gravity.RIGHT)) {
114
+        if (isDrawerOpen(Gravity.LEFT) || isDrawerOpen(Gravity.RIGHT)) {
104 115
             options = options.mergeWith(center.resolveCurrentOptions());
105 116
         }
106 117
         return options;
107 118
     }
108 119
 
109
-    //For onDrawerOpened and onDrawerClosed :
110
-    //Merge the options to the current state, if this happened due to a gesture we need to
111
-    //update the option state
120
+    public boolean isDrawerOpen(int gravity) {
121
+        return !isDestroyed() && getView().isDrawerOpen(gravity);
122
+    }
112 123
 
113 124
     @Override
114 125
     public void onDrawerOpened(@NonNull View drawerView) {
@@ -144,40 +155,25 @@ public class SideMenuController extends ParentController<DrawerLayout> implement
144 155
         return presenter.handleBack() || center.handleBack(listener) || super.handleBack(listener);
145 156
     }
146 157
 
158
+    @Nullable
159
+    @Override
160
+    public ViewController findController(View child) {
161
+        return getView().isSideMenu(child) ? this : super.findController(child);
162
+    }
163
+
147 164
     public void setCenterController(ViewController centerController) {
148
-		this.center = centerController;
149
-		View childView = centerController.getView();
150
-		getView().addView(childView);
165
+		center = centerController;
166
+        getView().setCenter(center);
151 167
 	}
152 168
 
153 169
     public void setLeftController(ViewController controller) {
154
-        this.left = controller;
155
-        int height = getHeight(options.sideMenuRootOptions.left);
156
-        int width = getWidth(options.sideMenuRootOptions.left);
157
-        getView().addView(controller.getView(), new LayoutParams(width, height, Gravity.LEFT));
170
+        left = controller;
171
+        getView().setLeft(left, options);
158 172
     }
159 173
 
160 174
     public void setRightController(ViewController controller) {
161
-        this.right = controller;
162
-        int height = getHeight(options.sideMenuRootOptions.right);
163
-        int width = getWidth(options.sideMenuRootOptions.right);
164
-        getView().addView(controller.getView(), new LayoutParams(width, height, Gravity.RIGHT));
165
-    }
166
-
167
-    private int getWidth(SideMenuOptions sideMenuOptions) {
168
-        int width = MATCH_PARENT;
169
-        if (sideMenuOptions.width.hasValue()) {
170
-            width = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, sideMenuOptions.width.get(), Resources.getSystem().getDisplayMetrics());
171
-        }
172
-        return width;
173
-    }
174
-
175
-    private int getHeight(SideMenuOptions sideMenuOptions) {
176
-        int height = MATCH_PARENT;
177
-        if (sideMenuOptions.height.hasValue()) {
178
-            height = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, sideMenuOptions.height.get(), Resources.getSystem().getDisplayMetrics());
179
-        }
180
-        return height;
175
+        right = controller;
176
+        getView().setRight(right, options);
181 177
     }
182 178
 
183 179
     private ViewController getMatchingView (View drawerView) {
@@ -203,10 +199,15 @@ public class SideMenuController extends ParentController<DrawerLayout> implement
203 199
     }
204 200
 
205 201
     private void dispatchSideMenuVisibilityEvents(ViewController drawer, float prevOffset, float offset) {
206
-        if (prevOffset == 0 && offset> 0) {
202
+        if (prevOffset == 0 && offset > 0) {
207 203
             drawer.onViewAppeared();
208 204
         } else if (prevOffset > 0 && offset == 0) {
209 205
             drawer.onViewDisappear();
210 206
         }
211 207
     }
208
+
209
+    @RestrictTo(RestrictTo.Scope.TESTS)
210
+    SideMenu getSideMenu() {
211
+        return presenter.getSideMenu();
212
+    }
212 213
 }

+ 7
- 7
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/stack/BackButtonHelper.java 查看文件

@@ -5,16 +5,16 @@ import com.reactnativenavigation.parse.params.Bool;
5 5
 import com.reactnativenavigation.viewcontrollers.ViewController;
6 6
 
7 7
 public class BackButtonHelper {
8
-    public void addToPushedChild(ViewController child) {
9
-        if (child.options.topBar.buttons.left != null || child.options.topBar.buttons.back.visible.isFalse()) return;
10
-        Options options = new Options();
11
-        options.topBar.buttons.back.setVisible();
12
-        child.mergeOptions(options);
13
-    }
14
-
15 8
     public void clear(ViewController child) {
16 9
         if (!child.options.topBar.buttons.back.hasValue()) {
17 10
             child.options.topBar.buttons.back.visible = new Bool(false);
18 11
         }
19 12
     }
13
+
14
+    void addToPushedChild(ViewController child) {
15
+        if (child.options.topBar.buttons.left != null || child.options.topBar.buttons.back.visible.isFalse()) return;
16
+        Options options = new Options();
17
+        options.topBar.buttons.back.setVisible();
18
+        child.mergeOptions(options);
19
+    }
20 20
 }

+ 41
- 33
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/stack/StackController.java 查看文件

@@ -1,13 +1,8 @@
1 1
 package com.reactnativenavigation.viewcontrollers.stack;
2 2
 
3 3
 import android.app.Activity;
4
-import android.support.annotation.NonNull;
5
-import android.support.annotation.RestrictTo;
6
-import android.support.annotation.VisibleForTesting;
7
-import android.support.v4.view.ViewPager;
8 4
 import android.view.View;
9 5
 import android.view.ViewGroup;
10
-import android.widget.RelativeLayout;
11 6
 
12 7
 import com.reactnativenavigation.anim.NavigationAnimator;
13 8
 import com.reactnativenavigation.parse.Options;
@@ -16,6 +11,7 @@ import com.reactnativenavigation.presentation.StackPresenter;
16 11
 import com.reactnativenavigation.react.Constants;
17 12
 import com.reactnativenavigation.utils.CommandListener;
18 13
 import com.reactnativenavigation.utils.CommandListenerAdapter;
14
+import com.reactnativenavigation.utils.CompatUtils;
19 15
 import com.reactnativenavigation.viewcontrollers.ChildControllersRegistry;
20 16
 import com.reactnativenavigation.viewcontrollers.IdStack;
21 17
 import com.reactnativenavigation.viewcontrollers.ParentController;
@@ -24,14 +20,22 @@ import com.reactnativenavigation.viewcontrollers.topbar.TopBarController;
24 20
 import com.reactnativenavigation.views.Component;
25 21
 import com.reactnativenavigation.views.ReactComponent;
26 22
 import com.reactnativenavigation.views.StackLayout;
23
+import com.reactnativenavigation.views.stack.StackBehaviour;
27 24
 import com.reactnativenavigation.views.topbar.TopBar;
28 25
 
29 26
 import java.util.Collection;
30 27
 import java.util.Iterator;
31 28
 import java.util.List;
32 29
 
33
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
30
+import androidx.annotation.NonNull;
31
+import androidx.annotation.RestrictTo;
32
+import androidx.annotation.VisibleForTesting;
33
+import androidx.coordinatorlayout.widget.CoordinatorLayout;
34
+import androidx.viewpager.widget.ViewPager;
35
+
34 36
 import static com.reactnativenavigation.utils.CollectionUtils.*;
37
+import static com.reactnativenavigation.utils.CoordinatorLayoutUtils.matchParentWithBehaviour;
38
+import static com.reactnativenavigation.utils.ObjectUtils.perform;
35 39
 
36 40
 public class StackController extends ParentController<StackLayout> {
37 41
 
@@ -49,8 +53,8 @@ public class StackController extends ParentController<StackLayout> {
49 53
         this.presenter = stackPresenter;
50 54
         stackPresenter.setButtonOnClickListener(this::onNavigationButtonPressed);
51 55
         for (ViewController child : children) {
52
-            stack.push(child.getId(), child);
53 56
             child.setParentController(this);
57
+            stack.push(child.getId(), child);
54 58
             if (size() > 1) backButtonHelper.addToPushedChild(child);
55 59
         }
56 60
     }
@@ -61,7 +65,7 @@ public class StackController extends ParentController<StackLayout> {
61 65
         if (getCurrentChild().isDestroyed()) return false;
62 66
         ViewGroup currentChild = getCurrentChild().getView();
63 67
         if (currentChild instanceof Component) {
64
-            return super.isRendered() && presenter.isRendered((Component) currentChild);
68
+            return super.isRendered() && presenter.isRendered(currentChild);
65 69
         }
66 70
         return super.isRendered();
67 71
     }
@@ -80,22 +84,22 @@ public class StackController extends ParentController<StackLayout> {
80 84
     @Override
81 85
     public void onAttachToParent() {
82 86
         if (!isEmpty() && !getCurrentChild().isDestroyed() && !isViewShown()) {
83
-            presenter.applyChildOptions(resolveCurrentOptions(), (Component) getCurrentChild().getView());
87
+            presenter.applyChildOptions(resolveCurrentOptions(), this, getCurrentChild());
84 88
         }
85 89
     }
86 90
 
87 91
     @Override
88 92
     public void mergeOptions(Options options) {
89
-        presenter.mergeOptions(options, (Component) getCurrentChild().getView());
93
+        presenter.mergeOptions(options, this, getCurrentChild());
90 94
         super.mergeOptions(options);
91 95
     }
92 96
 
93 97
     @Override
94
-    public void applyChildOptions(Options options, Component child) {
98
+    public void applyChildOptions(Options options, ViewController child) {
95 99
         super.applyChildOptions(options, child);
96
-        presenter.applyChildOptions(resolveCurrentOptions(), child);
97
-        if (child instanceof ReactComponent) {
98
-            fabOptionsPresenter.applyOptions(this.options.fabOptions, (ReactComponent) child, getView());
100
+        presenter.applyChildOptions(resolveCurrentOptions(), this, child);
101
+        if (child.getView() instanceof ReactComponent) {
102
+            fabOptionsPresenter.applyOptions(this.options.fabOptions, (ReactComponent) child.getView(), getView());
99 103
         }
100 104
         performOnParentController(parentController ->
101 105
                 ((ParentController) parentController).applyChildOptions(
@@ -111,10 +115,10 @@ public class StackController extends ParentController<StackLayout> {
111 115
     }
112 116
 
113 117
     @Override
114
-    public void mergeChildOptions(Options options, ViewController childController, Component child) {
115
-        super.mergeChildOptions(options, childController, child);
116
-        if (childController.isViewShown() && peek() == childController) {
117
-            presenter.mergeChildOptions(options, resolveCurrentOptions(), child);
118
+    public void mergeChildOptions(Options options, ViewController child) {
119
+        super.mergeChildOptions(options, child);
120
+        if (child.isViewShown() && peek() == child) {
121
+            presenter.mergeChildOptions(options, resolveCurrentOptions(), this, child);
118 122
             if (options.fabOptions.hasValue() && child instanceof ReactComponent) {
119 123
                 fabOptionsPresenter.mergeOptions(options.fabOptions, (ReactComponent) child, getView());
120 124
             }
@@ -127,14 +131,13 @@ public class StackController extends ParentController<StackLayout> {
127 131
                                 .clearFabOptions()
128 132
                                 .clearTopTabOptions()
129 133
                                 .clearTopTabsOptions(),
130
-                        childController,
131 134
                         child
132 135
                 )
133 136
         );
134 137
     }
135 138
 
136 139
     @Override
137
-    public void onChildDestroyed(Component child) {
140
+    public void onChildDestroyed(ViewController child) {
138 141
         super.onChildDestroyed(child);
139 142
         presenter.onChildDestroyed(child);
140 143
     }
@@ -173,11 +176,9 @@ public class StackController extends ParentController<StackLayout> {
173 176
     }
174 177
 
175 178
     private void addChildToStack(ViewController child, View view, Options resolvedOptions) {
176
-        view.setLayoutParams(new RelativeLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT));
177 179
         child.setWaitForRender(resolvedOptions.animations.push.waitForRender);
178
-        presenter.applyLayoutParamsOptions(resolvedOptions, view);
179 180
         if (size() == 1) presenter.applyInitialChildLayoutOptions(resolvedOptions);
180
-        getView().addView(view, getView().getChildCount() - 1);
181
+        getView().addView(view, getView().getChildCount() - 1, matchParentWithBehaviour(new StackBehaviour(this)));
181 182
     }
182 183
 
183 184
     public void setRoot(List<ViewController> children, CommandListener listener) {
@@ -235,16 +236,14 @@ public class StackController extends ParentController<StackLayout> {
235 236
         disappearing.onViewWillDisappear();
236 237
         appearing.onViewWillAppear();
237 238
 
238
-        Options resolvedOptions = resolveCurrentOptions();
239 239
         ViewGroup appearingView = appearing.getView();
240 240
         if (appearingView.getLayoutParams() == null) {
241
-            appearingView.setLayoutParams(new RelativeLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT));
242
-            presenter.applyLayoutParamsOptions(resolvedOptions, appearingView);
241
+            appearingView.setLayoutParams(matchParentWithBehaviour(new StackBehaviour(this)));
243 242
         }
244 243
         if (appearingView.getParent() == null) {
245 244
             getView().addView(appearingView, 0);
246 245
         }
247
-        presenter.onChildWillAppear(appearing.options, disappearing.options);
246
+        presenter.onChildWillAppear(this, appearing, disappearing);
248 247
         if (disappearingOptions.animations.pop.enabled.isTrueOrUndefined()) {
249 248
             animator.pop(disappearing.getView(), disappearingOptions.animations.pop, () -> finishPopping(disappearing, listener));
250 249
         } else {
@@ -329,7 +328,7 @@ public class StackController extends ParentController<StackLayout> {
329 328
     @Override
330 329
     protected StackLayout createView() {
331 330
         StackLayout stackLayout = new StackLayout(getActivity(), topBarController, getId());
332
-        presenter.bindView(topBarController.getView());
331
+        presenter.bindView(topBarController);
333 332
         addInitialChild(stackLayout);
334 333
         return stackLayout;
335 334
     }
@@ -337,11 +336,9 @@ public class StackController extends ParentController<StackLayout> {
337 336
     private void addInitialChild(StackLayout stackLayout) {
338 337
         if (isEmpty()) return;
339 338
         ViewGroup child = peek().getView();
340
-        child.setLayoutParams(new RelativeLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT));
341
-        Options options = resolveCurrentOptions();
342
-        presenter.applyLayoutParamsOptions(options, child);
343
-        presenter.applyInitialChildLayoutOptions(options);
344
-        stackLayout.addView(child, 0);
339
+        child.setId(CompatUtils.generateViewId());
340
+        presenter.applyInitialChildLayoutOptions(resolveCurrentOptions());
341
+        stackLayout.addView(child, 0, matchParentWithBehaviour(new StackBehaviour(this)));
345 342
     }
346 343
 
347 344
     private void onNavigationButtonPressed(String buttonId) {
@@ -373,6 +370,17 @@ public class StackController extends ParentController<StackLayout> {
373 370
         topBarController.clearTopTabs();
374 371
     }
375 372
 
373
+    @Override
374
+    public boolean onDependentViewChanged(CoordinatorLayout parent, ViewGroup child, View dependency) {
375
+        perform(findController(child), controller -> presenter.applyTopInsets(this, controller));
376
+        return false;
377
+    }
378
+
379
+    @Override
380
+    public int getTopInset(ViewController child) {
381
+        return resolveChildOptions(child).topBar.isHiddenOrDrawBehind() ? 0 : topBarController.getHeight();
382
+    }
383
+
376 384
     @RestrictTo(RestrictTo.Scope.TESTS)
377 385
     public TopBar getTopBar() {
378 386
         return topBarController.getView();

+ 3
- 1
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/stack/StackControllerBuilder.java 查看文件

@@ -11,7 +11,9 @@ import com.reactnativenavigation.viewcontrollers.ViewController;
11 11
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarController;
12 12
 import com.reactnativenavigation.views.element.ElementTransitionManager;
13 13
 
14
-import java.util.*;
14
+import java.util.ArrayList;
15
+import java.util.Arrays;
16
+import java.util.List;
15 17
 
16 18
 public class StackControllerBuilder {
17 19
     private Activity activity;

+ 66
- 8
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/topbar/TopBarController.java 查看文件

@@ -1,31 +1,53 @@
1 1
 package com.reactnativenavigation.viewcontrollers.topbar;
2 2
 
3 3
 import android.content.Context;
4
-import android.support.v4.view.ViewPager;
5 4
 import android.view.View;
6 5
 
6
+import com.reactnativenavigation.anim.TopBarAnimator;
7
+import com.reactnativenavigation.parse.AnimationOptions;
7 8
 import com.reactnativenavigation.utils.CompatUtils;
8 9
 import com.reactnativenavigation.views.StackLayout;
9 10
 import com.reactnativenavigation.views.topbar.TopBar;
10 11
 
12
+import androidx.annotation.VisibleForTesting;
13
+import androidx.viewpager.widget.ViewPager;
14
+
15
+import static com.reactnativenavigation.utils.ObjectUtils.perform;
16
+import static com.reactnativenavigation.utils.ViewUtils.isVisible;
17
+
11 18
 
12 19
 public class TopBarController {
13 20
     private TopBar topBar;
21
+    private TopBarAnimator animator;
22
+
23
+    public TopBarController() {
24
+        animator = new TopBarAnimator();
25
+    }
26
+
27
+    public TopBar getView() {
28
+        return topBar;
29
+    }
14 30
 
15
-    public View createView(Context context, StackLayout stackLayout) {
31
+    public int getHeight() {
32
+        return perform(topBar, 0, View::getHeight);
33
+    }
34
+
35
+    @VisibleForTesting
36
+    public void setAnimator(TopBarAnimator animator) {
37
+        this.animator = animator;
38
+    }
39
+
40
+    public TopBar createView(Context context, StackLayout parent) {
16 41
         if (topBar == null) {
17
-            topBar = createTopBar(context, stackLayout);
42
+            topBar = createTopBar(context, parent);
18 43
             topBar.setId(CompatUtils.generateViewId());
44
+            animator.bindView(topBar, parent);
19 45
         }
20 46
         return topBar;
21 47
     }
22 48
 
23 49
     protected TopBar createTopBar(Context context, StackLayout stackLayout) {
24
-        return new TopBar(context, stackLayout);
25
-    }
26
-
27
-    public TopBar getView() {
28
-        return topBar;
50
+        return new TopBar(context);
29 51
     }
30 52
 
31 53
     public void initTopTabs(ViewPager viewPager) {
@@ -35,4 +57,40 @@ public class TopBarController {
35 57
     public void clearTopTabs() {
36 58
         topBar.clearTopTabs();
37 59
     }
60
+
61
+    public void show() {
62
+        if (isVisible(topBar) || animator.isAnimatingShow()) return;
63
+        topBar.setVisibility(View.VISIBLE);
64
+    }
65
+
66
+    public void showAnimate(AnimationOptions options, int translationDy) {
67
+        if (isVisible(topBar) || animator.isAnimatingShow()) return;
68
+        animator.show(options, translationDy);
69
+    }
70
+
71
+    public void hide() {
72
+        if (!animator.isAnimatingHide()) {
73
+            topBar.setVisibility(View.GONE);
74
+        }
75
+    }
76
+
77
+    public void hideAnimate(AnimationOptions options, float translationStart, float translationEnd) {
78
+        hideAnimate(options, () -> {}, translationStart, translationEnd);
79
+    }
80
+
81
+    private void hideAnimate(AnimationOptions options, Runnable onAnimationEnd, float translationStart, float translationEnd) {
82
+        if (!isVisible(topBar)) return;
83
+        animator.hide(options, onAnimationEnd, translationStart, translationEnd);
84
+    }
85
+
86
+    public void resetViewProperties() {
87
+        topBar.setTranslationY(0);
88
+        topBar.setTranslationX(0);
89
+        topBar.setAlpha(1);
90
+        topBar.setScaleY(1);
91
+        topBar.setScaleX(1);
92
+        topBar.setRotationX(0);
93
+        topBar.setRotationY(0);
94
+        topBar.setRotation(0);
95
+    }
38 96
 }

+ 2
- 2
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/toptabs/TopTabsAdapter.java 查看文件

@@ -1,7 +1,7 @@
1 1
 package com.reactnativenavigation.viewcontrollers.toptabs;
2 2
 
3
-import android.support.v4.view.PagerAdapter;
4
-import android.support.v4.view.ViewPager;
3
+import androidx.viewpager.widget.PagerAdapter;
4
+import androidx.viewpager.widget.ViewPager;
5 5
 import android.view.View;
6 6
 import android.view.ViewGroup;
7 7
 

+ 5
- 6
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/toptabs/TopTabsController.java 查看文件

@@ -1,8 +1,8 @@
1 1
 package com.reactnativenavigation.viewcontrollers.toptabs;
2 2
 
3 3
 import android.app.Activity;
4
-import android.support.annotation.CallSuper;
5
-import android.support.annotation.NonNull;
4
+import androidx.annotation.CallSuper;
5
+import androidx.annotation.NonNull;
6 6
 import android.view.View;
7 7
 
8 8
 import com.reactnativenavigation.parse.Options;
@@ -12,7 +12,6 @@ import com.reactnativenavigation.viewcontrollers.ChildControllersRegistry;
12 12
 import com.reactnativenavigation.viewcontrollers.ParentController;
13 13
 import com.reactnativenavigation.viewcontrollers.ViewController;
14 14
 import com.reactnativenavigation.viewcontrollers.ViewVisibilityListenerAdapter;
15
-import com.reactnativenavigation.views.Component;
16 15
 import com.reactnativenavigation.views.toptabs.TopTabsLayoutCreator;
17 16
 import com.reactnativenavigation.views.toptabs.TopTabsViewPager;
18 17
 
@@ -88,14 +87,14 @@ public class TopTabsController extends ParentController<TopTabsViewPager> {
88 87
     }
89 88
 
90 89
     @Override
91
-    public void applyChildOptions(Options options, Component child) {
90
+    public void applyChildOptions(Options options, ViewController child) {
92 91
         super.applyChildOptions(options, child);
93 92
         performOnParentController(parentController -> ((ParentController) parentController).applyChildOptions(this.options.copy(), child));
94 93
     }
95 94
 
96 95
     @CallSuper
97
-    public void mergeChildOptions(Options options, ViewController childController, Component child) {
98
-        super.mergeChildOptions(options, childController, child);
96
+    public void mergeChildOptions(Options options, ViewController child) {
97
+        super.mergeChildOptions(options, child);
99 98
         performOnParentController(parentController -> ((ParentController) parentController).applyChildOptions(options.copy(), child));
100 99
     }
101 100
 

+ 19
- 0
lib/android/app/src/main/java/com/reactnativenavigation/views/BehaviourAdapter.java 查看文件

@@ -0,0 +1,19 @@
1
+package com.reactnativenavigation.views;
2
+
3
+import androidx.coordinatorlayout.widget.CoordinatorLayout;
4
+import android.view.View;
5
+import android.view.ViewGroup;
6
+
7
+public interface BehaviourAdapter<V extends ViewGroup> {
8
+    /**
9
+     * @see CoordinatorLayout.Behavior#onMeasureChild
10
+     * @return true if the Behavior measured the child view, false if the CoordinatorLayout should perform its default measurement
11
+     */
12
+    boolean onMeasureChild(CoordinatorLayout parent, V child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed);
13
+
14
+    /**
15
+     * @see CoordinatorLayout.Behavior#onDependentViewChanged
16
+     * @return true if the Behavior changed the child view's size or position, false otherwise
17
+     */
18
+    boolean onDependentViewChanged(CoordinatorLayout parent, V child, View dependency);
19
+}

+ 24
- 0
lib/android/app/src/main/java/com/reactnativenavigation/views/BehaviourDelegate.java 查看文件

@@ -0,0 +1,24 @@
1
+package com.reactnativenavigation.views;
2
+
3
+import androidx.coordinatorlayout.widget.CoordinatorLayout;
4
+import android.view.View;
5
+import android.view.ViewGroup;
6
+
7
+public class BehaviourDelegate<V extends ViewGroup> extends CoordinatorLayout.Behavior<V> {
8
+
9
+    private BehaviourAdapter delegate;
10
+
11
+    public BehaviourDelegate(BehaviourAdapter<V> delegate) {
12
+        this.delegate = delegate;
13
+    }
14
+
15
+    @Override
16
+    public boolean onDependentViewChanged(CoordinatorLayout parent, V child, View dependency) {
17
+        return delegate.onDependentViewChanged(parent, child, dependency);
18
+    }
19
+
20
+    @Override
21
+    public boolean onMeasureChild(CoordinatorLayout parent, V child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) {
22
+        return delegate.onMeasureChild(parent, child, parentWidthMeasureSpec, widthUsed, parentHeightMeasureSpec, heightUsed);
23
+    }
24
+}

+ 10
- 11
lib/android/app/src/main/java/com/reactnativenavigation/views/BottomTabs.java 查看文件

@@ -3,13 +3,15 @@ package com.reactnativenavigation.views;
3 3
 import android.annotation.SuppressLint;
4 4
 import android.content.Context;
5 5
 import android.graphics.drawable.Drawable;
6
-import android.support.annotation.IntRange;
7 6
 import android.widget.LinearLayout;
8 7
 
9 8
 import com.aurelhubert.ahbottomnavigation.AHBottomNavigation;
10 9
 import com.aurelhubert.ahbottomnavigation.AHBottomNavigationItem;
10
+import com.reactnativenavigation.BuildConfig;
11
+import com.reactnativenavigation.R;
11 12
 import com.reactnativenavigation.parse.LayoutDirection;
12
-import com.reactnativenavigation.utils.CompatUtils;
13
+
14
+import androidx.annotation.IntRange;
13 15
 
14 16
 import static com.reactnativenavigation.utils.ViewUtils.findChildByClass;
15 17
 
@@ -18,6 +20,12 @@ public class BottomTabs extends AHBottomNavigation {
18 20
     private boolean itemsCreationEnabled = true;
19 21
     private boolean shouldCreateItems = true;
20 22
 
23
+    public BottomTabs(Context context) {
24
+        super(context);
25
+        setId(R.id.bottomTabs);
26
+        if (BuildConfig.DEBUG) setContentDescription("BottomTabs");
27
+    }
28
+
21 29
     public void disableItemsCreation() {
22 30
         itemsCreationEnabled = false;
23 31
     }
@@ -27,11 +35,6 @@ public class BottomTabs extends AHBottomNavigation {
27 35
         if (shouldCreateItems) createItems();
28 36
     }
29 37
 
30
-    public BottomTabs(Context context) {
31
-        super(context);
32
-        setId(CompatUtils.generateViewId());
33
-    }
34
-
35 38
     @Override
36 39
     protected void createItems() {
37 40
         if (itemsCreationEnabled) {
@@ -50,10 +53,6 @@ public class BottomTabs extends AHBottomNavigation {
50 53
         super.createItems();
51 54
     }
52 55
 
53
-    public void setBadge(int bottomTabIndex, String badge) {
54
-        setNotification(badge, bottomTabIndex);
55
-    }
56
-
57 56
     @Override
58 57
     public void setCurrentItem(@IntRange(from = 0) int position) {
59 58
         super.setCurrentItem(position);

+ 0
- 4
lib/android/app/src/main/java/com/reactnativenavigation/views/Component.java 查看文件

@@ -1,9 +1,5 @@
1 1
 package com.reactnativenavigation.views;
2 2
 
3
-import com.reactnativenavigation.views.topbar.TopBar;
4
-
5 3
 public interface Component extends Renderable {
6
-    void drawBehindTopBar();
7 4
 
8
-    void drawBelowTopBar(TopBar topBar);
9 5
 }

+ 5
- 31
lib/android/app/src/main/java/com/reactnativenavigation/views/ComponentLayout.java 查看文件

@@ -4,25 +4,23 @@ import android.annotation.SuppressLint;
4 4
 import android.content.Context;
5 5
 import android.view.MotionEvent;
6 6
 import android.view.View;
7
-import android.widget.FrameLayout;
8
-import android.widget.RelativeLayout;
9 7
 
10 8
 import com.reactnativenavigation.interfaces.ScrollEventListener;
11 9
 import com.reactnativenavigation.parse.Options;
12 10
 import com.reactnativenavigation.parse.params.Bool;
13
-import com.reactnativenavigation.utils.ViewUtils;
14 11
 import com.reactnativenavigation.viewcontrollers.IReactView;
15 12
 import com.reactnativenavigation.viewcontrollers.TitleBarButtonController;
16 13
 import com.reactnativenavigation.views.element.Element;
17
-import com.reactnativenavigation.views.topbar.TopBar;
18 14
 import com.reactnativenavigation.views.touch.OverlayTouchDelegate;
19 15
 
20 16
 import java.util.List;
21 17
 
22
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
18
+import androidx.coordinatorlayout.widget.CoordinatorLayout;
19
+
20
+import static com.reactnativenavigation.utils.CoordinatorLayoutUtils.matchParentLP;
23 21
 
24 22
 @SuppressLint("ViewConstructor")
25
-public class ComponentLayout extends FrameLayout implements ReactComponent, TitleBarButtonController.OnClickListener {
23
+public class ComponentLayout extends CoordinatorLayout implements ReactComponent, TitleBarButtonController.OnClickListener {
26 24
 
27 25
     private IReactView reactView;
28 26
     private final OverlayTouchDelegate touchDelegate;
@@ -30,7 +28,7 @@ public class ComponentLayout extends FrameLayout implements ReactComponent, Titl
30 28
     public ComponentLayout(Context context, IReactView reactView) {
31 29
 		super(context);
32 30
 		this.reactView = reactView;
33
-        addView(reactView.asView(), MATCH_PARENT, MATCH_PARENT);
31
+        addView(reactView.asView(), matchParentLP());
34 32
         touchDelegate = new OverlayTouchDelegate(reactView);
35 33
     }
36 34
 
@@ -82,30 +80,6 @@ public class ComponentLayout extends FrameLayout implements ReactComponent, Titl
82 80
         reactView.dispatchTouchEventToJs(event);
83 81
     }
84 82
 
85
-    @Override
86
-    public void drawBehindTopBar() {
87
-        if (getLayoutParams() instanceof RelativeLayout.LayoutParams) {
88
-            RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) getLayoutParams();
89
-            layoutParams.topMargin = 0;
90
-            setLayoutParams(layoutParams);
91
-        }
92
-    }
93
-
94
-    @Override
95
-    public void drawBelowTopBar(TopBar topBar) {
96
-        if (getLayoutParams() instanceof RelativeLayout.LayoutParams) {
97
-            RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) getLayoutParams();
98
-            if (topBar.getLayoutParams() instanceof MarginLayoutParams) {
99
-                layoutParams.topMargin = ViewUtils.getHeight(topBar) + ((MarginLayoutParams) topBar.getLayoutParams()).topMargin;
100
-            } else {
101
-                layoutParams.topMargin = ViewUtils.getHeight(topBar);
102
-            }
103
-            try {
104
-                setLayoutParams(layoutParams);
105
-            } catch (IllegalStateException ignored) { }
106
-        }
107
-    }
108
-
109 83
     @Override
110 84
     public boolean isRendered() {
111 85
         return reactView.isRendered();

+ 2
- 25
lib/android/app/src/main/java/com/reactnativenavigation/views/ExternalComponentLayout.java 查看文件

@@ -2,37 +2,14 @@ package com.reactnativenavigation.views;
2 2
 
3 3
 import android.annotation.SuppressLint;
4 4
 import android.content.Context;
5
-import android.widget.FrameLayout;
6
-import android.widget.RelativeLayout;
7
-
8
-import com.reactnativenavigation.views.topbar.TopBar;
9
-
10
-import static android.widget.RelativeLayout.BELOW;
5
+import androidx.coordinatorlayout.widget.CoordinatorLayout;
11 6
 
12 7
 @SuppressLint("ViewConstructor")
13
-public class ExternalComponentLayout extends FrameLayout implements Component {
8
+public class ExternalComponentLayout extends CoordinatorLayout implements Component {
14 9
     public ExternalComponentLayout(Context context) {
15 10
 		super(context);
16 11
     }
17 12
 
18
-    @Override
19
-    public void drawBehindTopBar() {
20
-        if (getParent() instanceof RelativeLayout) {
21
-            RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) getLayoutParams();
22
-            layoutParams.removeRule(BELOW);
23
-            setLayoutParams(layoutParams);
24
-        }
25
-    }
26
-
27
-    @Override
28
-    public void drawBelowTopBar(TopBar topBar) {
29
-        if (getParent() instanceof RelativeLayout) {
30
-            RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) getLayoutParams();
31
-            layoutParams.addRule(BELOW, topBar.getId());
32
-            setLayoutParams(layoutParams);
33
-        }
34
-    }
35
-
36 13
     @Override
37 14
     public boolean isRendered() {
38 15
         return getChildCount() >= 1;

+ 1
- 1
lib/android/app/src/main/java/com/reactnativenavigation/views/Fab.java 查看文件

@@ -4,7 +4,7 @@ import android.content.Context;
4 4
 import android.graphics.PorterDuff;
5 5
 import android.graphics.PorterDuffColorFilter;
6 6
 import android.graphics.drawable.Drawable;
7
-import android.support.annotation.NonNull;
7
+import androidx.annotation.NonNull;
8 8
 
9 9
 import com.github.clans.fab.FloatingActionButton;
10 10
 import com.reactnativenavigation.anim.FabAnimator;

+ 3
- 2
lib/android/app/src/main/java/com/reactnativenavigation/views/SideMenu.java 查看文件

@@ -1,10 +1,11 @@
1 1
 package com.reactnativenavigation.views;
2 2
 
3 3
 import android.content.Context;
4
-import android.support.annotation.NonNull;
5
-import android.support.v4.widget.DrawerLayout;
6 4
 import android.util.Log;
7 5
 
6
+import androidx.annotation.NonNull;
7
+import androidx.drawerlayout.widget.DrawerLayout;
8
+
8 9
 public class SideMenu extends DrawerLayout {
9 10
     public SideMenu(@NonNull Context context) {
10 11
         super(context);

+ 0
- 0
lib/android/app/src/main/java/com/reactnativenavigation/views/SideMenuRoot.java 查看文件


部分文件因文件數量過多而無法顯示