Browse Source

Use explicit height for TopBar

Relying on WRAP_CONTENT caused rare bugs where the TopBar would appear stretched due to flex issues
when a react component was displayed in the title.
Another reason for this change is that we don’t need to wait for the TopBar to measure itself when its height needs to be taken
into account, for example when animating it or when positioning views below it.
Guy Carmeli 6 years ago
parent
commit
d00ebe4234

+ 7
- 9
lib/android/app/src/main/java/com/reactnativenavigation/anim/TopBarAnimator.java View File

12
 import android.view.animation.LinearInterpolator;
12
 import android.view.animation.LinearInterpolator;
13
 
13
 
14
 import com.reactnativenavigation.parse.AnimationOptions;
14
 import com.reactnativenavigation.parse.AnimationOptions;
15
-import com.reactnativenavigation.utils.UiUtils;
15
+import com.reactnativenavigation.utils.ViewUtils;
16
 import com.reactnativenavigation.views.topbar.TopBar;
16
 import com.reactnativenavigation.views.topbar.TopBar;
17
 
17
 
18
 import javax.annotation.Nullable;
18
 import javax.annotation.Nullable;
43
 
43
 
44
     public void show(AnimationOptions options) {
44
     public void show(AnimationOptions options) {
45
         topBar.setVisibility(View.VISIBLE);
45
         topBar.setVisibility(View.VISIBLE);
46
-        UiUtils.runOnMeasured(topBar, () -> {
47
-            if (options.hasValue() && (!options.id.hasValue() || options.id.get().equals(stackId))) {
48
-                showAnimator = options.getAnimation(topBar);
49
-            } else {
50
-                showAnimator = getDefaultShowAnimator(-1 * topBar.getHeight(), DECELERATE, DURATION);
51
-            }
52
-            show();
53
-        });
46
+        if (options.hasValue() && (!options.id.hasValue() || options.id.get().equals(stackId))) {
47
+            showAnimator = options.getAnimation(topBar);
48
+        } else {
49
+            showAnimator = getDefaultShowAnimator(-1 * ViewUtils.getHeight(topBar), DECELERATE, DURATION);
50
+        }
51
+        show();
54
     }
52
     }
55
 
53
 
56
     public void show(float startTranslation) {
54
     public void show(float startTranslation) {

+ 2
- 2
lib/android/app/src/main/java/com/reactnativenavigation/presentation/StackOptionsPresenter.java View File

162
     }
162
     }
163
 
163
 
