Browse Source

Mount all stack children (#6194)

This commit somewhat consolidates how stack children are mounted.

On Android, until now, only the top child in a stack was mounted when the stack became visible or when setStackRoot() was called. When popping the stack the new current child would be mounted when it was attached. This caused flickering when popping the stack as the pop animation started before the child finished mounting.

Now all children will be mounted together after the top child is mounted :)

Co-authored-by: Yogev Ben David <yogev132@gmail.com>
Guy Carmeli 4 years ago
parent
commit
a1beebe74b
No account linked to committer's email address

+ 1
- 1
lib/android/app/src/main/java/com/reactnativenavigation/react/ReactView.java View File

42
         start();
42
         start();
43
     }
43
     }
44
 
44
 
45
-    private void start() {
45
+    public void start() {
46
         if (isAttachedToReactInstance) return;
46
         if (isAttachedToReactInstance) return;
47
         isAttachedToReactInstance = true;
47
         isAttachedToReactInstance = true;
48
 		final Bundle opts = new Bundle();
48
 		final Bundle opts = new Bundle();

+ 5
- 5
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/ComponentViewController.java View File

9
 import com.reactnativenavigation.presentation.Presenter;
9
 import com.reactnativenavigation.presentation.Presenter;
10
 import com.reactnativenavigation.utils.StatusBarUtils;
10
 import com.reactnativenavigation.utils.StatusBarUtils;
11
 import com.reactnativenavigation.views.ComponentLayout;
11
 import com.reactnativenavigation.views.ComponentLayout;
12
-import com.reactnativenavigation.views.ReactComponent;
13
 
12
 
14
 import androidx.annotation.NonNull;
13
 import androidx.annotation.NonNull;
15
 import androidx.core.view.ViewCompat;
14
 import androidx.core.view.ViewCompat;
22
     private ComponentPresenter presenter;
21
     private ComponentPresenter presenter;
23
     private final ReactViewCreator viewCreator;
22
     private final ReactViewCreator viewCreator;
24
 
23
 
25
-    ReactComponent getComponent() {
26
-        return view;
27
-    }
28
-
29
     public ComponentViewController(final Activity activity,
24
     public ComponentViewController(final Activity activity,
30
                                    final ChildControllersRegistry childRegistry,
25
                                    final ChildControllersRegistry childRegistry,
31
                                    final String id,
26
                                    final String id,
40
         this.presenter = componentPresenter;
35
         this.presenter = componentPresenter;
41
     }
36
     }
42
 
37
 
38
+    @Override
39
+    public void start() {
40
+        if (!isDestroyed()) getView().start();
41
+    }
42
+
43
     @Override
43
     @Override
44
     public String getCurrentComponentName() {
44
     public String getCurrentComponentName() {
45
         return this.componentName;
45
         return this.componentName;

+ 6
- 7
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/IdStack.java View File

1
 package com.reactnativenavigation.viewcontrollers;
1
 package com.reactnativenavigation.viewcontrollers;
2
 
2
 
3
-import androidx.annotation.NonNull;
4
-
5
 import com.reactnativenavigation.utils.StringUtils;
3
 import com.reactnativenavigation.utils.StringUtils;
6
 
4
 
7
 import java.util.ArrayList;
5
 import java.util.ArrayList;
8
-import java.util.Collection;
9
 import java.util.HashMap;
6
 import java.util.HashMap;
10
 import java.util.Iterator;
7
 import java.util.Iterator;
8
+import java.util.List;
11
 import java.util.Map;
9
 import java.util.Map;
12
 
10
 
13
-import static com.reactnativenavigation.utils.CollectionUtils.last;
14
-import static com.reactnativenavigation.utils.CollectionUtils.removeLast;
11
+import androidx.annotation.NonNull;
12
+
13
+import static com.reactnativenavigation.utils.CollectionUtils.*;
15
 
14
 
16
 public class IdStack<E> implements Iterable<String> {
15
 public class IdStack<E> implements Iterable<String> {
17
 
16
 
84
 	}
83
 	}
85
 
84
 
86
 
85
 
87
-	public Collection<E> values() {
88
-		return map.values();
86
+	public List<E> values() {
87
+		return map(deque, map::get);
89
 	}
88
 	}
90
 
89
 
91
     public void remove(Iterator<String> iterator, String id) {
90
     public void remove(Iterator<String> iterator, String id) {

+ 4
- 0
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/ViewController.java View File

339
         );
339
         );
340
     }
340
     }
341
 
341
 
342
+    public void start() {
343
+
344
+    }
345
+
342
     void applyOnController(ViewController controller, Func1<ViewController> task) {
346
     void applyOnController(ViewController controller, Func1<ViewController> task) {
343
         if (controller != null) task.run(controller);
347
         if (controller != null) task.run(controller);
344
     }
348
     }

+ 10
- 0
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/stack/StackController.java View File

26
 import com.reactnativenavigation.views.stack.StackBehaviour;
26
 import com.reactnativenavigation.views.stack.StackBehaviour;
27
 import com.reactnativenavigation.views.topbar.TopBar;
27
 import com.reactnativenavigation.views.topbar.TopBar;
28
 
28
 
29
+import java.util.ArrayList;
29
 import java.util.Collection;
30
 import java.util.Collection;
30
 import java.util.Iterator;
31
 import java.util.Iterator;
31
 import java.util.List;
32
 import java.util.List;
214
                             backButtonHelper.addToPushedChild(children.get(i));
215
                             backButtonHelper.addToPushedChild(children.get(i));
215
                         }
216
                         }
