OSDN Git Service

Enable/disable fallback line spacing in TextView
authorSiyamed Sinir <siyamed@google.com>
Thu, 4 Jan 2018 03:39:39 +0000 (19:39 -0800)
committerSiyamed Sinir <siyamed@google.com>
Fri, 5 Jan 2018 03:48:35 +0000 (19:48 -0800)
Add API to enable/disable fallback line spacing in TextView.

Test: bit FrameworksCoreTests:android.widget.TextViewFallbackLineSpacingTest

Bug: 65161512

Change-Id: I76ba7bdb7d25c7a94653144536295c50d8b0e495

api/current.txt
core/java/android/widget/TextView.java
core/res/res/values/attrs.xml
core/res/res/values/public.xml
core/tests/coretests/src/android/widget/TextViewFallbackLineSpacingTest.java [new file with mode: 0644]
core/tests/coretests/src/android/widget/TextViewTest.java

index c57cf32..e94818e 100644 (file)
@@ -579,6 +579,7 @@ package android {
     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
@@ -52306,6 +52307,7 @@ package android.widget {
     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();
@@ -52349,6 +52351,7 @@ package android.widget {
     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);
index 1618d62..73d53d4 100644 (file)
@@ -296,6 +296,7 @@ import java.util.Locale;
  * @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
@@ -655,7 +656,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
     // 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;
@@ -3255,6 +3256,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
         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;
@@ -3279,6 +3282,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                     + "    mShadowRadius:" + mShadowRadius + "\n"
                     + "    mHasElegant:" + mHasElegant + "\n"
                     + "    mElegant:" + mElegant + "\n"
+                    + "    mHasFallbackLineSpacing:" + mHasFallbackLineSpacing + "\n"
+                    + "    mFallbackLineSpacing:" + mFallbackLineSpacing + "\n"
                     + "    mHasLetterSpacing:" + mHasLetterSpacing + "\n"
                     + "    mLetterSpacing:" + mLetterSpacing + "\n"
                     + "    mFontFeatureSettings:" + mFontFeatureSettings + "\n"
@@ -3317,6 +3322,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                 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,
@@ -3407,6 +3414,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                     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 =
@@ -3460,6 +3472,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
             setElegantTextHeight(attributes.mElegant);
         }
 
+        if (attributes.mHasFallbackLineSpacing) {
+            setFallbackLineSpacing(attributes.mFallbackLineSpacing);
+        }
+
         if (attributes.mHasLetterSpacing) {
             setLetterSpacing(attributes.mLetterSpacing);
         }
@@ -3741,7 +3757,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
      *
      * @param elegant set the paint's elegant metrics flag.
      *
-     * @see Paint#isElegantTextHeight(boolean)
+     * @see Paint#isElegantTextHeight()
      *
      * @attr ref android.R.styleable#TextView_elegantTextHeight
      */
@@ -3757,6 +3773,43 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
     }
 
     /**
+     * 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.
index 1b3d6ce..ee7c795 100644 (file)
         <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. -->
index 8540ff1..6ec88dc 100644 (file)
       <public name="isVrOnly"/>
       <public name="widgetFeatures" />
       <public name="appComponentFactory" />
+      <public name="fallbackLineSpacing" />
     </public-group>
 
     <public-group type="style" first-id="0x010302e0">
diff --git a/core/tests/coretests/src/android/widget/TextViewFallbackLineSpacingTest.java b/core/tests/coretests/src/android/widget/TextViewFallbackLineSpacingTest.java
new file mode 100644 (file)
index 0000000..e9d1d3e
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * 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));
+        }
+    }
+}
index 5dec42e..2b5b27b 100644 (file)
@@ -30,12 +30,10 @@ import android.support.test.annotation.UiThreadTest;
 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;
@@ -251,56 +249,4 @@ public class TextViewTest {
         }
         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));
-        }
-    }
 }