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 );