1 /* 2 * Copyright (C) 2006 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.text.style; 18 19 import android.annotation.NonNull; 20 import android.content.res.Configuration; 21 import android.graphics.Paint; 22 import android.graphics.Typeface; 23 import android.graphics.fonts.FontStyle; 24 import android.os.Parcel; 25 import android.text.ParcelableSpan; 26 import android.text.TextPaint; 27 import android.text.TextUtils; 28 29 /** 30 * Span that allows setting the style of the text it's attached to. 31 * Possible styles are: {@link Typeface#NORMAL}, {@link Typeface#BOLD}, {@link Typeface#ITALIC} and 32 * {@link Typeface#BOLD_ITALIC}. 33 * <p> 34 * Note that styles are cumulative -- if both bold and italic are set in 35 * separate spans, or if the base style is bold and a span calls for italic, 36 * you get bold italic. You can't turn off a style from the base style. 37 * <p> 38 * For example, the <code>StyleSpan</code> can be used like this: 39 * <pre> 40 * SpannableString string = new SpannableString("Bold and italic text"); 41 * string.setSpan(new StyleSpan(Typeface.BOLD), 0, 4, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 42 * string.setSpan(new StyleSpan(Typeface.ITALIC), 9, 15, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 43 * </pre> 44 * <img src="{@docRoot}reference/android/images/text/style/stylespan.png" /> 45 * <figcaption>Text styled bold and italic with the <code>StyleSpan</code>.</figcaption> 46 */ 47 @android.ravenwood.annotation.RavenwoodKeepWholeClass 48 public class StyleSpan extends MetricAffectingSpan implements ParcelableSpan { 49 50 private final int mStyle; 51 private final int mFontWeightAdjustment; 52 53 /** 54 * Creates a {@link StyleSpan} from a style. 55 * 56 * @param style An integer constant describing the style for this span. Examples 57 * include bold, italic, and normal. Values are constants defined 58 * in {@link Typeface}. 59 */ StyleSpan(int style)60 public StyleSpan(int style) { 61 this(style, Configuration.FONT_WEIGHT_ADJUSTMENT_UNDEFINED); 62 } 63 64 /** 65 * Creates a {@link StyleSpan} from a style and font weight adjustment. 66 * 67 * @param style An integer constant describing the style for this span. Examples 68 * include bold, italic, and normal. Values are constants defined 69 * in {@link Typeface}. 70 * @param fontWeightAdjustment An integer describing the adjustment to be made to the font 71 * weight. This is added to the value of the current weight returned by 72 * {@link Typeface#getWeight()}. 73 * @see Configuration#fontWeightAdjustment This is the adjustment in text font weight 74 * that is used to reflect the current user's preference for increasing font weight. 75 */ StyleSpan(@ypeface.Style int style, int fontWeightAdjustment)76 public StyleSpan(@Typeface.Style int style, int fontWeightAdjustment) { 77 mStyle = style; 78 mFontWeightAdjustment = fontWeightAdjustment; 79 } 80 81 /** 82 * Creates a {@link StyleSpan} from a parcel. 83 * 84 * @param src the parcel 85 */ StyleSpan(@onNull Parcel src)86 public StyleSpan(@NonNull Parcel src) { 87 mStyle = src.readInt(); 88 mFontWeightAdjustment = src.readInt(); 89 } 90 91 @Override getSpanTypeId()92 public int getSpanTypeId() { 93 return getSpanTypeIdInternal(); 94 } 95 96 /** @hide */ 97 @Override getSpanTypeIdInternal()98 public int getSpanTypeIdInternal() { 99 return TextUtils.STYLE_SPAN; 100 } 101 102 @Override describeContents()103 public int describeContents() { 104 return 0; 105 } 106 107 @Override writeToParcel(Parcel dest, int flags)108 public void writeToParcel(Parcel dest, int flags) { 109 writeToParcelInternal(dest, flags); 110 } 111 112 /** @hide */ 113 @Override writeToParcelInternal(@onNull Parcel dest, int flags)114 public void writeToParcelInternal(@NonNull Parcel dest, int flags) { 115 dest.writeInt(mStyle); 116 dest.writeInt(mFontWeightAdjustment); 117 } 118 119 /** 120 * Returns the style constant defined in {@link Typeface}. 121 */ getStyle()122 public int getStyle() { 123 return mStyle; 124 } 125 126 /** 127 * Returns the font weight adjustment specified by this span. 128 * <p> 129 * This can be {@link Configuration#FONT_WEIGHT_ADJUSTMENT_UNDEFINED}. This is added to the 130 * value of the current weight returned by {@link Typeface#getWeight()}. 131 */ getFontWeightAdjustment()132 public int getFontWeightAdjustment() { 133 return mFontWeightAdjustment; 134 } 135 136 @Override updateDrawState(TextPaint ds)137 public void updateDrawState(TextPaint ds) { 138 apply(ds, mStyle, mFontWeightAdjustment); 139 } 140 141 @Override updateMeasureState(TextPaint paint)142 public void updateMeasureState(TextPaint paint) { 143 apply(paint, mStyle, mFontWeightAdjustment); 144 } 145 apply(Paint paint, int style, int fontWeightAdjustment)146 private static void apply(Paint paint, int style, int fontWeightAdjustment) { 147 int oldStyle; 148 149 Typeface old = paint.getTypeface(); 150 if (old == null) { 151 oldStyle = 0; 152 } else { 153 oldStyle = old.getStyle(); 154 } 155 156 int want = oldStyle | style; 157 158 Typeface tf; 159 if (old == null) { 160 tf = Typeface.defaultFromStyle(want); 161 } else { 162 tf = Typeface.create(old, want); 163 } 164 165 // Base typeface may already be bolded by auto bold. Bold further. 166 if ((style & Typeface.BOLD) != 0) { 167 if (fontWeightAdjustment != 0 168 && fontWeightAdjustment != Configuration.FONT_WEIGHT_ADJUSTMENT_UNDEFINED) { 169 int newWeight = Math.min( 170 Math.max(tf.getWeight() + fontWeightAdjustment, FontStyle.FONT_WEIGHT_MIN), 171 FontStyle.FONT_WEIGHT_MAX); 172 boolean italic = (want & Typeface.ITALIC) != 0; 173 tf = Typeface.create(tf, newWeight, italic); 174 } 175 } 176 177 int fake = want & ~tf.getStyle(); 178 179 if ((fake & Typeface.BOLD) != 0) { 180 paint.setFakeBoldText(true); 181 } 182 183 if ((fake & Typeface.ITALIC) != 0) { 184 paint.setTextSkewX(-0.25f); 185 } 186 187 paint.setTypeface(tf); 188 } 189 190 @Override toString()191 public String toString() { 192 return "StyleSpan{" 193 + "style=" + getStyle() 194 + ", fontWeightAdjustment=" + getFontWeightAdjustment() 195 + '}'; 196 } 197 } 198