164
     private void applyTopBarOptions(TopBarOptions options, AnimationsOptions animationOptions, Component component, Options componentOptions) {
164
     private void applyTopBarOptions(TopBarOptions options, AnimationsOptions animationOptions, Component component, Options componentOptions) {
165
-        topBar.setHeight(options.height.get(LayoutParams.WRAP_CONTENT));
165
+        topBar.setHeight(options.height.get(UiUtils.getTopBarHeightDp(activity)));
166
         topBar.setElevation(options.elevation.get(DEFAULT_ELEVATION));
166
         topBar.setElevation(options.elevation.get(DEFAULT_ELEVATION));
167
         if (topBar.getLayoutParams() instanceof MarginLayoutParams) {
167
         if (topBar.getLayoutParams() instanceof MarginLayoutParams) {
168
             ((MarginLayoutParams) topBar.getLayoutParams()).topMargin = UiUtils.dpToPx(activity, options.topMargin.get(0));
168
             ((MarginLayoutParams) topBar.getLayoutParams()).topMargin = UiUtils.dpToPx(activity, options.topMargin.get(0));
169
         }
169
         }
170
 
170
 
171
-        topBar.setTitleHeight(options.title.height.get(LayoutParams.WRAP_CONTENT));
171
+        topBar.setTitleHeight(options.title.height.get(UiUtils.getTopBarHeightDp(activity)));
172
         topBar.setTitle(options.title.text.get(""));
172
         topBar.setTitle(options.title.text.get(""));
173
 
173
 
174
         if (options.title.component.hasValue()) {
174
         if (options.title.component.hasValue()) {

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

55
         constants.putString(Constants.BACK_BUTTON_JS_KEY,    Constants.BACK_BUTTON_ID);
55
         constants.putString(Constants.BACK_BUTTON_JS_KEY,    Constants.BACK_BUTTON_ID);
56
         constants.putDouble(Constants.TOP_BAR_HEIGHT_KEY,    Constants.BOTTOM_TABS_HEIGHT);
56
         constants.putDouble(Constants.TOP_BAR_HEIGHT_KEY,    Constants.BOTTOM_TABS_HEIGHT);
57
         constants.putDouble(Constants.STATUS_BAR_HEIGHT_KEY, UiUtils.pxToDp(ctx, UiUtils.getStatusBarHeight(ctx)));
57
         constants.putDouble(Constants.STATUS_BAR_HEIGHT_KEY, UiUtils.pxToDp(ctx, UiUtils.getStatusBarHeight(ctx)));
58
-        constants.putDouble(Constants.TOP_BAR_HEIGHT_KEY,    UiUtils.pxToDp(ctx, UiUtils.getToolBarHeight(ctx)));
58
+        constants.putDouble(Constants.TOP_BAR_HEIGHT_KEY,    UiUtils.pxToDp(ctx, UiUtils.getTopBarHeight(ctx)));
59
         promise.resolve(constants);
59
         promise.resolve(constants);
60
     }
60
     }
61
 
61
 

+ 17
- 11
lib/android/app/src/main/java/com/reactnativenavigation/utils/UiUtils.java View File

7
 import android.os.Looper;
7
 import android.os.Looper;
8
 import android.support.annotation.NonNull;
8
 import android.support.annotation.NonNull;
9
 import android.util.DisplayMetrics;
9
 import android.util.DisplayMetrics;
10
+import android.util.TypedValue;
10
 import android.view.View;
11
 import android.view.View;
11
 import android.view.ViewTreeObserver;
12
 import android.view.ViewTreeObserver;
12
 import android.view.WindowManager;
13
 import android.view.WindowManager;
17
     private static final int DEFAULT_TOOLBAR_HEIGHT = 56;
18
     private static final int DEFAULT_TOOLBAR_HEIGHT = 56;
18
 
19
 
19
     private static int statusBarHeight = -1;
20
     private static int statusBarHeight = -1;
20
-    private static int toolBarHeight = -1;
21
+    private static int topBarHeight = -1;
21
 
22
 
22
     public static void runOnPreDrawOnce(final View view, final Runnable task) {
23
     public static void runOnPreDrawOnce(final View view, final Runnable task) {
23
         view.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
24
         view.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
80
         return statusBarHeight;
81
         return statusBarHeight;
81
     }
82
     }
82
 
83
 
83
-    public static int getToolBarHeight(Context context) {
84
-        if (toolBarHeight > 0) {
85
-            return toolBarHeight;
84
+    public static int getTopBarHeightDp(Context context) {
85
+        return (int) UiUtils.pxToDp(context, getTopBarHeight(context));
86
+    }
87
+
88
+    public static int getTopBarHeight(Context context) {
89
+        if (topBarHeight > 0) {
90
+            return topBarHeight;
86
         }
91
         }
87
         final Resources resources = context.getResources();
92
         final Resources resources = context.getResources();
88
         final int resourceId = resources.getIdentifier("action_bar_size", "dimen", "android");
93
         final int resourceId = resources.getIdentifier("action_bar_size", "dimen", "android");
89
-        toolBarHeight = resourceId > 0 ?
94
+        topBarHeight = resourceId > 0 ?
90
                 resources.getDimensionPixelSize(resourceId) :
95
                 resources.getDimensionPixelSize(resourceId) :
91
                 dpToPx(context, DEFAULT_TOOLBAR_HEIGHT);
96
                 dpToPx(context, DEFAULT_TOOLBAR_HEIGHT);
92
-        return toolBarHeight;
97
+        return topBarHeight;
93
     }
98
     }
94
 
99
 
95
     public static float dpToPx(Context context, float dp) {
100
     public static float dpToPx(Context context, float dp) {
96
-        float scale = context.getResources().getDisplayMetrics().density;
97
-        return dp * scale + 0.5f;
101
+        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, dp,
102
+                context.getResources().getDisplayMetrics());
98
     }
103
     }
99
 
104
 
100
     public static int dpToPx(Context context, int dp) {
105
     public static int dpToPx(Context context, int dp) {
101
-        float scale = context.getResources().getDisplayMetrics().density;
102
-        return (int) (dp * scale + 0.5f);
106
+        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, dp,
107
+                context.getResources().getDisplayMetrics());
103
     }
108
     }
