Skip to content

Commit 0e161a9

Browse files
committed
Create flatSizes function that also retrieves widths
1 parent 7f495d1 commit 0e161a9

File tree

6 files changed

+210
-124
lines changed

6 files changed

+210
-124
lines changed

CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
66

77
### Added
88

9-
- Updated README.md with example for flatHeights - Thanks to @donni106
9+
- Update README.md with example for flatHeights - Thanks to @donni106
10+
- Create new flatSizes function that also gets the widths
11+
- Add support for `numberOfLines` and `maxWidth/maxHeight` dimensions.
1012

1113
### Changed
1214

README.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ See [Manual Installation][2] on the Wiki as an alternative if you have problems
6060

6161
- [`measure`](#measure)
6262

63-
- [`flatHeights`](#flatheights)
63+
- [`flatHeights` and `flatSizes`](#flatheights)
6464

6565
- [`specsForTextStyles`](#specsfortextstyles)
6666

@@ -199,9 +199,10 @@ class Test extends Component<Props, State> {
199199

200200
```ts
201201
flatHeights(options: TSHeightsParams): Promise<number[]>
202+
flatSizes(options: TSHeightsParams): Promise<TSFlatSizes>
202203
```
203204

204-
Calculate the height of each of the strings in an array.
205+
Calculate the height (or sizes) of each of the strings in an array.
205206

206207
This is an alternative to `measure` designed for cases in which you have to calculate the height of numerous text blocks with common characteristics (width, font, etc), a typical use case with `<FlatList>` or `<RecyclerListView>` components.
207208

@@ -214,6 +215,8 @@ I did tests on 5,000 random text blocks and these were the results (ms):
214215
Android | 49,624 | 1,091
215216
iOS | 1,949 | 732
216217

218+
Note that `flatSizes` is somewhat slower than `flatHeights` on Android since it needs to iterate through lines to find the maximum line length.
219+
217220
In the future I will prepare an example of its use with FlatList and multiple styles on the same card.
218221

219222
### TSHeightsParams

android/src/main/java/com/github/amarcruz/rntextsize/RNTextSizeModule.java

Lines changed: 88 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -163,61 +163,26 @@ public void measure(@Nullable final ReadableMap specs, final Promise promise) {
163163
}
164164
}
165165

166-
// https://stackoverflow.com/questions/3654321/measuring-text-height-to-be-drawn-on-canvas-android
166+
/**
167+
* Retrieves sizes of each entry in an array of strings rendered with the same style.
168+
*
169+
* https://stackoverflow.com/questions/3654321/measuring-text-height-to-be-drawn-on-canvas-android
170+
*/
167171
@SuppressWarnings("unused")
168172
@ReactMethod
169-
public void flatHeights(@Nullable final ReadableMap specs, final Promise promise) {
170-
final RNTextSizeConf conf = getConf(specs, promise, true);
171-
if (conf == null) {
172-
return;
173-
}
174-
175-
final ReadableArray texts = conf.getArray("text");
176-
if (texts == null) {
177-
promise.reject(E_MISSING_TEXT, "Missing required text, must be an array.");
178-
return;
179-
}
180-
181-
final float density = getCurrentDensity();
182-
final float width = conf.getWidth(density);
183-
final boolean includeFontPadding = conf.includeFontPadding;
184-
final int textBreakStrategy = conf.getTextBreakStrategy();
185-
186-
final WritableArray result = Arguments.createArray();
187-
188-
final SpannableStringBuilder sb = new SpannableStringBuilder(" ");
189-
RNTextSizeSpannedText.spannedFromSpecsAndText(mReactContext, conf, sb);
190-
191-
final TextPaint textPaint = new TextPaint(TextPaint.ANTI_ALIAS_FLAG);
192-
Layout layout;
193-
try {
194-
195-
for (int ix = 0; ix < texts.size(); ix++) {
196-
197-
// If this element is `null` or another type, return zero
198-
if (texts.getType(ix) != ReadableType.String) {
199-
result.pushInt(0);
200-
continue;
201-
}
202-
203-
final String text = texts.getString(ix);
204-
205-
// If empty, return the minimum height of <Text> components
206-
if (text.isEmpty()) {
207-
result.pushDouble(minimalHeight(density, includeFontPadding));
208-
continue;
209-
}
210-
211-
// Reset the SB text, the attrs will expand to its full length
212-
sb.replace(0, sb.length(), text);
213-
layout = buildStaticLayout(conf, includeFontPadding, sb, textPaint, (int) width);
214-
result.pushDouble(layout.getHeight() / density);
215-
}
173+
public void flatSizes(@Nullable final ReadableMap specs, final Promise promise) {
174+
flatHeightsInner(specs, promise, true);
175+
}
216176

217-
promise.resolve(result);
218-
} catch (Exception e) {
219-
promise.reject(E_UNKNOWN_ERROR, e);
220-
}
177+
/**
178+
* Retrieves heights of each entry in an array of strings rendered with the same style.
179+
*
180+
* https://stackoverflow.com/questions/3654321/measuring-text-height-to-be-drawn-on-canvas-android
181+
*/
182+
@SuppressWarnings("unused")
183+
@ReactMethod
184+
public void flatHeights(@Nullable final ReadableMap specs, final Promise promise) {
185+
flatHeightsInner(specs, promise, false);
221186
}
222187

223188
/**
@@ -313,6 +278,77 @@ public void fontNamesForFamilyName(final String ignored, final Promise promise)
313278
//
314279
// ============================================================================
315280

281+
private void flatHeightsInner(@Nullable final ReadableMap specs, final Promise promise, boolean includeWidths) {
282+
final RNTextSizeConf conf = getConf(specs, promise, true);
283+
if (conf == null) {
284+
return;
285+
}
286+
287+
final ReadableArray texts = conf.getArray("text");
288+
if (texts == null) {
289+
promise.reject(E_MISSING_TEXT, "Missing required text, must be an array.");
290+
return;
291+
}
292+
293+
final float density = getCurrentDensity();
294+
final float width = conf.getWidth(density);
295+
final boolean includeFontPadding = conf.includeFontPadding;
296+
final int textBreakStrategy = conf.getTextBreakStrategy();
297+
298+
final WritableArray heights = Arguments.createArray();
299+
final WritableArray widths = Arguments.createArray();
300+
301+
final SpannableStringBuilder sb = new SpannableStringBuilder(" ");
302+
RNTextSizeSpannedText.spannedFromSpecsAndText(mReactContext, conf, sb);
303+
304+
final TextPaint textPaint = new TextPaint(TextPaint.ANTI_ALIAS_FLAG);
305+
Layout layout;
306+
try {
307+
308+
for (int ix = 0; ix < texts.size(); ix++) {
309+
310+
// If this element is `null` or another type, return zero
311+
if (texts.getType(ix) != ReadableType.String) {
312+
heights.pushInt(0);
313+
continue;
314+
}
315+
316+
final String text = texts.getString(ix);
317+
318+
// If empty, return the minimum height of <Text> components
319+
if (text.isEmpty()) {
320+
heights.pushDouble(minimalHeight(density, includeFontPadding));
321+
continue;
322+
}
323+
324+
// Reset the SB text, the attrs will expand to its full length
325+
sb.replace(0, sb.length(), text);
326+
layout = buildStaticLayout(conf, includeFontPadding, sb, textPaint, (int) width);
327+
heights.pushDouble(layout.getHeight() / density);
328+
329+
if (includeWidths) {
330+
final int lineCount = layout.getLineCount();
331+
float measuredWidth = 0;
332+
for (int i = 0; i < lineCount; i++) {
333+
measuredWidth = Math.max(measuredWidth, layout.getLineMax(i));
334+
}
335+
widths.pushDouble(measuredWidth / density);
336+
}
337+
}
338+
339+
if (includeWidths) {
340+
final WritableMap output = Arguments.createMap();
341+
output.putArray("widths", widths);
342+
output.putArray("heights", heights);
343+
promise.resolve(output);
344+
} else {
345+
promise.resolve(heights);
346+
}
347+
} catch (Exception e) {
348+
promise.reject(E_UNKNOWN_ERROR, e);
349+
}
350+
}
351+
316352
@Nullable
317353
private RNTextSizeConf getConf(final ReadableMap specs, final Promise promise, boolean forText) {
318354
if (specs == null) {

index.d.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ declare module "react-native-text-size" {
55
export type TSFontVariant = 'small-caps' | 'oldstyle-nums' | 'lining-nums' | 'tabular-nums' | 'proportional-nums'
66
export type TSTextBreakStrategy = 'simple' | 'highQuality' | 'balanced'
77

8-
export type TSFontSize = {
8+
export interface TSFontSize {
99
readonly default: number,
1010
readonly button: number,
1111
readonly label: number,
@@ -41,7 +41,7 @@ declare module "react-native-text-size" {
4141
| 'title2'
4242
| 'title3'
4343

44-
export type TSFontInfo = {
44+
export interface TSFontInfo {
4545
fontFamily: string | null,
4646
fontName?: string | null,
4747
fontWeight: TSFontWeight,
@@ -88,7 +88,7 @@ declare module "react-native-text-size" {
8888
textBreakStrategy?: TSTextBreakStrategy;
8989
}
9090

91-
export type TSFontForStyle = {
91+
export interface TSFontForStyle {
9292
fontFamily: string,
9393
/** Unscaled font size, untits are SP in Android, points in iOS */
9494
fontSize: number,
@@ -143,7 +143,7 @@ declare module "react-native-text-size" {
143143
lineInfoForLine?: number;
144144
}
145145

146-
export type TSMeasureResult = {
146+
export interface TSMeasureResult {
147147
/**
148148
* Total used width. It may be less or equal to the `width` option.
149149
*
@@ -185,8 +185,14 @@ declare module "react-native-text-size" {
185185
};
186186
}
187187

188+
export interface TSFlatSizes {
189+
widths: number[];
190+
heights: number[];
191+
}
192+
188193
interface TextSizeStatic {
189194
measure(params: TSMeasureParams): Promise<TSMeasureResult>;
195+
flatSizes(params: TSHeightsParams): Promise<TSFlatSizes>;
190196
flatHeights(params: TSHeightsParams): Promise<number[]>;
191197
specsForTextStyles(): Promise<{ [key: string]: TSFontForStyle }>;
192198
fontFromSpecs(specs?: TSFontSpecs): Promise<TSFontInfo>;

index.js.flow

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,8 +184,14 @@ export interface TSMeasureResult {
184184
}
185185
}
186186

187+
export interface TSFlatSizes {
188+
widths: number[];
189+
heights: number[];
190+
}
191+
187192
declare interface TextSizeStatic {
188193
measure(params: TSMeasureParams): Promise<TSMeasureResult>;
194+
flatSizes(params: TSHeightsParams): Promise<TSFlatSizes>;
189195
flatHeights(params: TSHeightsParams): Promise<number[]>;
190196
specsForTextStyles(): Promise<{ [string]: TSFontForStyle }>;
191197
fontFromSpecs(specs: TSFontSpecs): Promise<TSFontInfo>;

0 commit comments

Comments
 (0)