Parcourir la source

Implement drawBehind BottomTabs

Fixes #2996
Guy Carmeli il y a 6 ans
Parent
révision
c2cc2ff191

+ 8
- 0
lib/android/app/src/main/java/com/reactnativenavigation/parse/BottomTabsOptions.java Voir le fichier

@@ -27,6 +27,7 @@ public class BottomTabsOptions {
27 27
         options.currentTabId = TextParser.parse(json, "currentTabId");
28 28
 		options.currentTabIndex = NumberParser.parse(json,"currentTabIndex");
29 29
 		options.visible = BoolParser.parse(json,"visible");
30
+        options.drawBehind = BoolParser.parse(json, "drawBehind");
30 31
 		options.animate = BoolParser.parse(json,"animate");
31 32
         options.testId = TextParser.parse(json, "testID");
32 33
 
@@ -37,6 +38,7 @@ public class BottomTabsOptions {
37 38
     public Color tabColor = new NullColor();
38 39
     public Color selectedTabColor = new NullColor();
39 40
 	public Bool visible = new NullBool();
41
+    public Bool drawBehind = new NullBool();
40 42
 	public Bool animate = new NullBool();
41 43
 	public Number currentTabIndex = new NullNumber();
42 44
 	public Text currentTabId = new NullText();
@@ -52,6 +54,9 @@ public class BottomTabsOptions {
52 54
 		if (other.visible.hasValue()) {
53 55
 			visible = other.visible;
54 56
 		}
57
+        if (other.drawBehind.hasValue()) {
58
+            drawBehind = other.drawBehind;
59
+        }
55 60
 		if (other.animate.hasValue()) {
56 61
 			animate = other.animate;
57 62
 		}
@@ -79,6 +84,9 @@ public class BottomTabsOptions {
79 84
         if (!visible.hasValue()) {
80 85
             visible = defaultOptions.visible;
81 86
         }
87
+        if (!drawBehind.hasValue()) {
88
+            drawBehind = defaultOptions.drawBehind;
89
+        }
82 90
         if (!animate.hasValue()) {
83 91
             animate = defaultOptions.animate;
84 92
         }

+ 27
- 7
lib/android/app/src/main/java/com/reactnativenavigation/presentation/BottomTabsOptionsPresenter.java Voir le fichier

@@ -1,22 +1,30 @@
1 1
 package com.reactnativenavigation.presentation;
2 2
 
3
+import android.view.ViewGroup.MarginLayoutParams;
4
+
3 5
 import com.reactnativenavigation.anim.BottomTabsAnimator;
4 6
 import com.reactnativenavigation.parse.AnimationsOptions;
5 7
 import com.reactnativenavigation.parse.BottomTabOptions;
6 8
 import com.reactnativenavigation.parse.BottomTabsOptions;
7 9
 import com.reactnativenavigation.parse.Options;
10
+import com.reactnativenavigation.viewcontrollers.ViewController;
8 11
 import com.reactnativenavigation.viewcontrollers.bottomtabs.BottomTabFinder;
9 12
 import com.reactnativenavigation.viewcontrollers.bottomtabs.TabSelector;
10 13
 import com.reactnativenavigation.views.BottomTabs;
14
+import com.reactnativenavigation.views.Component;
15
+
16
+import java.util.List;
11 17
 
12 18
 public class BottomTabsOptionsPresenter {
13
-    private BottomTabs bottomTabs;
14
-    private TabSelector tabSelector;
15
-    private BottomTabFinder bottomTabFinder;
16
-    private BottomTabsAnimator animator;
19
+    private final BottomTabs bottomTabs;
20
+    private final TabSelector tabSelector;
21
+    private final BottomTabFinder bottomTabFinder;
22
+    private final BottomTabsAnimator animator;
23
+    private final List<ViewController> tabs;
17 24
 
18
-    public BottomTabsOptionsPresenter(BottomTabs bottomTabs, TabSelector tabSelector, BottomTabFinder bottomTabFinder) {
25
+    public BottomTabsOptionsPresenter(BottomTabs bottomTabs, List<ViewController> tabs, TabSelector tabSelector, BottomTabFinder bottomTabFinder) {
19 26
         this.bottomTabs = bottomTabs;
27
+        this.tabs = tabs;
20 28
         this.tabSelector = tabSelector;
21 29
         this.bottomTabFinder = bottomTabFinder;
22 30
         animator = new BottomTabsAnimator(bottomTabs);
@@ -26,9 +34,11 @@ public class BottomTabsOptionsPresenter {
26 34
         applyBottomTabsOptions(options.bottomTabsOptions, options.animations);
27 35
     }
28 36
 
29
-    public void present(Options options, int tabIndex) {
37
+    public void presentChildOptions(Options options, Component child) {
30 38
         applyBottomTabsOptions(options.bottomTabsOptions, options.animations);
39
+        int tabIndex = bottomTabFinder.findByComponent(child);
31 40
         applyBottomTabOptions(options.bottomTabOptions, tabIndex);
41
+        applyDrawBehind(options.bottomTabsOptions, tabIndex);
32 42
     }
33 43
 
34 44
     private void applyBottomTabOptions(BottomTabOptions options, int tabIndex) {
@@ -37,6 +47,17 @@ public class BottomTabsOptionsPresenter {
37 47
         }
38 48
     }
39 49
 
50
+    private void applyDrawBehind(BottomTabsOptions options, int tabIndex) {
51
+        MarginLayoutParams lp = (MarginLayoutParams) tabs.get(tabIndex).getView().getLayoutParams();
52
+        if (options.drawBehind.isTrue()) {
53
+            lp.bottomMargin = 0;
54
+        }
55
+        if (options.drawBehind.isFalseOrUndefined()) {
56
+            lp.bottomMargin = bottomTabs.getHeight();
57
+        }
58
+
59
+    }
60
+
40 61
     private void applyBottomTabsOptions(BottomTabsOptions options, AnimationsOptions animationsOptions) {
41 62
         if (options.backgroundColor.hasValue()) {
42 63
             bottomTabs.setBackgroundColor(options.backgroundColor.get());
@@ -73,5 +94,4 @@ public class BottomTabsOptionsPresenter {
73 94
             }
74 95
         }
75 96
     }
76
-
77 97
 }

+ 1
- 1
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/bottomtabs/BottomTabFinder.java Voir le fichier

@@ -11,7 +11,7 @@ public class BottomTabFinder {
11 11
     private List<ViewController> tabs;
12 12
 
13 13
     @IntRange(from = -1)
14
-    int findByComponent(Component component) {
14
+    public int findByComponent(Component component) {
15 15
         for (int i = 0; i < tabs.size(); i++) {
16 16
             if (tabs.get(i).containsComponent(component)) {
17 17
                 return i;

+ 10
- 8
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/bottomtabs/BottomTabsController.java Voir le fichier

@@ -3,6 +3,7 @@ package com.reactnativenavigation.viewcontrollers.bottomtabs;
3 3
 import android.app.Activity;
4 4
 import android.graphics.drawable.Drawable;
5 5
 import android.support.annotation.NonNull;
6
+import android.support.annotation.RestrictTo;
6 7
 import android.view.ViewGroup;
7 8
 import android.widget.RelativeLayout;
8 9
 
@@ -48,7 +49,7 @@ public class BottomTabsController extends ParentController implements AHBottomNa
48 49
 	protected ViewGroup createView() {
49 50
 		RelativeLayout root = new RelativeLayout(getActivity());
50 51
 		bottomTabs = new BottomTabs(getActivity());
51
-        presenter = new BottomTabsOptionsPresenter(bottomTabs, this, bottomTabFinder);
52
+        presenter = new BottomTabsOptionsPresenter(bottomTabs, tabs, this, bottomTabFinder);
52 53
         bottomTabs.setOnTabSelectedListener(this);
53 54
 		RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT);
54 55
 		lp.addRule(ALIGN_PARENT_BOTTOM);
@@ -65,8 +66,7 @@ public class BottomTabsController extends ParentController implements AHBottomNa
65 66
     @Override
66 67
     public void applyChildOptions(Options options, Component child) {
67 68
         super.applyChildOptions(options, child);
68
-        final int tabIndex = bottomTabFinder.findByComponent(child);
69
-        if (tabIndex >= 0) presenter.present(this.options, tabIndex);
69
+        presenter.presentChildOptions(this.options, child);
70 70
         applyOnParentController(parentController ->
71 71
                 ((ParentController) parentController).applyChildOptions(this.options.copy().clearBottomTabsOptions().clearBottomTabOptions(), child)
72 72
         );
@@ -75,8 +75,7 @@ public class BottomTabsController extends ParentController implements AHBottomNa
75 75
     @Override
76 76
     public void mergeChildOptions(Options options, Component child) {
77 77
         super.mergeChildOptions(options, child);
78
-        final int tabIndex = bottomTabFinder.findByComponent(child);
79
-        presenter.present(options, tabIndex);
78
+        presenter.presentChildOptions(options, child);
80 79
         applyOnParentController(parentController ->
81 80
                 ((ParentController) parentController).mergeChildOptions(options.copy().clearBottomTabsOptions(), child)
82 81
         );
@@ -169,13 +168,16 @@ public class BottomTabsController extends ParentController implements AHBottomNa
169 168
     public void selectTab(final int newIndex) {
170 169
         getView().removeView(getCurrentView());
171 170
         bottomTabs.setCurrentItem(newIndex, false);
172
-        RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT);
173
-        params.bottomMargin = bottomTabs.getHeight();
174
-        getView().addView(getCurrentView(), params);
171
+        getView().addView(getCurrentView());
175 172
     }
176 173
 
177 174
     @NonNull
178 175
     private ViewGroup getCurrentView() {
179 176
         return tabs.get(bottomTabs.getCurrentItem()).getView();
180 177
     }
178
+
179
+    @RestrictTo(RestrictTo.Scope.TESTS)
180
+    public BottomTabs getBottomTabs() {
181
+        return bottomTabs;
182
+    }
181 183
 }

+ 38
- 2
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/BottomTabsControllerTest.java Voir le fichier

@@ -2,6 +2,7 @@ package com.reactnativenavigation.viewcontrollers;
2 2
 
3 3
 import android.app.Activity;
4 4
 import android.support.annotation.NonNull;
5
+import android.view.ViewGroup;
5 6
 import android.widget.RelativeLayout;
6 7
 
7 8
 import com.reactnativenavigation.BaseTest;
@@ -11,8 +12,10 @@ import com.reactnativenavigation.mocks.TitleBarReactViewCreatorMock;
11 12
 import com.reactnativenavigation.mocks.TopBarBackgroundViewCreatorMock;
12 13
 import com.reactnativenavigation.mocks.TopBarButtonCreatorMock;
13 14
 import com.reactnativenavigation.parse.Options;
15
+import com.reactnativenavigation.parse.params.Bool;
14 16
 import com.reactnativenavigation.parse.params.Color;
15 17
 import com.reactnativenavigation.parse.params.Number;
18
+import com.reactnativenavigation.parse.params.Text;
16 19
 import com.reactnativenavigation.react.EventEmitter;
17 20
 import com.reactnativenavigation.utils.CommandListenerAdapter;
18 21
 import com.reactnativenavigation.utils.ImageLoader;
@@ -51,18 +54,27 @@ public class BottomTabsControllerTest extends BaseTest {
51 54
     private ImageLoader imageLoaderMock = ImageLoaderMock.mock();
52 55
     private EventEmitter eventEmitter;
53 56
     private ChildControllersRegistry childRegistry;
57
+    private List<ViewController> tabs;
54 58
 
55 59
     @Override
56 60
     public void beforeEach() {
57 61
         activity = newActivity();
58 62
         childRegistry = new ChildControllersRegistry();
59 63
         eventEmitter = Mockito.mock(EventEmitter.class);
60
-        uut = spy(new BottomTabsController(activity, childRegistry, eventEmitter, imageLoaderMock, "uut", new Options()));
64
+        uut = spy(new BottomTabsController(activity, childRegistry, eventEmitter, imageLoaderMock, "uut", new Options()) {
65
+            @Override
66
+            public void ensureViewIsCreated() {
67
+                super.ensureViewIsCreated();
68
+                uut.getView().layout(0, 0, 1000, 1000);
69
+                uut.getBottomTabs().layout(0, 0, 1000, 100);
70
+            }
71
+        });
61 72
         child1 = spy(new SimpleViewController(activity, childRegistry, "child1", tabOptions));
62 73
         child2 = spy(new SimpleViewController(activity, childRegistry, "child2", tabOptions));
63 74
         child3 = spy(new SimpleViewController(activity, childRegistry, "child3", tabOptions));
64 75
         child4 = spy(new SimpleViewController(activity, childRegistry, "child4", tabOptions));
65 76
         child5 = spy(new SimpleViewController(activity, childRegistry, "child5", tabOptions));
77
+        tabs = createTabs();
66 78
     }
67 79
 
68 80
     @Test
@@ -169,7 +181,6 @@ public class BottomTabsControllerTest extends BaseTest {
169 181
 
170 182
     @Test
171 183
     public void mergeOptions_currentTabIndex() {
172
-        List<ViewController> tabs = createTabs();
173 184
         uut.setTabs(tabs);
174 185
         uut.ensureViewIsCreated();
175 186
 
@@ -180,6 +191,27 @@ public class BottomTabsControllerTest extends BaseTest {
180 191
         verify(eventEmitter, times(0)).emitBottomTabSelected(any(Integer.class), any(Integer.class));
181 192
     }
182 193
 
194
+    @Test
195
+    public void mergeOptions_drawBehind() {
196
+        List<ViewController> tabs = createTabs();
197
+        uut.setTabs(tabs);
198
+        uut.ensureViewIsCreated();
199
+        child1.onViewAppeared();
200
+        uut.selectTab(0);
201
+
202
+        assertThat(childLayoutParams(0).bottomMargin).isEqualTo(uut.getBottomTabs().getHeight());
203
+
204
+        Options o1 = new Options();
205
+        o1.bottomTabsOptions.drawBehind = new Bool(true);
206
+        child1.mergeOptions(o1);
207
+        assertThat(childLayoutParams(0).bottomMargin).isEqualTo(0);
208
+
209
+        Options o2 = new Options();
210
+        o2.topBar.title.text = new Text("Some text");
211
+        child1.mergeOptions(o1);
212
+        assertThat(childLayoutParams(0).bottomMargin).isEqualTo(0);
213
+    }
214
+
183 215
     @Test
184 216
     public void child_mergeOptions_currentTabIndex() {
185 217
         List<ViewController> tabs = createTabs();
@@ -220,4 +252,8 @@ public class BottomTabsControllerTest extends BaseTest {
220 252
                 .setInitialOptions(tabOptions)
221 253
                 .createStackController();
222 254
     }
255
+
256
+    private ViewGroup.MarginLayoutParams childLayoutParams(int index) {
257
+        return (ViewGroup.MarginLayoutParams) tabs.get(index).getView().getLayoutParams();
258
+    }
223 259
 }

+ 4
- 1
playground/src/screens/TextScreen.js Voir le fichier

@@ -10,6 +10,7 @@ class TextScreen extends Component {
10 10
   static get options() {
11 11
     return {
12 12
       bottomTabs: {
13
+        drawBehind: true,
13 14
         testID: testIDs.BOTTOM_TABS_ELEMENT
14 15
       },
15 16
       topBar: {
@@ -62,6 +63,7 @@ class TextScreen extends Component {
62 63
       bottomTabs: {
63 64
         currentTabIndex: 1,
64 65
         visible: false,
66
+        drawBehind: true,
65 67
         animate: true,
66 68
         tabColor: 'blue',
67 69
         selectedTabColor: 'red'
@@ -81,6 +83,7 @@ class TextScreen extends Component {
81 83
     Navigation.mergeOptions(this.props.componentId, {
82 84
       bottomTabs: {
83 85
         visible,
86
+        drawBehind: true,
84 87
         animate: true
85 88
       }
86 89
     });
@@ -108,7 +111,7 @@ const styles = {
108 111
     flexGrow: 1,
109 112
     justifyContent: 'center',
110 113
     alignItems: 'center',
111
-    backgroundColor: '#f5fcff'
114
+    backgroundColor: '#E3DCC3'
112 115
   },
113 116
   h1: {
114 117
     fontSize: 24,