react-native-navigation 8.8.7 → 8.8.8

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;
@@ -16,6 +18,7 @@ import static com.reactnativenavigation.utils.UiUtils.dpToPx;
16
18
 
17
19
  @SuppressLint("ViewConstructor")
18
20
  public class TitleBarReactButtonView extends ReactView {
21
+ private static final float FINAL_WIDTH_PADDING_DP = 1f;
19
22
  private final ComponentOptions component;
20
23
 
21
24
  public TitleBarReactButtonView(Context context, ComponentOptions component) {
@@ -23,6 +26,17 @@ public class TitleBarReactButtonView extends ReactView {
23
26
  this.component = component;
24
27
  }
25
28
 
29
+ @Override
30
+ public void onViewAdded(View child) {
31
+ super.onViewAdded(child);
32
+ if (child.getLayoutParams() instanceof FrameLayout.LayoutParams) {
33
+ FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) child.getLayoutParams();
34
+ layoutParams.gravity = layoutParams.gravity == -1
35
+ ? Gravity.CENTER_VERTICAL
36
+ : (layoutParams.gravity & ~Gravity.VERTICAL_GRAVITY_MASK) | Gravity.CENTER_VERTICAL;
37
+ }
38
+ }
39
+
26
40
  @Override
27
41
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
28
42
  // This is a workaround, ReactNative throws exception when views have ids, On android MenuItems
@@ -31,32 +45,44 @@ public class TitleBarReactButtonView extends ReactView {
31
45
  this.setId(View.NO_ID);
32
46
  }
33
47
 
34
- super.onMeasure(
35
- createWidthSpec(widthMeasureSpec, component.width),
36
- createHeightSpec(heightMeasureSpec, component.height)
37
- );
38
- }
48
+ int initialWidthSpec = component.width.hasValue()
49
+ ? createExactSpec(component.width)
50
+ : makeMeasureSpec(resolveAvailableWidth(widthMeasureSpec), AT_MOST);
51
+ int initialHeightSpec = createHeightSpec(heightMeasureSpec, component.height);
52
+
53
+ // First discover the content size without forcing every custom button to actionBarSize.
54
+ super.onMeasure(initialWidthSpec, initialHeightSpec);
39
55
 
40
- private int createWidthSpec(int measureSpec, Number dimension) {
41
- return createSpec(measureSpec, dimension, Math.max(getResources().getDisplayMetrics().widthPixels, 1));
56
+ if (component.width.hasValue() && component.height.hasValue()) {
57
+ return;
58
+ }
59
+
60
+ // Then give RN/Yoga a stable exact final box for compatibility with centered button layouts.
61
+ // A small allowance avoids clipping implicit RN padding/subpixel layout while staying content-based.
62
+ int finalWidth = component.width.hasValue()
63
+ ? MeasureSpec.getSize(initialWidthSpec)
64
+ : resolveFinalWidth(getMeasuredWidth());
65
+ int finalHeight = component.height.hasValue()
66
+ ? MeasureSpec.getSize(initialHeightSpec)
67
+ : Math.max(getMeasuredHeight(), 1);
68
+ super.onMeasure(makeMeasureSpec(finalWidth, EXACTLY), makeMeasureSpec(finalHeight, EXACTLY));
42
69
  }
43
70
 
44
71
  private int createHeightSpec(int measureSpec, Number dimension) {
45
72
  if (dimension.hasValue()) {
46
73
  return createExactSpec(dimension);
47
74
  }
48
- return makeMeasureSpec(Math.max(resolveActionBarSize(), 1), EXACTLY);
75
+ int availableSize = MeasureSpec.getSize(measureSpec);
76
+ return makeMeasureSpec(availableSize > 0 ? availableSize : Math.max(resolveActionBarSize(), 1), AT_MOST);
49
77
  }
50
78
 
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
- }
79
+ private int resolveAvailableWidth(int measureSpec) {
80
+ int availableSize = MeasureSpec.getSize(measureSpec);
81
+ return availableSize > 0 ? availableSize : Math.max(getResources().getDisplayMetrics().widthPixels, 1);
82
+ }
83
+
84
+ private int resolveFinalWidth(int measuredContentWidth) {
85
+ return Math.max(measuredContentWidth + (int) Math.ceil(dpToPx(getContext(), FINAL_WIDTH_PADDING_DP)), 1);
60
86
  }
