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,7 +12,7 @@ import android.view.animation.DecelerateInterpolator;
12 12
 import android.view.animation.LinearInterpolator;
13 13
 
14 14
 import com.reactnativenavigation.parse.AnimationOptions;
15
-import com.reactnativenavigation.utils.UiUtils;
15
+import com.reactnativenavigation.utils.ViewUtils;
16 16
 import com.reactnativenavigation.views.topbar.TopBar;
17 17
 
18 18
 import javax.annotation.Nullable;
@@ -43,14 +43,12 @@ public class TopBarAnimator {
43 43
 
44 44
     public void show(AnimationOptions options) {
45 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 54
     public void show(float startTranslation) {

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

@@ -162,13 +162,13 @@ public class StackOptionsPresenter {
162 162
     }
163 163
 
164 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 166
         topBar.setElevation(options.elevation.get(DEFAULT_ELEVATION));
167 167
         if (topBar.getLayoutParams() instanceof MarginLayoutParams) {
168 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 172
         topBar.setTitle(options.title.text.get(""));
173 173
 
174 174
         if (options.title.component.hasValue()) {

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

@@ -55,7 +55,7 @@ public class NavigationModule extends ReactContextBaseJavaModule {
55 55
         constants.putString(Constants.BACK_BUTTON_JS_KEY,    Constants.BACK_BUTTON_ID);
56 56
         constants.putDouble(Constants.TOP_BAR_HEIGHT_KEY,    Constants.BOTTOM_TABS_HEIGHT);
57 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 59
         promise.resolve(constants);
60 60
     }
61 61
 

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

@@ -7,6 +7,7 @@ import android.os.Handler;
7 7
 import android.os.Looper;
8 8
 import android.support.annotation.NonNull;
9 9
 import android.util.DisplayMetrics;
10
+import android.util.TypedValue;
10 11
 import android.view.View;
11 12
 import android.view.ViewTreeObserver;
12 13
 import android.view.WindowManager;
@@ -17,7 +18,7 @@ public class UiUtils {
17 18
     private static final int DEFAULT_TOOLBAR_HEIGHT = 56;
18 19
 
19 20
     private static int statusBarHeight = -1;
20
-    private static int toolBarHeight = -1;
21
+    private static int topBarHeight = -1;
21 22
 
22 23
     public static void runOnPreDrawOnce(final View view, final Runnable task) {
23 24
         view.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
@@ -80,30 +81,35 @@ public class UiUtils {
80 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 92
         final Resources resources = context.getResources();
88 93
         final int resourceId = resources.getIdentifier("action_bar_size", "dimen", "android");
89
-        toolBarHeight = resourceId > 0 ?
94
+        topBarHeight = resourceId > 0 ?
90 95
                 resources.getDimensionPixelSize(resourceId) :
91 96
                 dpToPx(context, DEFAULT_TOOLBAR_HEIGHT);
92
-        return toolBarHeight;
97
+        return topBarHeight;
93 98
     }
94 99
 
95 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 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 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 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,7 +85,7 @@ public class ViewUtils {
85 85
         return false;
86 86
     }
87 87
 
88
-    public static int getPreferredHeight(View view) {
88
+    public static int getHeight(View view) {
89 89
         if (view.getLayoutParams() == null) return 0;
90 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,7 +9,6 @@ import android.widget.RelativeLayout;
9 9
 
10 10
 import com.reactnativenavigation.interfaces.ScrollEventListener;
11 11
 import com.reactnativenavigation.parse.Options;
12
-import com.reactnativenavigation.utils.UiUtils;
13 12
 import com.reactnativenavigation.utils.ViewUtils;
14 13
 import com.reactnativenavigation.viewcontrollers.IReactView;
15 14
 import com.reactnativenavigation.viewcontrollers.TitleBarButtonController;
@@ -92,12 +91,7 @@ public class ComponentLayout extends FrameLayout implements ReactComponent, Titl
92 91
     public void drawBelowTopBar(TopBar topBar) {
93 92
         if (getLayoutParams() instanceof RelativeLayout.LayoutParams) {
94 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 95
             setLayoutParams(layoutParams);
102 96
         }
103 97
     }

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

@@ -4,10 +4,13 @@ import android.annotation.SuppressLint;
4 4
 import android.content.Context;
5 5
 import android.widget.RelativeLayout;
6 6
 
7
+import com.reactnativenavigation.utils.UiUtils;
7 8
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarBackgroundViewController;
8 9
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarController;
9 10
 import com.reactnativenavigation.views.topbar.TopBar;
10 11
 
12
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
13
+
11 14
 @SuppressLint("ViewConstructor")
12 15
 public class StackLayout extends RelativeLayout implements Component {
13 16
     private String stackId;
@@ -20,7 +23,10 @@ public class StackLayout extends RelativeLayout implements Component {
20 23
     }
21 24
 
22 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 32
     public String getStackId() {

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

@@ -178,9 +178,10 @@ public class TitleBar extends Toolbar {
178 178
     }
179 179
 
180 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 183
         ViewGroup.LayoutParams lp = getLayoutParams();
183
-        lp.height = (int) UiUtils.dpToPx(getContext(), height);
184
+        lp.height = pixelHeight;
184 185
         setLayoutParams(lp);
185 186
     }
186 187
 }

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

@@ -71,7 +71,7 @@ public class TopBar extends AppBarLayout implements ScrollEventListener.ScrollAw
71 71
 
72 72
         root = new FrameLayout(getContext());
73 73
         root.setId(CompatUtils.generateViewId());
74
-        content.addView(titleBar, MATCH_PARENT, WRAP_CONTENT);
74
+        content.addView(titleBar, MATCH_PARENT, UiUtils.getTopBarHeight(getContext()));
75 75
         content.addView(topTabs);
76 76
         root.addView(content);
77 77
         root.addView(border);
@@ -111,9 +111,10 @@ public class TopBar extends AppBarLayout implements ScrollEventListener.ScrollAw
111 111
     }
112 112
 
113 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 116
         ViewGroup.LayoutParams lp = getLayoutParams();
116
-        lp.height = (int) UiUtils.dpToPx(getContext(), height);
117
+        lp.height = pixelHeight;
117 118
         setLayoutParams(lp);
118 119
     }
119 120
 

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

@@ -10,6 +10,7 @@ import com.reactnativenavigation.parse.Options;
10 10
 import com.reactnativenavigation.parse.params.Bool;
11 11
 import com.reactnativenavigation.presentation.StackOptionsPresenter;
12 12
 import com.reactnativenavigation.utils.ImageLoader;
13
+import com.reactnativenavigation.utils.UiUtils;
13 14
 import com.reactnativenavigation.viewcontrollers.ChildControllersRegistry;
14 15
 import com.reactnativenavigation.viewcontrollers.ViewController;
15 16
 import com.reactnativenavigation.viewcontrollers.stack.StackControllerBuilder;
@@ -29,7 +30,7 @@ public class TestUtils {
29 30
                     @Override
30 31
                     protected TopBar createTopBar(Context context, TopBarBackgroundViewController topBarBackgroundViewController, StackLayout stackLayout) {
31 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 34
                         return topBar;
34 35
                     }
35 36
                 })

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

@@ -77,8 +77,8 @@ public class SimpleViewController extends ChildController<SimpleViewController.S
77 77
         public void drawBelowTopBar(TopBar topBar) {
78 78
             if (getLayoutParams() instanceof RelativeLayout.LayoutParams) {
79 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 82
 //                setLayoutParams(layoutParams);
83 83
             }
84 84
         }

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

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

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

@@ -21,6 +21,7 @@ import com.reactnativenavigation.presentation.StackOptionsPresenter;
21 21
 import com.reactnativenavigation.utils.CommandListenerAdapter;
22 22
 import com.reactnativenavigation.utils.ImageLoader;
23 23
 import com.reactnativenavigation.utils.TitleBarHelper;
24
+import com.reactnativenavigation.utils.UiUtils;
24 25
 import com.reactnativenavigation.utils.ViewHelper;
25 26
 import com.reactnativenavigation.utils.ViewUtils;
26 27
 import com.reactnativenavigation.viewcontrollers.ChildControllersRegistry;
@@ -880,7 +881,7 @@ public class StackControllerTest extends BaseTest {
880 881
             @Override
881 882
             protected TopBar createTopBar(Context context, TopBarBackgroundViewController topBarBackgroundViewController, StackLayout stackLayout) {
882 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 885
                 return spy;
885 886
             }
886 887
         });