diff --git a/Libraries/Text/TextStylePropTypes.js b/Libraries/Text/TextStylePropTypes.js
index 4c84de66dab394..211e97bf9508a5 100644
--- a/Libraries/Text/TextStylePropTypes.js
+++ b/Libraries/Text/TextStylePropTypes.js
@@ -49,7 +49,7 @@ const TextStylePropTypes = {
textShadowRadius: ReactPropTypes.number,
textShadowColor: ColorPropType,
/**
- * @platform ios
+ * Specifies the letter spacing (kerning).
*/
letterSpacing: ReactPropTypes.number,
lineHeight: ReactPropTypes.number,
diff --git a/RNTester/js/TextExample.android.js b/RNTester/js/TextExample.android.js
index ec9ce196300764..f0e72265bc8d35 100644
--- a/RNTester/js/TextExample.android.js
+++ b/RNTester/js/TextExample.android.js
@@ -288,6 +288,20 @@ class TextExample extends React.Component {
right right right right right right right right right right right right right
+
+
+ letterSpacing = 0
+
+
+ letterSpacing = 2
+
+
+ letterSpacing = 9
+
+
+ letterSpacing = -1
+
+
diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewProps.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewProps.java
index 67265436ae9eec..48c919e5767477 100644
--- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewProps.java
+++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewProps.java
@@ -80,6 +80,7 @@ public class ViewProps {
public static final String FONT_STYLE = "fontStyle";
public static final String FONT_FAMILY = "fontFamily";
public static final String LINE_HEIGHT = "lineHeight";
+ public static final String LETTER_SPACING = "letterSpacing";
public static final String NEEDS_OFFSCREEN_ALPHA_COMPOSITING = "needsOffscreenAlphaCompositing";
public static final String NUMBER_OF_LINES = "numberOfLines";
public static final String ELLIPSIZE_MODE = "ellipsizeMode";
diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextShadowNode.java b/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextShadowNode.java
index dbdcc2963b6304..1e758ef65c3233 100644
--- a/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextShadowNode.java
+++ b/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextShadowNode.java
@@ -330,6 +330,7 @@ private static int parseNumericFontWeight(String fontWeightString) {
}
private float mLineHeight = Float.NaN;
+ private float mLetterSpacing = Float.NaN;
private boolean mIsColorSet = false;
private boolean mAllowFontScaling = true;
private int mColor;
@@ -389,6 +390,14 @@ public ReactTextShadowNode() {
}
}
+ public float getLetterSpacing() {
+ return mLetterSpacing;
+ }
+
+ public int getFontSize() {
+ return mFontSize;
+ }
+
// Returns a line height which takes into account the requested line height
// and the height of the inline images.
public float getEffectiveLineHeight() {
@@ -463,6 +472,12 @@ public void setAllowFontScaling(boolean allowFontScaling) {
}
}
+ @ReactProp(name = ViewProps.LETTER_SPACING, defaultFloat = UNSET)
+ public void setLetterSpacing(float letterSpacing) {
+ mLetterSpacing = letterSpacing == UNSET ? Float.NaN : letterSpacing;
+ markUpdated();
+ }
+
@ReactProp(name = ViewProps.TEXT_ALIGN)
public void setTextAlign(@Nullable String textAlign) {
if (textAlign == null || "auto".equals(textAlign)) {
@@ -652,6 +667,8 @@ public void onCollectExtraUpdates(UIViewOperationQueue uiViewOperationQueue) {
getPadding(Spacing.TOP),
getPadding(Spacing.END),
getPadding(Spacing.BOTTOM),
+ getLetterSpacing(),
+ getFontSize(),
getTextAlign(),
mTextBreakStrategy
);
diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextUpdate.java b/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextUpdate.java
index 9f67aec6180dee..066df2a4cae463 100644
--- a/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextUpdate.java
+++ b/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextUpdate.java
@@ -26,6 +26,8 @@ public class ReactTextUpdate {
private final float mPaddingTop;
private final float mPaddingRight;
private final float mPaddingBottom;
+ private final float mLetterSpacing;
+ private final int mFontSize;
private final int mTextAlign;
private final int mTextBreakStrategy;
@@ -50,6 +52,8 @@ public ReactTextUpdate(
paddingTop,
paddingEnd,
paddingBottom,
+ 0f,
+ 12,
textAlign,
Layout.BREAK_STRATEGY_HIGH_QUALITY);
}
@@ -62,6 +66,8 @@ public ReactTextUpdate(
float paddingTop,
float paddingEnd,
float paddingBottom,
+ float letterSpacing,
+ int fontSize,
int textAlign,
int textBreakStrategy) {
mText = text;
@@ -71,6 +77,8 @@ public ReactTextUpdate(
mPaddingTop = paddingTop;
mPaddingRight = paddingEnd;
mPaddingBottom = paddingBottom;
+ mLetterSpacing = letterSpacing;
+ mFontSize = fontSize;
mTextAlign = textAlign;
mTextBreakStrategy = textBreakStrategy;
}
@@ -103,6 +111,14 @@ public float getPaddingBottom() {
return mPaddingBottom;
}
+ public float getLetterSpacing() {
+ return mLetterSpacing;
+ }
+
+ public int getFontSize() {
+ return mFontSize;
+ }
+
public int getTextAlign() {
return mTextAlign;
}
diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextView.java b/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextView.java
index fa8f87710b1b8f..dc8294f46a1d34 100644
--- a/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextView.java
+++ b/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextView.java
@@ -23,6 +23,8 @@
import android.view.ViewGroup;
import android.widget.TextView;
+import com.facebook.react.uimanager.FloatUtil;
+import com.facebook.react.uimanager.PixelUtil;
import com.facebook.react.uimanager.ReactCompoundView;
import com.facebook.react.uimanager.ViewDefaults;
import com.facebook.react.views.view.ReactViewBackgroundDrawable;
@@ -37,6 +39,7 @@ public class ReactTextView extends TextView implements ReactCompoundView {
private int mDefaultGravityVertical;
private boolean mTextIsSelectable;
private float mLineHeight = Float.NaN;
+ private float mLetterSpacing = Float.NaN;
private int mTextAlign = Gravity.NO_GRAVITY;
private int mNumberOfLines = ViewDefaults.NUMBER_OF_LINES;
private TextUtils.TruncateAt mEllipsizeLocation = TextUtils.TruncateAt.END;
@@ -65,6 +68,22 @@ public void setText(ReactTextUpdate update) {
(int) Math.floor(update.getPaddingRight()),
(int) Math.floor(update.getPaddingBottom()));
+ // API 21+: https://developer.android.com/reference/android/widget/TextView.html#setLetterSpacing(float)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ float nextLetterSpacing = update.getLetterSpacing();
+ int fontSize = update.getFontSize();
+ if (!FloatUtil.floatsEqual(mLetterSpacing, nextLetterSpacing)) {
+ mLetterSpacing = nextLetterSpacing;
+ if(Float.isNaN(mLetterSpacing)) {
+ setLetterSpacing((float) 0.0);
+ } else {
+ // Calculate EM from proper font pixels
+ // This does PX to EM conversions since Android does letterSpacing in EM
+ setLetterSpacing(1 + (mLetterSpacing - PixelUtil.toDIPFromPixel(fontSize)) / PixelUtil.toDIPFromPixel(fontSize));
+ }
+ }
+ }
+
int nextTextAlign = update.getTextAlign();
if (mTextAlign != nextTextAlign) {
mTextAlign = nextTextAlign;
diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputShadowNode.java b/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputShadowNode.java
index 2e6c5493c920ed..431b4ea7cf0d71 100644
--- a/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputShadowNode.java
+++ b/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputShadowNode.java
@@ -86,6 +86,11 @@ public long measure(
mFontSize == UNSET ?
(int) Math.ceil(PixelUtil.toPixelFromSP(ViewDefaults.FONT_SIZE_SP)) : mFontSize);
+ // API 21+: https://developer.android.com/reference/android/widget/TextView.html#setLetterSpacing(float)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ editText.setLetterSpacing(getLetterSpacing());
+ }
+
if (mNumberOfLines != UNSET) {
editText.setLines(mNumberOfLines);
}
@@ -146,6 +151,8 @@ public void onCollectExtraUpdates(UIViewOperationQueue uiViewOperationQueue) {
getPadding(Spacing.TOP),
getPadding(Spacing.RIGHT),
getPadding(Spacing.BOTTOM),
+ getLetterSpacing(),
+ getFontSize(),
mTextAlign,
mTextBreakStrategy
);