61
87
 
62
88
  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,6 +23,9 @@ 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;
@@ -28,15 +33,25 @@ public class TitleBarReactButtonViewTest extends BaseTest {
28
33
  private static final int CHILD_HEIGHT = 16;
29
34
 
30
35
  @Test
31
- public void missingDimensionsMeasureToContentWithinParentBounds() {
36
+ public void missingDimensionsMeasureToContentThenRemeasureExactForStableAlignment() {
32
37
  Activity activity = newActivity();
33
38
  TitleBarReactButtonView uut = createView(activity, new ComponentOptions());
34
- uut.addView(new FixedSizeView(activity), new ViewGroup.LayoutParams(CHILD_WIDTH, CHILD_HEIGHT));
39
+ RecordingContentView child = new RecordingContentView(activity);
40
+ setContentView(uut, child);
35
41
 
36
42
  uut.measure(makeMeasureSpec(PARENT_WIDTH, AT_MOST), makeMeasureSpec(PARENT_HEIGHT, AT_MOST));
37
43
 
38
- assertThat(uut.getMeasuredWidth()).isEqualTo(CHILD_WIDTH);
39
- assertThat(uut.getMeasuredHeight()).isEqualTo(resolveActionBarSize(activity));
44
+ assertThat(uut.getMeasuredWidth()).isEqualTo(finalWidth(activity));
45
+ assertThat(uut.getMeasuredHeight()).isEqualTo(CHILD_HEIGHT);
46
+ assertThat(child.widthMeasureSpecs.size()).isEqualTo(2);
47
+ assertThat(getMode(child.widthMeasureSpecs.get(0))).isEqualTo(AT_MOST);
48
+ assertThat(getSize(child.widthMeasureSpecs.get(0))).isEqualTo(PARENT_WIDTH);
49
+ assertThat(getMode(child.heightMeasureSpecs.get(0))).isEqualTo(AT_MOST);
50
+ assertThat(getSize(child.heightMeasureSpecs.get(0))).isEqualTo(PARENT_HEIGHT);
51
+ assertThat(getMode(child.widthMeasureSpecs.get(1))).isEqualTo(EXACTLY);
52
+ assertThat(getSize(child.widthMeasureSpecs.get(1))).isEqualTo(finalWidth(activity));
53
+ assertThat(getMode(child.heightMeasureSpecs.get(1))).isEqualTo(EXACTLY);
54
+ assertThat(getSize(child.heightMeasureSpecs.get(1))).isEqualTo(CHILD_HEIGHT);
40
55
  }
41
56
 
42
57
  @Test
@@ -46,50 +61,91 @@ public class TitleBarReactButtonViewTest extends BaseTest {
46
61
  component.width = new Number(72);
47
62
  component.height = new Number(32);
48
63
  TitleBarReactButtonView uut = createView(activity, component);
49
- uut.addView(new FixedSizeView(activity), new ViewGroup.LayoutParams(CHILD_WIDTH, CHILD_HEIGHT));
64
+ RecordingContentView child = new RecordingContentView(activity);
65
+ setContentView(uut, child);
50
66
 
51
67
  uut.measure(makeMeasureSpec(PARENT_WIDTH, AT_MOST), makeMeasureSpec(PARENT_HEIGHT, AT_MOST));
52
68
 
53
69
  assertThat(uut.getMeasuredWidth()).isEqualTo(UiUtils.dpToPx(activity, 72));
54
70
  assertThat(uut.getMeasuredHeight()).isEqualTo(UiUtils.dpToPx(activity, 32));
71
+ assertThat(child.widthMeasureSpecs.size()).isEqualTo(1);
72
+ assertThat(getMode(child.widthMeasureSpecs.get(0))).isEqualTo(EXACTLY);
73
+ assertThat(getSize(child.widthMeasureSpecs.get(0))).isEqualTo(UiUtils.dpToPx(activity, 72));
74
+ assertThat(getMode(child.heightMeasureSpecs.get(0))).isEqualTo(EXACTLY);
75
+ assertThat(getSize(child.heightMeasureSpecs.get(0))).isEqualTo(UiUtils.dpToPx(activity, 32));
55
76
  }
56
77
 
57
78
  @Test
58
- public void zeroParentWidthFallbacksToBoundedAtMostSpecAndHeightUsesActionBarSize() {
79
+ public void zeroParentSpecsFallbackToBoundedAtMostSpecs() {
59
80
  Activity activity = newActivity();
60
81
  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
- ));
82
+ RecordingContentView child = new RecordingContentView(activity);
83
+ setContentView(uut, child);
66
84
 
67
85
  uut.measure(makeMeasureSpec(0, AT_MOST), makeMeasureSpec(0, AT_MOST));
68
86
 
69
- assertThat(getMode(child.lastWidthMeasureSpec)).isEqualTo(AT_MOST);
70
- assertThat(getSize(child.lastWidthMeasureSpec))
87
+ assertThat(child.widthMeasureSpecs.size()).isEqualTo(2);
88
+ assertThat(getMode(child.widthMeasureSpecs.get(0))).isEqualTo(AT_MOST);
89
+ assertThat(getSize(child.widthMeasureSpecs.get(0)))
71
90
  .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));