104
 
109
 
105
     public static float pxToDp(Context context, float px) {
110
     public static float pxToDp(Context context, float px) {
106
-        return px / ((float) context.getResources().getDisplayMetrics().densityDpi / DisplayMetrics.DENSITY_DEFAULT);
111
+        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, px,
112
+                context.getResources().getDisplayMetrics());
107
     }
113
     }
108
 
114
 
109
     public static float dpToSp(Context context, float dp) {
115
     public static float dpToSp(Context context, float dp) {

+ 1
- 1
lib/android/app/src/main/java/com/reactnativenavigation/utils/ViewUtils.java View File

85
         return false;
85
         return false;
86
     }
86
     }
87
 
87
 
88
-    public static int getPreferredHeight(View view) {
88
+    public static int getHeight(View view) {
89
         if (view.getLayoutParams() == null) return 0;
89
         if (view.getLayoutParams() == null) return 0;
90
         return view.getLayoutParams().height < 0 ? view.getHeight() : view.getLayoutParams().height;
90
         return view.getLayoutParams().height < 0 ? view.getHeight() : view.getLayoutParams().height;
91
     }
91
     }

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

9
 
9
 
10
 import com.reactnativenavigation.interfaces.ScrollEventListener;
10
 import com.reactnativenavigation.interfaces.ScrollEventListener;
11
 import com.reactnativenavigation.parse.Options;
11
 import com.reactnativenavigation.parse.Options;
12
-import com.reactnativenavigation.utils.UiUtils;
13
 import com.reactnativenavigation.utils.ViewUtils;
12
 import com.reactnativenavigation.utils.ViewUtils;
14
 import com.reactnativenavigation.viewcontrollers.IReactView;
13
 import com.reactnativenavigation.viewcontrollers.IReactView;
15
 import com.reactnativenavigation.viewcontrollers.TitleBarButtonController;
14
 import com.reactnativenavigation.viewcontrollers.TitleBarButtonController;
92
     public void drawBelowTopBar(TopBar topBar) {
91
     public void drawBelowTopBar(TopBar topBar) {
93
         if (getLayoutParams() instanceof RelativeLayout.LayoutParams) {
92
         if (getLayoutParams() instanceof RelativeLayout.LayoutParams) {
94
             RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) getLayoutParams();
93
             RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) getLayoutParams();
95
-            int topBarHeight = ViewUtils.getPreferredHeight(topBar);
96
-            if (topBarHeight == 0) {
97
-                UiUtils.runOnMeasured(topBar, () -> layoutParams.topMargin = topBar.getHeight());
98
-            } else {
99
-                layoutParams.topMargin = topBarHeight;
100
-            }
94
+            layoutParams.topMargin = ViewUtils.getHeight(topBar);
101
             setLayoutParams(layoutParams);
95
             setLayoutParams(layoutParams);
102
         }
96
         }
103
     }
97
     }

+ 7
- 1
lib/android/app/src/main/java/com/reactnativenavigation/views/StackLayout.java View File

4
 import android.content.Context;
4
 import android.content.Context;
5
 import android.widget.RelativeLayout;
5
 import android.widget.RelativeLayout;
6
 
6
 
7
+import com.reactnativenavigation.utils.UiUtils;
7
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarBackgroundViewController;
8
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarBackgroundViewController;
8
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarController;
9
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarController;
9
 import com.reactnativenavigation.views.topbar.TopBar;
10
 import com.reactnativenavigation.views.topbar.TopBar;
10
 
11
 
12
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
13
+
11
 @SuppressLint("ViewConstructor")
14
 @SuppressLint("ViewConstructor")
12
 public class StackLayout extends RelativeLayout implements Component {
15
 public class StackLayout extends RelativeLayout implements Component {
13
     private String stackId;
16
     private String stackId;
20
     }
23
     }
21
 
24
 