216
                     }
217
                     }
218
+                    startChildrenBellowTopChild();
217
                 }
219
                 }
218
                 listener.onSuccess(childId);
220
                 listener.onSuccess(childId);
219
             }
221
             }
360
         if (isEmpty()) return;
362
         if (isEmpty()) return;
361
         ViewGroup child = peek().getView();
363
         ViewGroup child = peek().getView();
362
         child.setId(CompatUtils.generateViewId());
364
         child.setId(CompatUtils.generateViewId());
365
+        peek().addOnAppearedListener(this::startChildrenBellowTopChild);
363
         presenter.applyInitialChildLayoutOptions(resolveCurrentOptions());
366
         presenter.applyInitialChildLayoutOptions(resolveCurrentOptions());
364
         stackLayout.addView(child, 0, matchParentWithBehaviour(new StackBehaviour(this)));
367
         stackLayout.addView(child, 0, matchParentWithBehaviour(new StackBehaviour(this)));
365
     }
368
     }
366
 
369
 
370
+    private void startChildrenBellowTopChild() {
371
+        ArrayList<ViewController> children = new ArrayList(getChildControllers());
372
+        for (int i = children.size() - 2; i >= 0; i--) {
373
+            children.get(i).start();
374
+        }
375
+    }
376
+
367
     private void onNavigationButtonPressed(String buttonId) {
377
     private void onNavigationButtonPressed(String buttonId) {
368
         if (Constants.BACK_BUTTON_ID.equals(buttonId)) {
378
         if (Constants.BACK_BUTTON_ID.equals(buttonId)) {
369
             pop(Options.EMPTY, new CommandListenerAdapter());
379
             pop(Options.EMPTY, new CommandListenerAdapter());

+ 4
- 0
lib/android/app/src/main/java/com/reactnativenavigation/views/ComponentLayout.java View File

45
         reactView.destroy();
45
         reactView.destroy();
46
     }
46
     }
47
 
47
 
48
+    public void start() {
49
+        reactView.start();
50
+    }
51
+
48
 	public void sendComponentStart() {
52
 	public void sendComponentStart() {
49
 		reactView.sendComponentStart(ComponentType.Component);
53
 		reactView.sendComponentStart(ComponentType.Component);
50
 	}
54
 	}

+ 1
- 1
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/IdStackTest.java View File

109
         assertThat(uut.values()).isNotNull().isEmpty();
109
         assertThat(uut.values()).isNotNull().isEmpty();
110
         uut.push("123", 123);
110
         uut.push("123", 123);
111
         uut.push("456", 456);
111
         uut.push("456", 456);
112
-        assertThat(uut.values()).isNotNull().containsExactlyInAnyOrder(123, 456);
112
+        assertThat(uut.values()).isNotNull().containsSequence(123, 456);
113
     }
113
     }
114
 }
114
 }

+ 28
- 4
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/stack/StackControllerTest.java View File

12
 import com.reactnativenavigation.anim.NavigationAnimator;
12
 import com.reactnativenavigation.anim.NavigationAnimator;
13
 import com.reactnativenavigation.mocks.ImageLoaderMock;
13
 import com.reactnativenavigation.mocks.ImageLoaderMock;
14
 import com.reactnativenavigation.mocks.SimpleViewController;
14
 import com.reactnativenavigation.mocks.SimpleViewController;
15
+import com.reactnativenavigation.mocks.TitleBarButtonCreatorMock;
15
 import com.reactnativenavigation.mocks.TitleBarReactViewCreatorMock;