91
+ assertThat(getMode(child.widthMeasureSpecs.get(1))).isEqualTo(EXACTLY);
92
+ assertThat(getSize(child.widthMeasureSpecs.get(1))).isEqualTo(finalWidth(activity));
93
+ assertThat(getMode(child.heightMeasureSpecs.get(0))).isEqualTo(AT_MOST);
94
+ assertThat(getSize(child.heightMeasureSpecs.get(0))).isEqualTo(Math.max(resolveActionBarSize(activity), 1));
95
+ assertThat(getMode(child.heightMeasureSpecs.get(1))).isEqualTo(EXACTLY);
96
+ assertThat(getSize(child.heightMeasureSpecs.get(1))).isEqualTo(CHILD_HEIGHT);
74
97
  }
75
98
 
76
99
  @Test
77
100
  public void rtlMissingDimensionsUseBoundedSpecs() {
78
101
  Activity activity = newActivity();
79
102
  TitleBarReactButtonView uut = createView(activity, new ComponentOptions());
80
- RecordingView child = new RecordingView(activity);
103
+ RecordingContentView child = new RecordingContentView(activity);
81
104
  uut.setLayoutDirection(View.LAYOUT_DIRECTION_RTL);
82
- uut.addView(child, new ViewGroup.LayoutParams(
105
+ setContentView(uut, child);
106
+
107
+ uut.measure(makeMeasureSpec(PARENT_WIDTH, AT_MOST), makeMeasureSpec(PARENT_HEIGHT, AT_MOST));
108
+
109
+ assertThat(child.widthMeasureSpecs.size()).isEqualTo(2);
110
+ assertThat(getMode(child.widthMeasureSpecs.get(0))).isEqualTo(AT_MOST);
111
+ assertThat(getSize(child.widthMeasureSpecs.get(0))).isEqualTo(PARENT_WIDTH);
112
+ assertThat(getMode(child.widthMeasureSpecs.get(1))).isEqualTo(EXACTLY);
113
+ assertThat(getSize(child.widthMeasureSpecs.get(1))).isEqualTo(finalWidth(activity));
114
+ assertThat(getMode(child.heightMeasureSpecs.get(1))).isEqualTo(EXACTLY);
115
+ assertThat(getSize(child.heightMeasureSpecs.get(1))).isEqualTo(CHILD_HEIGHT);
116
+ }
117
+
118
+ @Test
119
+ public void contentRemainsCenteredWhenMenuCellLaysButtonOutTallerThanMeasuredHeight() {
120
+ Activity activity = newActivity();
121
+ TitleBarReactButtonView uut = createView(activity, new ComponentOptions());
122
+ RecordingContentView child = new RecordingContentView(activity);
123
+ setContentView(uut, child);
124
+
125
+ uut.measure(makeMeasureSpec(PARENT_WIDTH, AT_MOST), makeMeasureSpec(PARENT_HEIGHT, AT_MOST));
126
+ uut.layout(0, 0, uut.getMeasuredWidth(), PARENT_HEIGHT);
127
+
128
+ assertThat(uut.getMeasuredHeight()).isEqualTo(CHILD_HEIGHT);
129
+ assertThat(child.getTop()).isEqualTo((PARENT_HEIGHT - CHILD_HEIGHT) / 2);
130
+ assertThat(child.getBottom()).isEqualTo((PARENT_HEIGHT + CHILD_HEIGHT) / 2);
131
+ }
132
+
133
+ @Test
134
+ public void contentCenteringReplacesExistingVerticalGravityOnly() {
135
+ Activity activity = newActivity();
136
+ TitleBarReactButtonView uut = createView(activity, new ComponentOptions());
137
+ RecordingContentView child = new RecordingContentView(activity);
138
+ FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
83
139
  ViewGroup.LayoutParams.MATCH_PARENT,
84
140
  ViewGroup.LayoutParams.MATCH_PARENT
85
- ));
141
+ );
142
+ params.gravity = Gravity.TOP | Gravity.RIGHT;
86
143
 