22
     private void createLayout(TopBarBackgroundViewController topBarBackgroundViewController, TopBarController topBarController) {
25
     private void createLayout(TopBarBackgroundViewController topBarBackgroundViewController, TopBarController topBarController) {
23
-        addView(topBarController.createView(getContext(), topBarBackgroundViewController, this));
26
+        addView(topBarController.createView(getContext(), topBarBackgroundViewController, this),
27
+                MATCH_PARENT,
28
+                UiUtils.getTopBarHeight(getContext())
29
+        );
24
     }
30
     }
25
 
31
 
26
     public String getStackId() {
32
     public String getStackId() {

+ 3
- 2
lib/android/app/src/main/java/com/reactnativenavigation/views/titlebar/TitleBar.java View File

178
     }
178
     }
179
 
179
 
180
     public void setHeight(int height) {
180
     public void setHeight(int height) {
181
-        if (height == getLayoutParams().height) return;
181
+        int pixelHeight = UiUtils.dpToPx(getContext(), height);
182
+        if (pixelHeight == getLayoutParams().height) return;
182
         ViewGroup.LayoutParams lp = getLayoutParams();
183
         ViewGroup.LayoutParams lp = getLayoutParams();
183
-        lp.height = (int) UiUtils.dpToPx(getContext(), height);
184
+        lp.height = pixelHeight;
184
         setLayoutParams(lp);
185
         setLayoutParams(lp);
185
     }
186
     }
186
 }
187
 }

+ 4
- 3
lib/android/app/src/main/java/com/reactnativenavigation/views/topbar/TopBar.java View File

71
 
71
 
72
         root = new FrameLayout(getContext());
72
         root = new FrameLayout(getContext());
73
         root.setId(CompatUtils.generateViewId());
73
         root.setId(CompatUtils.generateViewId());
74
-        content.addView(titleBar, MATCH_PARENT, WRAP_CONTENT);
74
+        content.addView(titleBar, MATCH_PARENT, UiUtils.getTopBarHeight(getContext()));
75
         content.addView(topTabs);
75
         content.addView(topTabs);
76
         root.addView(content);
76
         root.addView(content);
77
         root.addView(border);
77
         root.addView(border);
111
     }
111
     }
112
 
112
 
113
     public void setHeight(int height) {
113
     public void setHeight(int height) {
114
-        if (height == getLayoutParams().height) return;
114
+        int pixelHeight = UiUtils.dpToPx(getContext(), height);
115
+        if (pixelHeight == getLayoutParams().height) return;
115
         ViewGroup.LayoutParams lp = getLayoutParams();
116
         ViewGroup.LayoutParams lp = getLayoutParams();
116
-        lp.height = (int) UiUtils.dpToPx(getContext(), height);
117
+        lp.height = pixelHeight;
117
         setLayoutParams(lp);
118
         setLayoutParams(lp);
118
     }
119
     }
119
 
120
 

+ 2
- 1
lib/android/app/src/test/java/com/reactnativenavigation/TestUtils.java View File

10
 import com.reactnativenavigation.parse.params.Bool;
10
 import com.reactnativenavigation.parse.params.Bool;
11
 import com.reactnativenavigation.presentation.StackOptionsPresenter;
11
 import com.reactnativenavigation.presentation.StackOptionsPresenter;
12
 import com.reactnativenavigation.utils.ImageLoader;
12
 import com.reactnativenavigation.utils.ImageLoader;
13
+import com.reactnativenavigation.utils.UiUtils;
13
 import com.reactnativenavigation.viewcontrollers.ChildControllersRegistry;
14
 import com.reactnativenavigation.viewcontrollers.ChildControllersRegistry;
14
 import com.reactnativenavigation.viewcontrollers.ViewController;
15
 import com.reactnativenavigation.viewcontrollers.ViewController;
15
 import com.reactnativenavigation.viewcontrollers.stack.StackControllerBuilder;
16
 import com.reactnativenavigation.viewcontrollers.stack.StackControllerBuilder;
29
                     @Override
30
                     @Override
