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.annotation.Nullable; 21 import android.graphics.LeakyTypefaceStorage; 22 import android.graphics.Paint; 23 import android.graphics.Typeface; 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 updates the typeface of the text it's attached to. The <code>TypefaceSpan</code> can 31 * be constructed either based on a font family or based on a <code>Typeface</code>. When 32 * {@link #TypefaceSpan(String)} is used, the previous style of the <code>TextView</code> is kept. 33 * When {@link #TypefaceSpan(Typeface)} is used, the <code>Typeface</code> style replaces the 34 * <code>TextView</code>'s style. 35 * <p> 36 * For example, let's consider a <code>TextView</code> with 37 * <code>android:textStyle="italic"</code> and a typeface created based on a font from resources, 38 * with a bold style. When applying a <code>TypefaceSpan</code> based the typeface, the text will 39 * only keep the bold style, overriding the <code>TextView</code>'s textStyle. When applying a 40 * <code>TypefaceSpan</code> based on a font family: "monospace", the resulted text will keep the 41 * italic style. 42 * <pre> 43 * Typeface myTypeface = Typeface.create(ResourcesCompat.getFont(context, R.font.acme), 44 * Typeface.BOLD); 45 * SpannableString string = new SpannableString("Text with typeface span."); 46 * string.setSpan(new TypefaceSpan(myTypeface), 10, 18, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 47 * string.setSpan(new TypefaceSpan("monospace"), 19, 22, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 48 * </pre> 49 * <img src="{@docRoot}reference/android/images/text/style/typefacespan.png" /> 50 * <figcaption>Text with <code>TypefaceSpan</code>s constructed based on a font from resource and 51 * from a font family.</figcaption> 52 */ 53 public class TypefaceSpan extends MetricAffectingSpan implements ParcelableSpan { 54 55 @Nullable 56 private final String mFamily; 57 58 @Nullable 59 private final Typeface mTypeface; 60 61 /** 62 * Constructs a {@link TypefaceSpan} based on the font family. The previous style of the 63 * TextPaint is kept. If the font family is null, the text paint is not modified. 64 * 65 * @param family The font family for this typeface. Examples include 66 * "monospace", "serif", and "sans-serif" 67 */ TypefaceSpan(@ullable String family)68 public TypefaceSpan(@Nullable String family) { 69 this(family, null); 70 } 71 72 /** 73 * Constructs a {@link TypefaceSpan} from a {@link Typeface}. The previous style of the 74 * TextPaint is overridden and the style of the typeface is used. 75 * 76 * @param typeface the typeface 77 */ TypefaceSpan(@onNull Typeface typeface)78 public TypefaceSpan(@NonNull Typeface typeface) { 79 this(null, typeface); 80 } 81 82 /** 83 * Constructs a {@link TypefaceSpan} from a parcel. 84 */ TypefaceSpan(@onNull Parcel src)85 public TypefaceSpan(@NonNull Parcel src) { 86 mFamily = src.readString(); 87 mTypeface = LeakyTypefaceStorage.readTypefaceFromParcel(src); 88 } 89 TypefaceSpan(@ullable String family, @Nullable Typeface typeface)90 private TypefaceSpan(@Nullable String family, @Nullable Typeface typeface) { 91 mFamily = family; 92 mTypeface = typeface; 93 } 94 95 @Override getSpanTypeId()96 public int getSpanTypeId() { 97 return getSpanTypeIdInternal(); 98 } 99 100 /** @hide */ 101 @Override getSpanTypeIdInternal()102 public int getSpanTypeIdInternal() { 103 return TextUtils.TYPEFACE_SPAN; 104 } 105 106 @Override describeContents()107 public int describeContents() { 108 return 0; 109 } 110 111 @Override writeToParcel(@onNull Parcel dest, int flags)112 public void writeToParcel(@NonNull Parcel dest, int flags) { 113 writeToParcelInternal(dest, flags); 114 } 115 116 /** @hide */ 117 @Override writeToParcelInternal(@onNull Parcel dest, int flags)118 public void writeToParcelInternal(@NonNull Parcel dest, int flags) { 119 dest.writeString(mFamily); 120 LeakyTypefaceStorage.writeTypefaceToParcel(mTypeface, dest); 121 } 122 123 /** 124 * Returns the font family name set in the span. 125 * 126 * @return the font family name 127 * @see #TypefaceSpan(String) 128 */ 129 @Nullable getFamily()130 public String getFamily() { 131 return mFamily; 132 } 133 134 /** 135 * Returns the typeface set in the span. 136 * 137 * @return the typeface set 138 * @see #TypefaceSpan(Typeface) 139 */ 140 @Nullable getTypeface()141 public Typeface getTypeface() { 142 return mTypeface; 143 } 144 145 @Override updateDrawState(@onNull TextPaint ds)146 public void updateDrawState(@NonNull TextPaint ds) { 147 updateTypeface(ds); 148 } 149 150 @Override updateMeasureState(@onNull TextPaint paint)151 public void updateMeasureState(@NonNull TextPaint paint) { 152 updateTypeface(paint); 153 } 154 updateTypeface(@onNull Paint paint)155 private void updateTypeface(@NonNull Paint paint) { 156 if (mTypeface != null) { 157 paint.setTypeface(mTypeface); 158 } else if (mFamily != null) { 159 applyFontFamily(paint, mFamily); 160 } 161 } 162 applyFontFamily(@onNull Paint paint, @NonNull String family)163 private void applyFontFamily(@NonNull Paint paint, @NonNull String family) { 164 int style; 165 Typeface old = paint.getTypeface(); 166 if (old == null) { 167 style = Typeface.NORMAL; 168 } else { 169 style = old.getStyle(); 170 } 171 final Typeface styledTypeface = Typeface.create(family, style); 172 int fake = style & ~styledTypeface.getStyle(); 173 174 if ((fake & Typeface.BOLD) != 0) { 175 paint.setFakeBoldText(true); 176 } 177 178 if ((fake & Typeface.ITALIC) != 0) { 179 paint.setTextSkewX(-0.25f); 180 } 181 paint.setTypeface(styledTypeface); 182 } 183 184 @Override toString()185 public String toString() { 186 return "TypefaceSpan{" 187 + "family='" + getFamily() + '\'' 188 + ", typeface=" + getTypeface() 189 + '}'; 190 } 191 } 192