87
- uut.measure(makeMeasureSpec(PARENT_WIDTH, AT_MOST), makeMeasureSpec(PARENT_HEIGHT, AT_MOST));
144
+ uut.addView(child, params);
88
145
 
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));
146
+ FrameLayout.LayoutParams updatedParams = (FrameLayout.LayoutParams) child.getLayoutParams();
147
+ assertThat(updatedParams.gravity & Gravity.VERTICAL_GRAVITY_MASK).isEqualTo(Gravity.CENTER_VERTICAL);
148
+ assertThat(updatedParams.gravity & Gravity.HORIZONTAL_GRAVITY_MASK).isEqualTo(Gravity.RIGHT);
93
149
  }
94
150
 
95
151
  private TitleBarReactButtonView createView(Activity activity, ComponentOptions component) {
@@ -98,6 +154,14 @@ public class TitleBarReactButtonViewTest extends BaseTest {
98
154
  return new TitleBarReactButtonView(activity, component);
99
155
  }
100
156
 
157
+ private void setContentView(TitleBarReactButtonView uut, View child) {
158
+ uut.removeAllViews();
159
+ uut.addView(child, new ViewGroup.LayoutParams(
160
+ ViewGroup.LayoutParams.MATCH_PARENT,
161
+ ViewGroup.LayoutParams.MATCH_PARENT
162
+ ));
163
+ }
164
+
101
165
  private int resolveActionBarSize(Activity activity) {
102
166
  TypedValue tv = new TypedValue();
103
167
  if (activity.getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true)) {
@@ -106,30 +170,30 @@ public class TitleBarReactButtonViewTest extends BaseTest {
106
170
  return UiUtils.dpToPx(activity, 48);
107
171
  }
108
172
 
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
- }
173
+ private int finalWidth(Activity activity) {
174
+ return CHILD_WIDTH + (int) Math.ceil(UiUtils.dpToPx(activity, 1f));
118
175
  }
119
176
 
120
- private static class RecordingView extends View {
121
- int lastWidthMeasureSpec;
122
- int lastHeightMeasureSpec;
177
+ private static class RecordingContentView extends View {
178
+ final List<Integer> widthMeasureSpecs = new ArrayList<>();
179
+ final List<Integer> heightMeasureSpecs = new ArrayList<>();
123
180
 
124
- RecordingView(Activity activity) {
181
+ RecordingContentView(Activity activity) {
125
182
  super(activity);
126
183
  }
127
184
 
128
185
  @Override
129
186
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
130
- lastWidthMeasureSpec = widthMeasureSpec;
131
- lastHeightMeasureSpec = heightMeasureSpec;
132
- setMeasuredDimension(getSize(widthMeasureSpec), getSize(heightMeasureSpec));
187
+ widthMeasureSpecs.add(widthMeasureSpec);
188
+ heightMeasureSpecs.add(heightMeasureSpec);
189
+
190
+ int measuredWidth = getMode(widthMeasureSpec) == EXACTLY
191
+ ? getSize(widthMeasureSpec)
192
+ : Math.min(CHILD_WIDTH, getSize(widthMeasureSpec));
193
+ int measuredHeight = getMode(heightMeasureSpec) == EXACTLY
194
+ ? getSize(heightMeasureSpec)
195
+ : Math.min(CHILD_HEIGHT, getSize(heightMeasureSpec));
196
+ setMeasuredDimension(measuredWidth, measuredHeight);
133
197
  }
134
198
  }
135
199
  }
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",
4
4
  "description": "React Native Navigation - truly native navigation for iOS and Android",
5
5
  "license": "MIT",
6
6
  "nativePackage": true,