Browse Source

Apply resolved options each time a child controller appears

This commit fixes #4045, which revealed a design flaw in the options applying logic.
Child options are applied when a ViewController.onViewAppeared is invoked.
A ViewController's appearance is determined from a GlobalLayoutListener.

Consider the following layout hierarchy:
BottomTabs > Stack > Component

onViewAppeared will be triggered for the BottomTabs first, then for the Stack and last for the component.
This means that if the Stack and the Component options will be applied consecutively which in some cases
might case flickering, mostly when drawBehind options are applied.
Guy Carmeli 6 years ago
parent
commit
c5b77de568

+ 1
- 1
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/ParentController.java View File

@@ -96,7 +96,7 @@ public abstract class ParentController<T extends ViewGroup> extends ChildControl
96 96
 
97 97
     @CallSuper
98 98
     public void applyChildOptions(Options options, Component child) {
99
-        this.options = this.initialOptions.mergeWith(options);
99
+        this.options = initialOptions.mergeWith(options);
100 100
         if (isRoot()) {
101 101
             presenter.applyRootOptions(getView(), options);
102 102
         }

+ 7
- 2
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/bottomtabs/BottomTabsController.java View File

@@ -91,9 +91,14 @@ public class BottomTabsController extends ParentController implements AHBottomNa
91 91
     @Override
92 92
     public void applyChildOptions(Options options, Component child) {
93 93
         super.applyChildOptions(options, child);
94
-        presenter.applyChildOptions(this.options, child);
94
+        presenter.applyChildOptions(resolveCurrentOptions(), child);
95 95
         performOnParentController(parentController ->
96
-                ((ParentController) parentController).applyChildOptions(this.options.copy().clearBottomTabsOptions().clearBottomTabOptions(), child)
96
+                ((ParentController) parentController).applyChildOptions(
97
+                        this.options.copy()
98
+                                .clearBottomTabsOptions()
99
+                                .clearBottomTabOptions(),
100
+                        child
101
+                )
97 102
         );
98 103
     }
99 104
 

+ 42
- 5
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/BottomTabsControllerTest.java View File

@@ -11,8 +11,6 @@ import com.reactnativenavigation.BaseTest;
11 11
 import com.reactnativenavigation.TestUtils;
12 12
 import com.reactnativenavigation.mocks.ImageLoaderMock;
13 13
 import com.reactnativenavigation.mocks.SimpleViewController;
14
-import com.reactnativenavigation.mocks.TitleBarReactViewCreatorMock;
15
-import com.reactnativenavigation.mocks.TopBarButtonCreatorMock;
16 14
 import com.reactnativenavigation.parse.Options;
17 15
 import com.reactnativenavigation.parse.params.Bool;
18 16
 import com.reactnativenavigation.parse.params.Colour;
@@ -21,7 +19,6 @@ import com.reactnativenavigation.parse.params.Text;
21 19
 import com.reactnativenavigation.presentation.BottomTabOptionsPresenter;
22 20
 import com.reactnativenavigation.presentation.BottomTabsOptionsPresenter;
23 21
 import com.reactnativenavigation.presentation.OptionsPresenter;
24
-import com.reactnativenavigation.presentation.StackOptionsPresenter;
25 22
 import com.reactnativenavigation.react.EventEmitter;
26 23
 import com.reactnativenavigation.utils.CommandListenerAdapter;
27 24
 import com.reactnativenavigation.utils.ImageLoader;
@@ -36,9 +33,12 @@ import org.junit.Test;
36 33
 import org.mockito.ArgumentCaptor;
37 34
 import org.mockito.Mockito;
38 35
 
36
+import java.util.ArrayList;
39 37
 import java.util.Arrays;
40 38
 import java.util.List;
41 39
 
40
+import edu.emory.mathcs.backport.java.util.Collections;
41
+
42 42
 import static com.reactnativenavigation.TestUtils.hideBackButton;
43 43
 import static org.assertj.core.api.Java6Assertions.assertThat;
44 44
 import static org.mockito.ArgumentMatchers.any;
@@ -211,6 +211,37 @@ public class BottomTabsControllerTest extends BaseTest {
211 211
         assertThat(childLayoutParams(0).bottomMargin).isEqualTo(0);
212 212
     }
213 213
 
214
+    @Test
215
+    public void applyChildOptions_resolvedOptionsAreUsed() {
216
+        Options childOptions = new Options();
217
+        SimpleViewController pushedScreen = new SimpleViewController(activity, childRegistry, "child4.1", childOptions);
218
+        disablePushAnimation(pushedScreen);
219
+        child4 = createStack(pushedScreen);
220
+
221
+        tabs = new ArrayList<>(Collections.singletonList(child4));
222
+
223
+        initialOptions.bottomTabsOptions.currentTabIndex = new Number(3);
224
+        Options resolvedOptions = new Options();
225
+        uut = new BottomTabsController(activity,
226
+                tabs,
227
+                childRegistry,
228
+                eventEmitter,
229
+                imageLoaderMock,
230
+                "uut",
231
+                initialOptions,
232
+                new OptionsPresenter(activity, new Options()),
233
+                presenter,
234
+                new BottomTabOptionsPresenter(activity, tabs, new Options())) {
235
+            @Override
236
+            public Options resolveCurrentOptions() {
237
+                return resolvedOptions;
238
+            }
239
+        };
240
+
241
+        activity.setContentView(uut.getView());
242
+        verify(presenter, times(2)).applyChildOptions(eq(resolvedOptions), any());
243
+    }
244
+
214 245
     @Test
215 246
     public void child_mergeOptions_currentTabIndex() {
216 247
         uut.ensureViewIsCreated();
@@ -285,7 +316,13 @@ public class BottomTabsControllerTest extends BaseTest {
285 316
         return TestUtils.newStackController(activity)
286 317
                 .setId(id)
287 318
                 .setInitialOptions(tabOptions)
288
-                .setStackPresenter(new StackOptionsPresenter(activity, new TitleBarReactViewCreatorMock(), new TopBarButtonCreatorMock(), new ImageLoader(), new Options()))
319
+                .build();
320
+    }
321
+
322
+    private StackController createStack(ViewController initialChild) {
323
+        return TestUtils.newStackController(activity)
324
+                .setInitialOptions(tabOptions)
325
+                .setChildren(new ArrayList<>(Collections.singleton(initialChild)))
289 326
                 .build();
290 327
     }
291 328
 
@@ -293,7 +330,7 @@ public class BottomTabsControllerTest extends BaseTest {
293 330
         return (ViewGroup.MarginLayoutParams) tabs.get(index).getView().getLayoutParams();
294 331
     }
295 332
 
296
-    public BottomTabsController createBottomTabs() {
333
+    private BottomTabsController createBottomTabs() {
297 334
         return new BottomTabsController(activity,
298 335
                 tabs,
299 336
                 childRegistry,

+ 11
- 1
playground/src/screens/ComplexLayout.js View File

@@ -64,7 +64,12 @@ class ComplexLayout extends Component {
64 64
                 children: [
65 65
                   {
66 66
                     component: {
67
-                      name: 'navigation.playground.TextScreen'
67
+                      name: 'navigation.playground.TextScreen',
68
+                      options: {
69
+                        topBar: {
70
+                          animate: false
71
+                        }
72
+                      }
68 73
                     }
69 74
                   }
70 75
                 ],
@@ -81,6 +86,11 @@ class ComplexLayout extends Component {
81 86
                 left: {
82 87
                   component: {
83 88
                     name: 'navigation.playground.SideMenuScreen',
89
+                    options: {
90
+                      topBar: {
91
+                        animate: false
92
+                      }
93
+                    },
84 94
                     passProps: {
85 95
                       side: 'left'
86 96
                     }

+ 3
- 1
playground/src/screens/WelcomeScreen.js View File

@@ -79,6 +79,7 @@ class WelcomeScreen extends Component {
79 79
                       options: {
80 80
                         topBar: {
81 81
                           visible: true,
82
+                          animate: false,
82 83
                           title: {
83 84
                             text: 'React Native Navigation!'
84 85
                           }
@@ -130,7 +131,8 @@ class WelcomeScreen extends Component {
130 131
                 },
131 132
                 options: {
132 133
                   topBar: {
133
-                    visible: true
134
+                    visible: true,
135
+                    animate: false
134 136
                   },
135 137
                   bottomTab: {
136 138
                     text: 'Tab 3',