From e245116af39f58288b3003f221fc0f151b0fea05 Mon Sep 17 00:00:00 2001 From: Seigo Nonaka Date: Thu, 22 Feb 2018 11:26:39 -0800 Subject: [PATCH] New attribute textFontWeight for selecting weight in the font family Note that AppCompatTextView doesn't work well with this attribute since it overwrites the selected Typeface. Bug: 63135308 Test: atest android.widget.cts.TextViewFontWeightTest Change-Id: I76ee5e3007ea5f96249d2a0bfb66ff5975c62522 --- api/current.txt | 1 + core/java/android/widget/TextView.java | 91 +++++++++++++++++++--------- core/res/res/values/attrs.xml | 4 ++ core/res/res/values/public.xml | 1 + graphics/java/android/graphics/Typeface.java | 27 +++++++-- 5 files changed, 90 insertions(+), 34 deletions(-) diff --git a/api/current.txt b/api/current.txt index 573e635d0634..9c30480524e7 100644 --- a/api/current.txt +++ b/api/current.txt @@ -1378,6 +1378,7 @@ package android { field public static final int textEditSidePasteWindowLayout = 16843614; // 0x101035e field public static final int textEditSuggestionItemLayout = 16843636; // 0x1010374 field public static final int textFilterEnabled = 16843007; // 0x10100ff + field public static final int textFontWeight = 16844166; // 0x1010586 field public static final int textIsSelectable = 16843542; // 0x1010316 field public static final int textOff = 16843045; // 0x1010125 field public static final int textOn = 16843044; // 0x1010124 diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 2cfdb7693a38..50e6393d2579 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -319,6 +319,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // Enum for the "typeface" XML parameter. // TODO: How can we get this from the XML instead of hardcoding it here? + /** @hide */ + @IntDef(value = {DEFAULT_TYPEFACE, SANS, SERIF, MONOSPACE}) + @Retention(RetentionPolicy.SOURCE) + public @interface XMLTypefaceAttr{} + private static final int DEFAULT_TYPEFACE = -1; private static final int SANS = 1; private static final int SERIF = 2; private static final int MONOSPACE = 3; @@ -1976,33 +1981,52 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } - private void setTypefaceFromAttrs(Typeface fontTypeface, String familyName, int typefaceIndex, - int styleIndex) { - Typeface tf = fontTypeface; - if (tf == null && familyName != null) { - tf = Typeface.create(familyName, styleIndex); - } else if (tf != null && tf.getStyle() != styleIndex) { - tf = Typeface.create(tf, styleIndex); - } - if (tf != null) { - setTypeface(tf); - return; + /** + * Sets the Typeface taking into account the given attributes. + * + * @param typeface a typeface + * @param familyName family name string, e.g. "serif" + * @param typefaceIndex an index of the typeface enum, e.g. SANS, SERIF. + * @param style a typeface style + * @param weight a weight value for the Typeface or -1 if not specified. + */ + private void setTypefaceFromAttrs(@Nullable Typeface typeface, @Nullable String familyName, + @XMLTypefaceAttr int typefaceIndex, @Typeface.Style int style, + @IntRange(from = -1, to = Typeface.MAX_WEIGHT) int weight) { + if (typeface == null && familyName != null) { + // Lookup normal Typeface from system font map. + final Typeface normalTypeface = Typeface.create(familyName, Typeface.NORMAL); + resolveStyleAndSetTypeface(normalTypeface, style, weight); + } else if (typeface != null) { + resolveStyleAndSetTypeface(typeface, style, weight); + } else { // both typeface and familyName is null. + switch (typefaceIndex) { + case SANS: + resolveStyleAndSetTypeface(Typeface.SANS_SERIF, style, weight); + break; + case SERIF: + resolveStyleAndSetTypeface(Typeface.SERIF, style, weight); + break; + case MONOSPACE: + resolveStyleAndSetTypeface(Typeface.MONOSPACE, style, weight); + break; + case DEFAULT_TYPEFACE: + default: + resolveStyleAndSetTypeface(null, style, weight); + break; + } } - switch (typefaceIndex) { - case SANS: - tf = Typeface.SANS_SERIF; - break; - - case SERIF: - tf = Typeface.SERIF; - break; + } - case MONOSPACE: - tf = Typeface.MONOSPACE; - break; + private void resolveStyleAndSetTypeface(@NonNull Typeface typeface, @Typeface.Style int style, + @IntRange(from = -1, to = Typeface.MAX_WEIGHT) int weight) { + if (weight >= 0) { + weight = Math.min(Typeface.MAX_WEIGHT, weight); + final boolean italic = (style & Typeface.ITALIC) != 0; + setTypeface(Typeface.create(typeface, weight, italic)); + } else { + setTypeface(Typeface.create(typeface, style)); } - - setTypeface(tf, styleIndex); } private void setRelativeDrawablesIfNeeded(Drawable start, Drawable end) { @@ -3392,6 +3416,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener boolean mFontFamilyExplicit = false; int mTypefaceIndex = -1; int mStyleIndex = -1; + int mFontWeight = -1; boolean mAllCaps = false; int mShadowColor = 0; float mShadowDx = 0, mShadowDy = 0, mShadowRadius = 0; @@ -3416,6 +3441,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener + " mFontFamilyExplicit:" + mFontFamilyExplicit + "\n" + " mTypefaceIndex:" + mTypefaceIndex + "\n" + " mStyleIndex:" + mStyleIndex + "\n" + + " mFontWeight:" + mFontWeight + "\n" + " mAllCaps:" + mAllCaps + "\n" + " mShadowColor:" + mShadowColor + "\n" + " mShadowDx:" + mShadowDx + "\n" @@ -3451,6 +3477,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener com.android.internal.R.styleable.TextAppearance_fontFamily); sAppearanceValues.put(com.android.internal.R.styleable.TextView_textStyle, com.android.internal.R.styleable.TextAppearance_textStyle); + sAppearanceValues.put(com.android.internal.R.styleable.TextView_textFontWeight, + com.android.internal.R.styleable.TextAppearance_textFontWeight); sAppearanceValues.put(com.android.internal.R.styleable.TextView_textAllCaps, com.android.internal.R.styleable.TextAppearance_textAllCaps); sAppearanceValues.put(com.android.internal.R.styleable.TextView_shadowColor, @@ -3536,6 +3564,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener case com.android.internal.R.styleable.TextAppearance_textStyle: attributes.mStyleIndex = appearance.getInt(attr, attributes.mStyleIndex); break; + case com.android.internal.R.styleable.TextAppearance_textFontWeight: + attributes.mFontWeight = appearance.getInt(attr, attributes.mFontWeight); + break; case com.android.internal.R.styleable.TextAppearance_textAllCaps: attributes.mAllCaps = appearance.getBoolean(attr, attributes.mAllCaps); break; @@ -3598,7 +3629,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener attributes.mFontFamily = null; } setTypefaceFromAttrs(attributes.mFontTypeface, attributes.mFontFamily, - attributes.mTypefaceIndex, attributes.mStyleIndex); + attributes.mTypefaceIndex, attributes.mStyleIndex, attributes.mFontWeight); if (attributes.mShadowColor != 0) { setShadowLayer(attributes.mShadowRadius, attributes.mShadowDx, attributes.mShadowDy, @@ -5938,15 +5969,19 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener boolean forceUpdate = false; if (isPassword) { setTransformationMethod(PasswordTransformationMethod.getInstance()); - setTypefaceFromAttrs(null/* fontTypeface */, null /* fontFamily */, MONOSPACE, 0); + setTypefaceFromAttrs(null/* fontTypeface */, null /* fontFamily */, MONOSPACE, + Typeface.NORMAL, -1 /* weight, not specifeid */); } else if (isVisiblePassword) { if (mTransformation == PasswordTransformationMethod.getInstance()) { forceUpdate = true; } - setTypefaceFromAttrs(null/* fontTypeface */, null /* fontFamily */, MONOSPACE, 0); + setTypefaceFromAttrs(null/* fontTypeface */, null /* fontFamily */, MONOSPACE, + Typeface.NORMAL, -1 /* weight, not specified */); } else if (wasPassword || wasVisiblePassword) { // not in password mode, clean up typeface and transformation - setTypefaceFromAttrs(null/* fontTypeface */, null /* fontFamily */, -1, -1); + setTypefaceFromAttrs(null/* fontTypeface */, null /* fontFamily */, + DEFAULT_TYPEFACE /* typeface index */, Typeface.NORMAL, + -1 /* weight, not specified */); if (mTransformation == PasswordTransformationMethod.getInstance()) { forceUpdate = true; } diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 96a83f837fb3..22ab9c913540 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -4472,6 +4472,8 @@ + + @@ -4561,6 +4563,8 @@ + + diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index a5ba4c63d0fb..c4006b324569 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -2872,6 +2872,7 @@ + diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java index 8595165aab27..38beebde4b14 100644 --- a/graphics/java/android/graphics/Typeface.java +++ b/graphics/java/android/graphics/Typeface.java @@ -21,6 +21,7 @@ import static android.content.res.FontResourcesParser.FontFamilyFilesResourceEnt import static android.content.res.FontResourcesParser.FontFileResourceEntry; import static android.content.res.FontResourcesParser.ProviderResourceEntry; +import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; @@ -49,6 +50,8 @@ import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.util.ArrayList; @@ -117,6 +120,11 @@ public class Typeface { */ public long native_instance; + /** @hide */ + @IntDef(value = {NORMAL, BOLD, ITALIC, BOLD_ITALIC}) + @Retention(RetentionPolicy.SOURCE) + public @interface Style {} + // Style public static final int NORMAL = 0; public static final int BOLD = 1; @@ -124,8 +132,15 @@ public class Typeface { public static final int BOLD_ITALIC = 3; /** @hide */ public static final int STYLE_MASK = 0x03; - private int mStyle = 0; - private int mWeight = 0; + private @Style int mStyle = 0; + + /** + * A maximum value for the weight value. + * @hide + */ + public static final int MAX_WEIGHT = 1000; + + private @IntRange(from = 0, to = MAX_WEIGHT) int mWeight = 0; // Value for weight and italic. Indicates the value is resolved by font metadata. // Must be the same as the C++ constant in core/jni/android/graphics/FontFamily.cpp @@ -153,7 +168,7 @@ public class Typeface { } /** Returns the typeface's intrinsic style attributes */ - public int getStyle() { + public @Style int getStyle() { return mStyle; } @@ -659,7 +674,7 @@ public class Typeface { * e.g. NORMAL, BOLD, ITALIC, BOLD_ITALIC * @return The best matching typeface. */ - public static Typeface create(String familyName, int style) { + public static Typeface create(String familyName, @Style int style) { return create(sSystemFontMap.get(familyName), style); } @@ -680,7 +695,7 @@ public class Typeface { * e.g. NORMAL, BOLD, ITALIC, BOLD_ITALIC * @return The best matching typeface. */ - public static Typeface create(Typeface family, int style) { + public static Typeface create(Typeface family, @Style int style) { if ((style & ~STYLE_MASK) != 0) { style = NORMAL; } @@ -776,7 +791,7 @@ public class Typeface { * * @return the default typeface that corresponds to the style */ - public static Typeface defaultFromStyle(int style) { + public static Typeface defaultFromStyle(@Style int style) { return sDefaults[style]; } -- 2.11.0