30
                     protected TopBar createTopBar(Context context, TopBarBackgroundViewController topBarBackgroundViewController, StackLayout stackLayout) {
31
                     protected TopBar createTopBar(Context context, TopBarBackgroundViewController topBarBackgroundViewController, StackLayout stackLayout) {
31
                         TopBar topBar = super.createTopBar(context, topBarBackgroundViewController, stackLayout);
32
                         TopBar topBar = super.createTopBar(context, topBarBackgroundViewController, stackLayout);
32
-                        topBar.layout(0, 0, 1000, 100);
33
+                        topBar.layout(0, 0, 1000, UiUtils.getTopBarHeight(context));
33
                         return topBar;
34
                         return topBar;
34
                     }
35
                     }
35
                 })
36
                 })

+ 2
- 2
lib/android/app/src/test/java/com/reactnativenavigation/mocks/SimpleViewController.java View File

77
         public void drawBelowTopBar(TopBar topBar) {
77
         public void drawBelowTopBar(TopBar topBar) {
78
             if (getLayoutParams() instanceof RelativeLayout.LayoutParams) {
78
             if (getLayoutParams() instanceof RelativeLayout.LayoutParams) {
79
                 RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) getLayoutParams();
79
                 RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) getLayoutParams();
80
-                if (layoutParams.topMargin == ViewUtils.getPreferredHeight(topBar)) return;
81
-                layoutParams.topMargin = ViewUtils.getPreferredHeight(topBar);
80
+                if (layoutParams.topMargin == ViewUtils.getHeight(topBar)) return;
81
+                layoutParams.topMargin = ViewUtils.getHeight(topBar);
82
 //                setLayoutParams(layoutParams);
82
 //                setLayoutParams(layoutParams);
83
             }
83
             }
84
         }
84
         }

+ 2
- 0
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/navigator/NavigatorTest.java View File

35
 import org.junit.Test;
35
 import org.junit.Test;
36
 import org.mockito.Mockito;
36
 import org.mockito.Mockito;
37
 import org.robolectric.android.controller.ActivityController;
37
 import org.robolectric.android.controller.ActivityController;
38
+import org.robolectric.annotation.Config;
38
 
39
 
39
 import java.util.Arrays;
40
 import java.util.Arrays;
40
 import java.util.List;
41
 import java.util.List;
46
 import static org.mockito.Mockito.verify;
47
 import static org.mockito.Mockito.verify;
47
 import static org.mockito.Mockito.when;
48
 import static org.mockito.Mockito.when;
48
 
49
 
50
+@Config(qualifiers = "xxhdpi")
49
 public class NavigatorTest extends BaseTest {
51
 public class NavigatorTest extends BaseTest {
50
     private TestActivity activity;
52
     private TestActivity activity;
51
     private ChildControllersRegistry childRegistry;
53
     private ChildControllersRegistry childRegistry;

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

21
 import com.reactnativenavigation.utils.CommandListenerAdapter;
21
 import com.reactnativenavigation.utils.CommandListenerAdapter;
22
 import com.reactnativenavigation.utils.ImageLoader;
22
 import com.reactnativenavigation.utils.ImageLoader;
23
 import com.reactnativenavigation.utils.TitleBarHelper;
23
 import com.reactnativenavigation.utils.TitleBarHelper;
24
+import com.reactnativenavigation.utils.UiUtils;
24
 import com.reactnativenavigation.utils.ViewHelper;
25
 import com.reactnativenavigation.utils.ViewHelper;
25
 import com.reactnativenavigation.utils.ViewUtils;
26
 import com.reactnativenavigation.utils.ViewUtils;
26
 import com.reactnativenavigation.viewcontrollers.ChildControllersRegistry;
27
 import com.reactnativenavigation.viewcontrollers.ChildControllersRegistry;
880
             @Override
881
             @Override
881
             protected TopBar createTopBar(Context context, TopBarBackgroundViewController topBarBackgroundViewController, StackLayout stackLayout) {
882
             protected TopBar createTopBar(Context context, TopBarBackgroundViewController topBarBackgroundViewController, StackLayout stackLayout) {
882
                 TopBar spy = spy(super.createTopBar(context, topBarBackgroundViewController, stackLayout));
883
                 TopBar spy = spy(super.createTopBar(context, topBarBackgroundViewController, stackLayout));
883
-                spy.layout(0, 0, 1000, 100);
884
+                spy.layout(0, 0, 1000, UiUtils.getTopBarHeight(activity));
884
                 return spy;
885
                 return spy;
885
             }
886
             }
886
         });
887
         });