• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.IntRange;
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.graphics.Canvas;
23 import android.graphics.Paint;
24 import android.graphics.Rect;
25 import android.graphics.drawable.Drawable;
26 
27 import java.lang.ref.WeakReference;
28 
29 /**
30  * Span that replaces the text it's attached to with a {@link Drawable} that can be aligned with
31  * the bottom or with the baseline of the surrounding text.
32  * <p>
33  * For an implementation that constructs the drawable from various sources (<code>Bitmap</code>,
34  * <code>Drawable</code>, resource id or <code>Uri</code>) use {@link ImageSpan}.
35  * <p>
36  * A simple implementation of <code>DynamicDrawableSpan</code> that uses drawables from resources
37  * looks like this:
38  * <pre>
39  * class MyDynamicDrawableSpan extends DynamicDrawableSpan {
40  *
41  * private final Context mContext;
42  * private final int mResourceId;
43  *
44  * public MyDynamicDrawableSpan(Context context, @DrawableRes int resourceId) {
45  *     mContext = context;
46  *     mResourceId = resourceId;
47  * }
48  *
49  * {@literal @}Override
50  * public Drawable getDrawable() {
51  *      Drawable drawable = mContext.getDrawable(mResourceId);
52  *      drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
53  *      return drawable;
54  * }
55  * }</pre>
56  * The class can be used like this:
57  * <pre>
58  * SpannableString string = new SpannableString("Text with a drawable span");
59  * string.setSpan(new MyDynamicDrawableSpan(context, R.mipmap.ic_launcher), 12, 20, Spanned
60  * .SPAN_EXCLUSIVE_EXCLUSIVE);</pre>
61  * <img src="{@docRoot}reference/android/images/text/style/dynamicdrawablespan.png" />
62  * <figcaption>Replacing text with a drawable.</figcaption>
63  */
64 public abstract class DynamicDrawableSpan extends ReplacementSpan {
65 
66     /**
67      * A constant indicating that the bottom of this span should be aligned
68      * with the bottom of the surrounding text, i.e., at the same level as the
69      * lowest descender in the text.
70      */
71     public static final int ALIGN_BOTTOM = 0;
72 
73     /**
74      * A constant indicating that the bottom of this span should be aligned
75      * with the baseline of the surrounding text.
76      */
77     public static final int ALIGN_BASELINE = 1;
78 
79     protected final int mVerticalAlignment;
80 
81     private WeakReference<Drawable> mDrawableRef;
82 
83     /**
84      * Creates a {@link DynamicDrawableSpan}. The default vertical alignment is
85      * {@link #ALIGN_BOTTOM}
86      */
DynamicDrawableSpan()87     public DynamicDrawableSpan() {
88         mVerticalAlignment = ALIGN_BOTTOM;
89     }
90 
91     /**
92      * Creates a {@link DynamicDrawableSpan} based on a vertical alignment.\
93      *
94      * @param verticalAlignment one of {@link #ALIGN_BOTTOM} or {@link #ALIGN_BASELINE}
95      */
DynamicDrawableSpan(int verticalAlignment)96     protected DynamicDrawableSpan(int verticalAlignment) {
97         mVerticalAlignment = verticalAlignment;
98     }
99 
100     /**
101      * Returns the vertical alignment of this span, one of {@link #ALIGN_BOTTOM} or
102      * {@link #ALIGN_BASELINE}.
103      */
getVerticalAlignment()104     public int getVerticalAlignment() {
105         return mVerticalAlignment;
106     }
107 
108     /**
109      * Your subclass must implement this method to provide the bitmap
110      * to be drawn.  The dimensions of the bitmap must be the same
111      * from each call to the next.
112      */
getDrawable()113     public abstract Drawable getDrawable();
114 
115     @Override
getSize(@onNull Paint paint, CharSequence text, @IntRange(from = 0) int start, @IntRange(from = 0) int end, @Nullable Paint.FontMetricsInt fm)116     public int getSize(@NonNull Paint paint, CharSequence text,
117             @IntRange(from = 0) int start, @IntRange(from = 0) int end,
118             @Nullable Paint.FontMetricsInt fm) {
119         Drawable d = getCachedDrawable();
120         Rect rect = d.getBounds();
121 
122         if (fm != null) {
123             fm.ascent = -rect.bottom;
124             fm.descent = 0;
125 
126             fm.top = fm.ascent;
127             fm.bottom = 0;
128         }
129 
130         return rect.right;
131     }
132 
133     @Override
draw(@onNull Canvas canvas, CharSequence text, @IntRange(from = 0) int start, @IntRange(from = 0) int end, float x, int top, int y, int bottom, @NonNull Paint paint)134     public void draw(@NonNull Canvas canvas, CharSequence text,
135             @IntRange(from = 0) int start, @IntRange(from = 0) int end, float x,
136             int top, int y, int bottom, @NonNull Paint paint) {
137         Drawable b = getCachedDrawable();
138         canvas.save();
139 
140         int transY = bottom - b.getBounds().bottom;
141         if (mVerticalAlignment == ALIGN_BASELINE) {
142             transY -= paint.getFontMetricsInt().descent;
143         }
144 
145         canvas.translate(x, transY);
146         b.draw(canvas);
147         canvas.restore();
148     }
149 
getCachedDrawable()150     private Drawable getCachedDrawable() {
151         WeakReference<Drawable> wr = mDrawableRef;
152         Drawable d = null;
153 
154         if (wr != null) {
155             d = wr.get();
156         }
157 
158         if (d == null) {
159             d = getDrawable();
160             mDrawableRef = new WeakReference<Drawable>(d);
161         }
162 
163         return d;
164     }
165 }
166 
167