react-native-navigation 8.8.7 → 8.8.8-snapshot.2616

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,7 +3,9 @@ package com.reactnativenavigation.views.stack.topbar.titlebar;
3
3
  import android.annotation.SuppressLint;
4
4
  import android.content.Context;
5
5
  import android.util.TypedValue;
6
+ import android.view.Gravity;
6
7
  import android.view.View;
8
+ import android.widget.FrameLayout;
7
9
 
8
10
  import com.reactnativenavigation.options.ComponentOptions;
9
11
  import com.reactnativenavigation.options.params.Number;
@@ -23,6 +25,17 @@ public class TitleBarReactButtonView extends ReactView {
23
25
  this.component = component;
24
26
  }
25
27
 
28
+ @Override
29
+ public void onViewAdded(View child) {
30
+ super.onViewAdded(child);
31
+ if (child.getLayoutParams() instanceof FrameLayout.LayoutParams) {
32
+ FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) child.getLayoutParams();
33
+ layoutParams.gravity = layoutParams.gravity == -1
34
+ ? Gravity.CENTER_VERTICAL
35
+ : (layoutParams.gravity & ~Gravity.VERTICAL_GRAVITY_MASK) | Gravity.CENTER_VERTICAL;
36
+ }
37
+ }
38
+
26
39
  @Override
27
40
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
28
41
  // This is a workaround, ReactNative throws exception when views have ids, On android MenuItems
@@ -31,32 +44,34 @@ public class TitleBarReactButtonView extends ReactView {
31
44
  this.setId(View.NO_ID);
32
45
  }
33
46
 
34
- super.onMeasure(
35
- createWidthSpec(widthMeasureSpec, component.width),
36
- createHeightSpec(heightMeasureSpec, component.height)
37
- );
38
- }
39
-
40
- private int createWidthSpec(int measureSpec, Number dimension) {
41
- return createSpec(measureSpec, dimension, Math.max(getResources().getDisplayMetrics().widthPixels, 1));
47
+ // Width: bounded (AT_MOST) so the hosted React surface sizes itself to its content. Under
48
+ // Fabric, ReactSurfaceView reports max-of-children under AT_MOST (the laid-out content width)
49
+ // and pushes that to the async layout. Crucially we must NOT push a forced EXACTLY *width*:
50
+ // before the content has laid out that width is collapsed (~1px), Fabric lays the content into
51
+ // it and the button never recovers (#8320/#8326 did this and regressed under the New Arch).
52
+ //
53
+ // Height: EXACTLY the available bar height. Giving the surface a filled height (rather than a
54
+ // content-hugging AT_MOST height) lets content that centers itself (e.g. a flex container with
55
+ // justifyContent: 'center') sit vertically centered in the bar, matching the legacy layout.
56
+ // It does not cause the width collapse — only a forced width does.
57
+ int widthSpec = component.width.hasValue()
58
+ ? createExactSpec(component.width)
59
+ : makeMeasureSpec(resolveAvailableWidth(widthMeasureSpec), AT_MOST);
60
+ int heightSpec = createHeightSpec(heightMeasureSpec, component.height);
61
+ super.onMeasure(widthSpec, heightSpec);
42
62
  }
43
63
 
44
64
  private int createHeightSpec(int measureSpec, Number dimension) {
45
65
  if (dimension.hasValue()) {
46
66
  return createExactSpec(dimension);
47
67
  }
48
- return makeMeasureSpec(Math.max(resolveActionBarSize(), 1), EXACTLY);
68
+ int availableSize = MeasureSpec.getSize(measureSpec);
69
+ return makeMeasureSpec(availableSize > 0 ? availableSize : Math.max(resolveActionBarSize(), 1), EXACTLY);
49
70
  }
50
71
 