16
 import com.reactnativenavigation.mocks.TitleBarReactViewCreatorMock;
16
 import com.reactnativenavigation.mocks.TopBarBackgroundViewCreatorMock;
17
 import com.reactnativenavigation.mocks.TopBarBackgroundViewCreatorMock;
17
-import com.reactnativenavigation.mocks.TitleBarButtonCreatorMock;
18
 import com.reactnativenavigation.parse.AnimationOptions;
18
 import com.reactnativenavigation.parse.AnimationOptions;
19
 import com.reactnativenavigation.parse.NestedAnimationsOptions;
19
 import com.reactnativenavigation.parse.NestedAnimationsOptions;
20
 import com.reactnativenavigation.parse.Options;
20
 import com.reactnativenavigation.parse.Options;
59
 
59
 
60
 import androidx.coordinatorlayout.widget.CoordinatorLayout;
60
 import androidx.coordinatorlayout.widget.CoordinatorLayout;
61
 
61
 
62
+import static com.reactnativenavigation.utils.ObjectUtils.take;
62
 import static com.reactnativenavigation.utils.ViewUtils.topMargin;
63
 import static com.reactnativenavigation.utils.ViewUtils.topMargin;
63
 import static org.assertj.core.api.Java6Assertions.assertThat;
64
 import static org.assertj.core.api.Java6Assertions.assertThat;
64
 import static org.mockito.ArgumentMatchers.any;
65
 import static org.mockito.ArgumentMatchers.any;
82
     private ViewController child1a;
83
     private ViewController child1a;
83
     private ViewController child2;
84
     private ViewController child2;
84
     private ViewController child3;
85
     private ViewController child3;
86
+    private SimpleViewController.SimpleView child3View;
85
     private ViewController child4;
87
     private ViewController child4;
86
     private NavigationAnimator animator;
88
     private NavigationAnimator animator;
87
     private TopBarController topBarController;
89
     private TopBarController topBarController;
108
                     new Options()
110
                     new Options()
109
                 )
111
                 )
110
         );
112
         );
113
+        createChildren();
114
+        uut = createStack();
115
+        activity.setContentView(uut.getView());
116
+    }
117
+
118
+    private void createChildren() {
111
         child1 = spy(new SimpleViewController(activity, childRegistry, "child1", new Options()));
119
         child1 = spy(new SimpleViewController(activity, childRegistry, "child1", new Options()));
112
         child1a = spy(new SimpleViewController(activity, childRegistry, "child1", new Options()));
120
         child1a = spy(new SimpleViewController(activity, childRegistry, "child1", new Options()));
113
         child2 = spy(new SimpleViewController(activity, childRegistry, "child2", new Options()));
121
         child2 = spy(new SimpleViewController(activity, childRegistry, "child2", new Options()));
114
-        child3 = spy(new SimpleViewController(activity, childRegistry, "child3", new Options()));
122
+        child3 = spy(new SimpleViewController(activity, childRegistry, "child3", new Options()) {
123
+            @Override
124
+            protected SimpleView createView() {
125
+                return take(child3View, super.createView());
126
+            }
127
+        });
115
         child4 = spy(new SimpleViewController(activity, childRegistry, "child4", new Options()));
128
         child4 = spy(new SimpleViewController(activity, childRegistry, "child4", new Options()));
116
-        uut = createStack();
117
-        activity.setContentView(uut.getView());
118
     }
129
     }
119
 
130
 
120
     @Test
131
     @Test
394
         assertContainsOnlyId(child1a.getId());
405
         assertContainsOnlyId(child1a.getId());
395
     }
406
     }
396
 
407
 
408
+    @Test
409
+    public void setRoot_topScreenIsStartedThenTheRest() {
410
+        disablePushAnimation(child1, child2, child3);
411
+        child3View = spy(new SimpleViewController.SimpleView(activity));
412
+
413
+        uut.setRoot(Arrays.asList(child1, child2, child3), new CommandListenerAdapter());
414
+        ShadowLooper.idleMainLooper();
415
+        InOrder inOrder = inOrder(child3View, child2, child1);
416
+        inOrder.verify(child3View).start();
417
+        inOrder.verify(child2).start();
418
+        inOrder.verify(child1).start();
419
+    }
420
+
397
     @Test
421
     @Test
398
     public synchronized void pop() {
422
     public synchronized void pop() {
399
         disablePushAnimation(child1, child2);
423
         disablePushAnimation(child1, child2);