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.Px;
22 import android.graphics.Bitmap;
23 import android.graphics.Canvas;
24 import android.graphics.Paint;
25 import android.text.Layout;
26 import android.text.Spanned;
27 
28 /**
29  * Paragraph affecting span, that draws a bitmap at the beginning of a text. The span also allows
30  * setting a padding between the bitmap and the text. The default value of the padding is 0px. The
31  * span should be attached from the first character of the text.
32  * <p>
33  * For example, an <code>IconMarginSpan</code> with a bitmap and a padding of 30px can be set
34  * like this:
35  * <pre>
36  * SpannableString string = new SpannableString("Text with icon and padding");
37  * string.setSpan(new IconMarginSpan(bitmap, 30), 0, string.length(),
38  * Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
39  * </pre>
40  * <img src="{@docRoot}reference/android/images/text/style/iconmarginspan.png" />
41  * <figcaption>Text with <code>IconMarginSpan</code></figcaption>
42  * <p>
43  *
44  * @see DrawableMarginSpan for working with a {@link android.graphics.drawable.Drawable} instead of
45  * a {@link Bitmap}.
46  */
47 public class IconMarginSpan implements LeadingMarginSpan, LineHeightSpan {
48 
49     @NonNull
50     private final Bitmap mBitmap;
51     @Px
52     private final int mPad;
53 
54     /**
55      * Creates an {@link IconMarginSpan} from a {@link Bitmap}.
56      *
57      * @param bitmap bitmap to be rendered at the beginning of the text
58      */
IconMarginSpan(@onNull Bitmap bitmap)59     public IconMarginSpan(@NonNull Bitmap bitmap) {
60         this(bitmap, 0);
61     }
62 
63     /**
64      * Creates an {@link IconMarginSpan} from a {@link Bitmap}.
65      *
66      * @param bitmap bitmap to be rendered at the beginning of the text
67      * @param pad    padding width, in pixels, between the bitmap and the text
68      */
IconMarginSpan(@onNull Bitmap bitmap, @IntRange(from = 0) int pad)69     public IconMarginSpan(@NonNull Bitmap bitmap, @IntRange(from = 0) int pad) {
70         mBitmap = bitmap;
71         mPad = pad;
72     }
73 
74     @Override
getLeadingMargin(boolean first)75     public int getLeadingMargin(boolean first) {
76         return mBitmap.getWidth() + mPad;
77     }
78 
79     @Override
drawLeadingMargin(Canvas c, Paint p, int x, int dir, int top, int baseline, int bottom, CharSequence text, int start, int end, boolean first, Layout layout)80     public void drawLeadingMargin(Canvas c, Paint p, int x, int dir,
81             int top, int baseline, int bottom,
82             CharSequence text, int start, int end,
83             boolean first, Layout layout) {
84         int st = ((Spanned) text).getSpanStart(this);
85         int itop = layout.getLineTop(layout.getLineForOffset(st));
86 
87         if (dir < 0) {
88             x -= mBitmap.getWidth();
89         }
90 
91         c.drawBitmap(mBitmap, x, itop, p);
92     }
93 
94     @Override
chooseHeight(CharSequence text, int start, int end, int istartv, int v, Paint.FontMetricsInt fm)95     public void chooseHeight(CharSequence text, int start, int end,
96             int istartv, int v,
97             Paint.FontMetricsInt fm) {
98         if (end == ((Spanned) text).getSpanEnd(this)) {
99             int ht = mBitmap.getHeight();
100 
101             int need = ht - (v + fm.descent - fm.ascent - istartv);
102             if (need > 0) {
103                 fm.descent += need;
104             }
105 
106             need = ht - (v + fm.bottom - fm.top - istartv);
107             if (need > 0) {
108                 fm.bottom += need;
109             }
110         }
111     }
112 
113     @Override
toString()114     public String toString() {
115         return "IconMarginSpan{bitmap=" + getBitmap() + ", padding=" + getPadding() + '}';
116     }
117 
118     /**
119      * Returns the bitmap to be used at the beginning of the text
120      * @return a bitmap
121      */
getBitmap()122     @NonNull public Bitmap getBitmap() {
123         return mBitmap;
124     }
125 
126     /**
127      * Returns the padding width between the bitmap and the text.
128      * @return a padding width in pixels
129      */
getPadding()130     @Px public int getPadding() {
131         return mPad;
132     }
133 }
134