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 @android.ravenwood.annotation.RavenwoodKeepWholeClass 54 public class TypefaceSpan extends MetricAffectingSpan implements ParcelableSpan { 55 56 @Nullable 57 private final String mFamily; 58 59 @Nullable 60 private final Typeface mTypeface; 61 62 /** 63 * Constructs a {@link TypefaceSpan} based on the font family. The previous style of the 64 * TextPaint is kept. If the font family is null, the text paint is not modified. 65 * 66 * @param family The font family for this typeface. Examples include 67 * "monospace", "serif", and "sans-serif" 68 */ TypefaceSpan(@ullable String family)69 public TypefaceSpan(@Nullable String family) { 70 this(family, null); 71 } 72 73 /** 74 * Constructs a {@link TypefaceSpan} from a {@link Typeface}. The previous style of the 75 * TextPaint is overridden and the style of the typeface is used. 76 * 77 * @param typeface the typeface 78 */ TypefaceSpan(@onNull Typeface typeface)79 public TypefaceSpan(@NonNull Typeface typeface) { 80 this(null, typeface); 81 } 82 83 /** 84 * Constructs a {@link TypefaceSpan} from a parcel. 85 */ TypefaceSpan(@onNull Parcel src)86 public TypefaceSpan(@NonNull Parcel src) { 87 mFamily = src.readString(); 88 mTypeface = LeakyTypefaceStorage.readTypefaceFromParcel(src); 89 } 90 TypefaceSpan(@ullable String family, @Nullable Typeface typeface)91 private TypefaceSpan(@Nullable String family, @Nullable Typeface typeface) { 92 mFamily = family; 93 mTypeface = typeface; 94 } 95 96 @Override getSpanTypeId()97 public int getSpanTypeId() { 98 return getSpanTypeIdInternal(); 99 } 100 101 /** @hide */ 102 @Override getSpanTypeIdInternal()103 public int getSpanTypeIdInternal() { 104 return TextUtils.TYPEFACE_SPAN; 105 } 106 107 @Override describeContents()108 public int describeContents() { 109 return 0; 110 } 111 112 @Override writeToParcel(@onNull Parcel dest, int flags)113 public void writeToParcel(@NonNull Parcel dest, int flags) { 114 writeToParcelInternal(dest, flags); 115 } 116 117 /** @hide */ 118 @Override writeToParcelInternal(@onNull Parcel dest, int flags)119 public void writeToParcelInternal(@NonNull Parcel dest, int flags) { 120 dest.writeString(mFamily); 121 LeakyTypefaceStorage.writeTypefaceToParcel(mTypeface, dest); 122 } 123 124 /** 125 * Returns the font family name set in the span. 126 * 127 * @return the font family name 128 * @see #TypefaceSpan(String) 129 */ 130 @Nullable getFamily()131 public String getFamily() { 132 return mFamily; 133 } 134 135 /** 136 * Returns the typeface set in the span. 137 * 138 * @return the typeface set 139 * @see #TypefaceSpan(Typeface) 140 */ 141 @Nullable getTypeface()142 public Typeface getTypeface() { 143 return mTypeface; 144 } 145 146 @Override updateDrawState(@onNull TextPaint ds)147 public void updateDrawState(@NonNull TextPaint ds) { 148 updateTypeface(ds); 149 } 150 151 @Override updateMeasureState(@onNull TextPaint paint)152 public void updateMeasureState(@NonNull TextPaint paint) { 153 updateTypeface(paint); 154 } 155 updateTypeface(@onNull Paint paint)156 private void updateTypeface(@NonNull Paint paint) { 157 if (mTypeface != null) { 158 paint.setTypeface(mTypeface); 159 } else if (mFamily != null) { 160 applyFontFamily(paint, mFamily); 161 } 162 } 163 applyFontFamily(@onNull Paint paint, @NonNull String family)164 private void applyFontFamily(@NonNull Paint paint, @NonNull String family) { 165 int style; 166 Typeface old = paint.getTypeface(); 167 if (old == null) { 168 style = Typeface.NORMAL; 169 } else { 170 style = old.getStyle(); 171 } 172 final Typeface styledTypeface = Typeface.create(family, style); 173 int fake = style & ~styledTypeface.getStyle(); 174 175 if ((fake & Typeface.BOLD) != 0) { 176 paint.setFakeBoldText(true); 177 } 178 179 if ((fake & Typeface.ITALIC) != 0) { 180 paint.setTextSkewX(-0.25f); 181 } 182 paint.setTypeface(styledTypeface); 183 } 184 185 @Override toString()186 public String toString() { 187 return "TypefaceSpan{" 188 + "family='" + getFamily() + '\'' 189 + ", typeface=" + getTypeface() 190 + '}'; 191 } 192 } 193