field public static final int fadingEdge = 16842975; // 0x10100df
field public static final int fadingEdgeLength = 16842976; // 0x10100e0
field public static final int fadingMode = 16843745; // 0x10103e1
+ field public static final int fallbackLineSpacing = 16844155; // 0x101057b
field public static final int fastScrollAlwaysVisible = 16843573; // 0x1010335
field public static final int fastScrollEnabled = 16843302; // 0x1010226
field public static final int fastScrollOverlayPosition = 16843578; // 0x101033a
method public boolean isAllCaps();
method public boolean isCursorVisible();
method public boolean isElegantTextHeight();
+ method public boolean isFallbackLineSpacing();
method public boolean isInputMethodTarget();
method public boolean isSuggestionsEnabled();
method public boolean isTextSelectable();
method public void setError(java.lang.CharSequence);
method public void setError(java.lang.CharSequence, android.graphics.drawable.Drawable);
method public void setExtractedText(android.view.inputmethod.ExtractedText);
+ method public void setFallbackLineSpacing(boolean);
method public void setFilters(android.text.InputFilter[]);
method public void setFontFeatureSettings(java.lang.String);
method public boolean setFontVariationSettings(java.lang.String);
* @attr ref android.R.styleable#TextView_imeActionId
* @attr ref android.R.styleable#TextView_editorExtras
* @attr ref android.R.styleable#TextView_elegantTextHeight
+ * @attr ref android.R.styleable#TextView_fallbackLineSpacing
* @attr ref android.R.styleable#TextView_letterSpacing
* @attr ref android.R.styleable#TextView_fontFeatureSettings
* @attr ref android.R.styleable#TextView_breakStrategy
// True if internationalized input should be used for numbers and date and time.
private final boolean mUseInternationalizedInput;
// True if fallback fonts that end up getting used should be allowed to affect line spacing.
- /* package */ final boolean mUseFallbackLineSpacing;
+ /* package */ boolean mUseFallbackLineSpacing;
@ViewDebug.ExportedProperty(category = "text")
private int mGravity = Gravity.TOP | Gravity.START;
float mShadowDx = 0, mShadowDy = 0, mShadowRadius = 0;
boolean mHasElegant = false;
boolean mElegant = false;
+ boolean mHasFallbackLineSpacing = false;
+ boolean mFallbackLineSpacing = false;
boolean mHasLetterSpacing = false;
float mLetterSpacing = 0;
String mFontFeatureSettings = null;
+ " mShadowRadius:" + mShadowRadius + "\n"
+ " mHasElegant:" + mHasElegant + "\n"
+ " mElegant:" + mElegant + "\n"
+ + " mHasFallbackLineSpacing:" + mHasFallbackLineSpacing + "\n"
+ + " mFallbackLineSpacing:" + mFallbackLineSpacing + "\n"
+ " mHasLetterSpacing:" + mHasLetterSpacing + "\n"
+ " mLetterSpacing:" + mLetterSpacing + "\n"
+ " mFontFeatureSettings:" + mFontFeatureSettings + "\n"
com.android.internal.R.styleable.TextAppearance_shadowRadius);
sAppearanceValues.put(com.android.internal.R.styleable.TextView_elegantTextHeight,
com.android.internal.R.styleable.TextAppearance_elegantTextHeight);
+ sAppearanceValues.put(com.android.internal.R.styleable.TextView_fallbackLineSpacing,
+ com.android.internal.R.styleable.TextAppearance_fallbackLineSpacing);
sAppearanceValues.put(com.android.internal.R.styleable.TextView_letterSpacing,
com.android.internal.R.styleable.TextAppearance_letterSpacing);
sAppearanceValues.put(com.android.internal.R.styleable.TextView_fontFeatureSettings,
attributes.mHasElegant = true;
attributes.mElegant = appearance.getBoolean(attr, attributes.mElegant);
break;
+ case com.android.internal.R.styleable.TextAppearance_fallbackLineSpacing:
+ attributes.mHasFallbackLineSpacing = true;
+ attributes.mFallbackLineSpacing = appearance.getBoolean(attr,
+ attributes.mFallbackLineSpacing);
+ break;
case com.android.internal.R.styleable.TextAppearance_letterSpacing:
attributes.mHasLetterSpacing = true;
attributes.mLetterSpacing =
setElegantTextHeight(attributes.mElegant);
}
+ if (attributes.mHasFallbackLineSpacing) {
+ setFallbackLineSpacing(attributes.mFallbackLineSpacing);
+ }
+
if (attributes.mHasLetterSpacing) {
setLetterSpacing(attributes.mLetterSpacing);
}
*
* @param elegant set the paint's elegant metrics flag.
*
- * @see Paint#isElegantTextHeight(boolean)
+ * @see Paint#isElegantTextHeight()
*
* @attr ref android.R.styleable#TextView_elegantTextHeight
*/
}
/**
+ * Set whether to respect the ascent and descent of the fallback fonts that are used in
+ * displaying the text (which is needed to avoid text from consecutive lines running into
+ * each other). If set, fallback fonts that end up getting used can increase the ascent
+ * and descent of the lines that they are used on.
+ * <p/>
+ * It is required to be true if text could be in languages like Burmese or Tibetan where text
+ * is typically much taller or deeper than Latin text.
+ *
+ * @param enabled whether to expand linespacing based on fallback fonts, {@code true} by default
+ *
+ * @see StaticLayout.Builder#setUseLineSpacingFromFallbacks(boolean)
+ *
+ * @attr ref android.R.styleable#TextView_fallbackLineSpacing
+ */
+ public void setFallbackLineSpacing(boolean enabled) {
+ if (mUseFallbackLineSpacing != enabled) {
+ mUseFallbackLineSpacing = enabled;
+ if (mLayout != null) {
+ nullLayouts();
+ requestLayout();
+ invalidate();
+ }
+ }
+ }
+
+ /**
+ * @return whether fallback line spacing is enabled, {@code true} by default
+ *
+ * @see #setFallbackLineSpacing(boolean)
+ *
+ * @attr ref android.R.styleable#TextView_fallbackLineSpacing
+ */
+ public boolean isFallbackLineSpacing() {
+ return mUseFallbackLineSpacing;
+ }
+
+ /**
* Get the value of the TextView's elegant height metrics flag. This setting selects font
* variants that have not been compacted to fit Latin-based vertical
* metrics, and also increases top and bottom bounds to provide more space.
<attr name="shadowRadius" format="float" />
<!-- Elegant text height, especially for less compacted complex script text. -->
<attr name="elegantTextHeight" format="boolean" />
+ <!-- Whether to respect the ascent and descent of the fallback fonts that are used in
+ displaying the text. When true, fallback fonts that end up getting used can increase
+ the ascent and descent of the lines that they are used on. -->
+ <attr name="fallbackLineSpacing" format="boolean"/>
<!-- Text letter-spacing. -->
<attr name="letterSpacing" format="float" />
<!-- Font feature settings. -->
<attr name="textAllCaps" />
<!-- Elegant text height, especially for less compacted complex script text. -->
<attr name="elegantTextHeight" />
+ <!-- Whether to respect the ascent and descent of the fallback fonts that are used in
+ displaying the text. When true, fallback fonts that end up getting used can increase
+ the ascent and descent of the lines that they are used on. -->
+ <attr name="fallbackLineSpacing" format="boolean"/>
<!-- Text letter-spacing. -->
<attr name="letterSpacing" />
<!-- Font feature settings. -->
<public name="isVrOnly"/>
<public name="widgetFeatures" />
<public name="appComponentFactory" />
+ <public name="fallbackLineSpacing" />
</public-group>
<public-group type="style" first-id="0x010302e0">
--- /dev/null
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.widget;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.app.Activity;
+import android.support.test.filters.MediumTest;
+import android.support.test.rule.ActivityTestRule;
+import android.text.DynamicLayout;
+import android.text.FontFallbackSetup;
+import android.text.Layout;
+import android.text.StaticLayout;
+import android.util.TypedValue;
+import android.view.View;
+import android.widget.TextView.BufferType;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+/**
+ * Parametrized test for TextView#setFallbackLineSpacing.
+ */
+@MediumTest
+@RunWith(Parameterized.class)
+public class TextViewFallbackLineSpacingTest {
+
+ @Parameterized.Parameters(name = "{0}")
+ public static Collection layouts() {
+ return Arrays.asList(new Object[][] {
+ // name, enabled, BufferType
+ { "Enabled - StaticLayout", true, BufferType.NORMAL},
+ { "Disabled - StaticLayout", false, BufferType.NORMAL},
+ { "Enabled - DynamicLayout", true, BufferType.EDITABLE},
+ { "Disabled - DynamicLayout", false, BufferType.EDITABLE},
+ });
+ }
+
+ @Rule
+ public ActivityTestRule<TextViewActivity> mActivityRule = new ActivityTestRule<>(
+ TextViewActivity.class);
+
+ private final boolean mEnabled;
+ private final BufferType mBufferType;
+
+ public TextViewFallbackLineSpacingTest(String testName, boolean enabled,
+ BufferType bufferType) {
+ mEnabled = enabled;
+ mBufferType = bufferType;
+ }
+
+ @Test
+ public void testFallbackLineSpacing() {
+ // All glyphs in the fonts are 1em wide.
+ final String[] testFontFiles = {
+ // ascent == 1em, descent == 2em, only supports 'a' and space
+ "ascent1em-descent2em.ttf",
+ // ascent == 3em, descent == 4em, only supports 'b'
+ "ascent3em-descent4em.ttf"
+ };
+ final String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+ + "<familyset>"
+ + " <family name='sans-serif'>"
+ + " <font weight='400' style='normal'>ascent1em-descent2em.ttf</font>"
+ + " </family>"
+ + " <family>"
+ + " <font weight='400' style='normal'>ascent3em-descent4em.ttf</font>"
+ + " </family>"
+ + "</familyset>";
+
+ try (FontFallbackSetup setup =
+ new FontFallbackSetup("DynamicLayout", testFontFiles, xml)) {
+ final Activity activity = mActivityRule.getActivity();
+ final TextView textView = new TextView(activity);
+ textView.setTypeface(setup.getTypefaceFor("sans-serif"));
+ textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, 100);
+ // This should result in three lines.
+ textView.setText("aaaaa aabaa aaaaa", mBufferType);
+ textView.setPadding(0, 0, 0, 0);
+ textView.setIncludeFontPadding(false);
+ textView.setFallbackLineSpacing(mEnabled);
+
+ final int em = (int) Math.ceil(textView.getPaint().measureText("a"));
+ final int width = 5 * em;
+ final int height = 30 * em; // tall enough to not affect our other measurements
+ textView.measure(
+ View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY),
+ View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY));
+ textView.layout(0, 0, width, height);
+
+ final Layout layout = textView.getLayout();
+ assertNotNull(layout);
+ if (mBufferType == BufferType.NORMAL) {
+ assertTrue(layout instanceof StaticLayout);
+ } else {
+ assertTrue(layout instanceof DynamicLayout);
+ }
+ assertEquals(3, layout.getLineCount());
+
+ assertEquals(-em, layout.getLineAscent(0));
+ assertEquals(2 * em, layout.getLineDescent(0));
+
+ if (mEnabled) {
+ // The second line has a 'b', so it needs more ascent and descent.
+ assertEquals(-3 * em, layout.getLineAscent(1));
+ assertEquals(4 * em, layout.getLineDescent(1));
+ } else {
+ // old behavior
+ assertEquals(-em, layout.getLineAscent(1));
+ assertEquals(2 * em, layout.getLineDescent(1));
+ }
+
+ assertEquals(-em, layout.getLineAscent(2));
+ assertEquals(2 * em, layout.getLineDescent(2));
+ }
+ }
+}
import android.support.test.filters.MediumTest;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
-import android.text.FontFallbackSetup;
import android.text.GetChars;
import android.text.Layout;
import android.text.Selection;
import android.text.Spannable;
-import android.util.TypedValue;
import android.view.View;
import org.junit.Before;
}
return builder.toString();
}
-
- @Test
- public void testFallbackLineSpacing() {
- // All glyphs in the fonts are 1em wide.
- final String[] testFontFiles = {
- // ascent == 1em, descent == 2em, only supports 'a' and space
- "ascent1em-descent2em.ttf",
- // ascent == 3em, descent == 4em, only supports 'b'
- "ascent3em-descent4em.ttf"
- };
- final String xml = "<?xml version='1.0' encoding='UTF-8'?>"
- + "<familyset>"
- + " <family name='sans-serif'>"
- + " <font weight='400' style='normal'>ascent1em-descent2em.ttf</font>"
- + " </family>"
- + " <family>"
- + " <font weight='400' style='normal'>ascent3em-descent4em.ttf</font>"
- + " </family>"
- + "</familyset>";
-
- try (FontFallbackSetup setup =
- new FontFallbackSetup("DynamicLayout", testFontFiles, xml)) {
- mTextView = new TextView(mActivity);
- mTextView.setTypeface(setup.getTypefaceFor("sans-serif"));
- mTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, 100);
- mTextView.setText("aaaaa aabaa aaaaa"); // This should result in three lines.
- mTextView.setPadding(0, 0, 0, 0);
- mTextView.setIncludeFontPadding(false);
-
- final int em = (int) Math.ceil(mTextView.getPaint().measureText("a"));
- final int width = 5 * em;
- final int height = 30 * em; // tall enough to not affect our other measurements
- mTextView.measure(
- View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY),
- View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY));
- mTextView.layout(0, 0, width, height);
-
- final Layout layout = mTextView.getLayout();
- assertNotNull(layout);
- assertEquals(3, layout.getLineCount());
-
- assertEquals(-em, layout.getLineAscent(0));
- assertEquals(2 * em, layout.getLineDescent(0));
-
- // The second line has a 'b', so it needs more ascent and descent.
- assertEquals(-3 * em, layout.getLineAscent(1));
- assertEquals(4 * em, layout.getLineDescent(1));
-
- assertEquals(-em, layout.getLineAscent(2));
- assertEquals(2 * em, layout.getLineDescent(2));
- }
- }
}