51
- private int createSpec(int measureSpec, Number dimension, int fallbackSize) {
52
- if (dimension.hasValue()) {
53
- return createExactSpec(dimension);
54
- } else {
55
- // Use bounded wrap-content width to avoid RN/Yoga RTL padding issues caused by
56
- // UNSPECIFIED, without forcing every custom button to actionBarSize width.
57
- int availableSize = MeasureSpec.getSize(measureSpec);
58
- return makeMeasureSpec(availableSize > 0 ? availableSize : fallbackSize, AT_MOST);
59
- }
72
+ private int resolveAvailableWidth(int measureSpec) {
73
+ int availableSize = MeasureSpec.getSize(measureSpec);
74
+ return availableSize > 0 ? availableSize : Math.max(getResources().getDisplayMetrics().widthPixels, 1);
60
75
  }
61
76
 
62
77
  private int createExactSpec(Number dimension) {
@@ -9,8 +9,10 @@ import static org.assertj.core.api.Java6Assertions.assertThat;
9
9
 
10
10
  import android.app.Activity;
11
11
  import android.util.TypedValue;
12
+ import android.view.Gravity;
12
13
  import android.view.View;
13
14
  import android.view.ViewGroup;
15
+ import android.widget.FrameLayout;
14
16
 
15
17
  import com.reactnativenavigation.BaseTest;
16
18
  import com.reactnativenavigation.options.ComponentOptions;
@@ -21,22 +23,37 @@ import com.reactnativenavigation.views.stack.topbar.titlebar.TitleBarReactButton
21
23
 
22
24
  import org.junit.Test;
23
25
 
26
+ import java.util.ArrayList;
27
+ import java.util.List;
28
+
24
29
  public class TitleBarReactButtonViewTest extends BaseTest {
25
30
  private static final int PARENT_WIDTH = 200;
26
31
  private static final int PARENT_HEIGHT = 100;
27
32
  private static final int CHILD_WIDTH = 24;
28
33
  private static final int CHILD_HEIGHT = 16;
29
34
 
35
+ // Without explicit dimensions the button measures the hosted React surface ONCE:
36
+ // - width AT_MOST → the surface sizes itself to its content (max-of-children under Fabric).
37
+ // - height EXACTLY the available bar height → the surface gets a filled box so content that
38
+ // centers itself (flex justifyContent: 'center') stays vertically centered in the bar.
39
+ // It deliberately does not push a forced EXACTLY *width*; under the New Architecture that re-pushes
40
+ // a (initially collapsed) width to the async Fabric layout and the button never recovers.
30
41
  @Test
31
- public void missingDimensionsMeasureToContentWithinParentBounds() {
42
+ public void missingDimensionsSizeWidthToContentAndFillHeight() {
32
43
  Activity activity = newActivity();
33
44
  TitleBarReactButtonView uut = createView(activity, new ComponentOptions());
34
- uut.addView(new FixedSizeView(activity), new ViewGroup.LayoutParams(CHILD_WIDTH, CHILD_HEIGHT));
45
+ RecordingContentView child = new RecordingContentView(activity);
46
+ setContentView(uut, child);
35
47
 
36
48
  uut.measure(makeMeasureSpec(PARENT_WIDTH, AT_MOST), makeMeasureSpec(PARENT_HEIGHT, AT_MOST));
37
49
 
38
50
  assertThat(uut.getMeasuredWidth()).isEqualTo(CHILD_WIDTH);
39
- assertThat(uut.getMeasuredHeight()).isEqualTo(resolveActionBarSize(activity));
51
+ assertThat(uut.getMeasuredHeight()).isEqualTo(PARENT_HEIGHT);
52
+ assertThat(child.widthMeasureSpecs.size()).isEqualTo(1);
53
+ assertThat(getMode(child.widthMeasureSpecs.get(0))).isEqualTo(AT_MOST);
54
+ assertThat(getSize(child.widthMeasureSpecs.get(0))).isEqualTo(PARENT_WIDTH);
55
+ assertThat(getMode(child.heightMeasureSpecs.get(0))).isEqualTo(EXACTLY);
56
+ assertThat(getSize(child.heightMeasureSpecs.get(0))).isEqualTo(PARENT_HEIGHT);
40
57
  }
41
58
 
42
59
  @Test
@@ -46,50 +63,70 @@ public class TitleBarReactButtonViewTest extends BaseTest {
46
63
  component.width = new Number(72);
47
64
  component.height = new Number(32);
48
65
  TitleBarReactButtonView uut = createView(activity, component);
49
- uut.addView(new FixedSizeView(activity), new ViewGroup.LayoutParams(CHILD_WIDTH, CHILD_HEIGHT));
66
+ RecordingContentView child = new RecordingContentView(activity);
67
+ setContentView(uut, child);
50
68
 
51
69
  uut.measure(makeMeasureSpec(PARENT_WIDTH, AT_MOST), makeMeasureSpec(PARENT_HEIGHT, AT_MOST));
52
70
 
53
71
  assertThat(uut.getMeasuredWidth()).isEqualTo(UiUtils.dpToPx(activity, 72));
54
72
  assertThat(uut.getMeasuredHeight()).isEqualTo(UiUtils.dpToPx(activity, 32));
73
+ assertThat(child.widthMeasureSpecs.size()).isEqualTo(1);
74
+ assertThat(getMode(child.widthMeasureSpecs.get(0))).isEqualTo(EXACTLY);
75
+ assertThat(getSize(child.widthMeasureSpecs.get(0))).isEqualTo(UiUtils.dpToPx(activity, 72));
76
+ assertThat(getMode(child.heightMeasureSpecs.get(0))).isEqualTo(EXACTLY);
77
+ assertThat(getSize(child.heightMeasureSpecs.get(0))).isEqualTo(UiUtils.dpToPx(activity, 32));
55
78
  }
56
79
 
57
80
  @Test
58
- public void zeroParentWidthFallbacksToBoundedAtMostSpecAndHeightUsesActionBarSize() {
81
+ public void zeroParentSpecsFallBackToScreenWidthAndActionBarHeight() {
59
82
  Activity activity = newActivity();
60
83
  TitleBarReactButtonView uut = createView(activity, new ComponentOptions());
61
- RecordingView child = new RecordingView(activity);
62
- uut.addView(child, new ViewGroup.LayoutParams(
63
- ViewGroup.LayoutParams.MATCH_PARENT,
64
- ViewGroup.LayoutParams.MATCH_PARENT
65
- ));
84
+ RecordingContentView child = new RecordingContentView(activity);
85
+ setContentView(uut, child);
66
86
 
67
87
  uut.measure(makeMeasureSpec(0, AT_MOST), makeMeasureSpec(0, AT_MOST));
68
88
 
69
- assertThat(getMode(child.lastWidthMeasureSpec)).isEqualTo(AT_MOST);
70
- assertThat(getSize(child.lastWidthMeasureSpec))
89
+ assertThat(child.widthMeasureSpecs.size()).isEqualTo(1);
90
+ assertThat(getMode(child.widthMeasureSpecs.get(0))).isEqualTo(AT_MOST);
91
+ assertThat(getSize(child.widthMeasureSpecs.get(0)))
71
92
  .isEqualTo(Math.max(activity.getResources().getDisplayMetrics().widthPixels, 1));
72
- assertThat(getMode(child.lastHeightMeasureSpec)).isEqualTo(EXACTLY);
73
- assertThat(getSize(child.lastHeightMeasureSpec)).isEqualTo(Math.max(resolveActionBarSize(activity), 1));
93
+ assertThat(getMode(child.heightMeasureSpecs.get(0))).isEqualTo(EXACTLY);
94
+ assertThat(getSize(child.heightMeasureSpecs.get(0))).isEqualTo(Math.max(resolveActionBarSize(activity), 1));
74
95
  }
75
96
 
76
97
  @Test
77
- public void rtlMissingDimensionsUseBoundedSpecs() {
98
+ public void rtlMissingDimensionsUseBoundedWidthAndFilledHeight() {
78
99
  Activity activity = newActivity();
79
100
  TitleBarReactButtonView uut = createView(activity, new ComponentOptions());
80
- RecordingView child = new RecordingView(activity);
101
+ RecordingContentView child = new RecordingContentView(activity);
81
102
  uut.setLayoutDirection(View.LAYOUT_DIRECTION_RTL);
82
- uut.addView(child, new ViewGroup.LayoutParams(
103
+ setContentView(uut, child);
104
+
105
+ uut.measure(makeMeasureSpec(PARENT_WIDTH, AT_MOST), makeMeasureSpec(PARENT_HEIGHT, AT_MOST));
106
+
107
+ assertThat(child.widthMeasureSpecs.size()).isEqualTo(1);
108
+ assertThat(getMode(child.widthMeasureSpecs.get(0))).isEqualTo(AT_MOST);
109
+ assertThat(getSize(child.widthMeasureSpecs.get(0))).isEqualTo(PARENT_WIDTH);
110
+ assertThat(getMode(child.heightMeasureSpecs.get(0))).isEqualTo(EXACTLY);
111
+ assertThat(getSize(child.heightMeasureSpecs.get(0))).isEqualTo(PARENT_HEIGHT);
112
+ }
113
+
114
+ @Test
115
+ public void contentCenteringReplacesExistingVerticalGravityOnly() {
116
+ Activity activity = newActivity();
117
+ TitleBarReactButtonView uut = createView(activity, new ComponentOptions());
118
+ RecordingContentView child = new RecordingContentView(activity);
119
+ FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
83
120
  ViewGroup.LayoutParams.MATCH_PARENT,
84
121
  ViewGroup.LayoutParams.MATCH_PARENT
85
- ));
122
+ );
123
+ params.gravity = Gravity.TOP | Gravity.RIGHT;
86
124
 
87
- uut.measure(makeMeasureSpec(PARENT_WIDTH, AT_MOST), makeMeasureSpec(PARENT_HEIGHT, AT_MOST));
125
+ uut.addView(child, params);
88
126
 
89
- assertThat(getMode(child.lastWidthMeasureSpec)).isEqualTo(AT_MOST);
90
- assertThat(getSize(child.lastWidthMeasureSpec)).isEqualTo(PARENT_WIDTH);
91
- assertThat(getMode(child.lastHeightMeasureSpec)).isEqualTo(EXACTLY);
92
- assertThat(getSize(child.lastHeightMeasureSpec)).isEqualTo(resolveActionBarSize(activity));
127
+ FrameLayout.LayoutParams updatedParams = (FrameLayout.LayoutParams) child.getLayoutParams();
128
+ assertThat(updatedParams.gravity & Gravity.VERTICAL_GRAVITY_MASK).isEqualTo(Gravity.CENTER_VERTICAL);
129
+ assertThat(updatedParams.gravity & Gravity.HORIZONTAL_GRAVITY_MASK).isEqualTo(Gravity.RIGHT);
93
130
  }
94
131
 
95
132
  private TitleBarReactButtonView createView(Activity activity, ComponentOptions component) {
@@ -98,6 +135,14 @@ public class TitleBarReactButtonViewTest extends BaseTest {
98
135
  return new TitleBarReactButtonView(activity, component);
99
136
  }
100
137
 
138
+ private void setContentView(TitleBarReactButtonView uut, View child) {
139
+ uut.removeAllViews();
140
+ uut.addView(child, new ViewGroup.LayoutParams(
141
+ ViewGroup.LayoutParams.MATCH_PARENT,
142
+ ViewGroup.LayoutParams.MATCH_PARENT
143
+ ));
144
+ }
145
+
101
146
  private int resolveActionBarSize(Activity activity) {
102
147
  TypedValue tv = new TypedValue();
103
148
  if (activity.getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true)) {
@@ -106,30 +151,26 @@ public class TitleBarReactButtonViewTest extends BaseTest {
106
151
  return UiUtils.dpToPx(activity, 48);
107
152
  }
108
153
 
109
- private static class FixedSizeView extends View {
110
- FixedSizeView(Activity activity) {
111
- super(activity);
112
- }
113
-
114
- @Override
115
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
116
- setMeasuredDimension(CHILD_WIDTH, CHILD_HEIGHT);
117
- }
118
- }
119
-
120
- private static class RecordingView extends View {
121
- int lastWidthMeasureSpec;
122
- int lastHeightMeasureSpec;
154
+ private static class RecordingContentView extends View {
155
+ final List<Integer> widthMeasureSpecs = new ArrayList<>();
156
+ final List<Integer> heightMeasureSpecs = new ArrayList<>();
123
157
 
124
- RecordingView(Activity activity) {
158
+ RecordingContentView(Activity activity) {
125
159
  super(activity);
126
160
  }
127
161
 
128
162
  @Override
129
163
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
130
- lastWidthMeasureSpec = widthMeasureSpec;
131
- lastHeightMeasureSpec = heightMeasureSpec;
132
- setMeasuredDimension(getSize(widthMeasureSpec), getSize(heightMeasureSpec));
164
+ widthMeasureSpecs.add(widthMeasureSpec);
165
+ heightMeasureSpecs.add(heightMeasureSpec);
166
+
167
+ int measuredWidth = getMode(widthMeasureSpec) == EXACTLY
168
+ ? getSize(widthMeasureSpec)
169
+ : Math.min(CHILD_WIDTH, getSize(widthMeasureSpec));
170
+ int measuredHeight = getMode(heightMeasureSpec) == EXACTLY
171
+ ? getSize(heightMeasureSpec)
172
+ : Math.min(CHILD_HEIGHT, getSize(heightMeasureSpec));
173
+ setMeasuredDimension(measuredWidth, measuredHeight);
133
174
  }
134
175
  }
135
176
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-navigation",
3
- "version": "8.8.7",
3
+ "version": "8.8.8-snapshot.2616",
4
4
  "description": "React Native Navigation - truly native navigation for iOS and Android",
5
5
  "license": "MIT",
6
6
  "nativePackage": true,