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.widget;
18 
19 import android.graphics.Canvas;
20 import android.graphics.ColorFilter;
21 import android.graphics.PixelFormat;
22 import android.graphics.Rect;
23 import android.graphics.drawable.Drawable;
24 
25 /**
26  * This is only used by View for displaying its scroll bars.  It should probably
27  * be moved in to the view package since it is used in that lower-level layer.
28  * For now, we'll hide it so it can be cleaned up later.
29  * {@hide}
30  */
31 public class ScrollBarDrawable extends Drawable {
32     private static final int[] STATE_ENABLED = new int[] { android.R.attr.state_enabled };
33 
34     private Drawable mVerticalTrack;
35     private Drawable mHorizontalTrack;
36     private Drawable mVerticalThumb;
37     private Drawable mHorizontalThumb;
38     private int mRange;
39     private int mOffset;
40     private int mExtent;
41     private boolean mVertical;
42     private boolean mChanged;
43     private boolean mRangeChanged;
44     private final Rect mTempBounds = new Rect();
45     private boolean mAlwaysDrawHorizontalTrack;
46     private boolean mAlwaysDrawVerticalTrack;
47     private boolean mMutated;
48 
ScrollBarDrawable()49     public ScrollBarDrawable() {
50     }
51 
52     /**
53      * Indicate whether the horizontal scrollbar track should always be drawn regardless of the
54      * extent. Defaults to false.
55      *
56      * @param alwaysDrawTrack Set to true if the track should always be drawn
57      */
setAlwaysDrawHorizontalTrack(boolean alwaysDrawTrack)58     public void setAlwaysDrawHorizontalTrack(boolean alwaysDrawTrack) {
59         mAlwaysDrawHorizontalTrack = alwaysDrawTrack;
60     }
61 
62     /**
63      * Indicate whether the vertical scrollbar track should always be drawn regardless of the
64      * extent. Defaults to false.
65      *
66      * @param alwaysDrawTrack Set to true if the track should always be drawn
67      */
setAlwaysDrawVerticalTrack(boolean alwaysDrawTrack)68     public void setAlwaysDrawVerticalTrack(boolean alwaysDrawTrack) {
69         mAlwaysDrawVerticalTrack = alwaysDrawTrack;
70     }
71 
72     /**
73      * Indicates whether the vertical scrollbar track should always be drawn regardless of the
74      * extent.
75      */
getAlwaysDrawVerticalTrack()76     public boolean getAlwaysDrawVerticalTrack() {
77         return mAlwaysDrawVerticalTrack;
78     }
79 
80     /**
81      * Indicates whether the horizontal scrollbar track should always be drawn regardless of the
82      * extent.
83      */
getAlwaysDrawHorizontalTrack()84     public boolean getAlwaysDrawHorizontalTrack() {
85         return mAlwaysDrawHorizontalTrack;
86     }
87 
setParameters(int range, int offset, int extent, boolean vertical)88     public void setParameters(int range, int offset, int extent, boolean vertical) {
89         if (mVertical != vertical) {
90             mChanged = true;
91         }
92 
93         if (mRange != range || mOffset != offset || mExtent != extent) {
94             mRangeChanged = true;
95         }
96 
97         mRange = range;
98         mOffset = offset;
99         mExtent = extent;
100         mVertical = vertical;
101     }
102 
103     @Override
draw(Canvas canvas)104     public void draw(Canvas canvas) {
105         final boolean vertical = mVertical;
106         final int extent = mExtent;
107         final int range = mRange;
108 
109         boolean drawTrack = true;
110         boolean drawThumb = true;
111         if (extent <= 0 || range <= extent) {
112             drawTrack = vertical ? mAlwaysDrawVerticalTrack : mAlwaysDrawHorizontalTrack;
113             drawThumb = false;
114         }
115 
116         Rect r = getBounds();
117         if (canvas.quickReject(r.left, r.top, r.right, r.bottom, Canvas.EdgeType.AA)) {
118             return;
119         }
120         if (drawTrack) {
121             drawTrack(canvas, r, vertical);
122         }
123 
124         if (drawThumb) {
125             int size = vertical ? r.height() : r.width();
126             int thickness = vertical ? r.width() : r.height();
127             int length = Math.round((float) size * extent / range);
128             int offset = Math.round((float) (size - length) * mOffset / (range - extent));
129 
130             // avoid the tiny thumb
131             int minLength = thickness * 2;
132             if (length < minLength) {
133                 length = minLength;
134             }
135             // avoid the too-big thumb
136             if (offset + length > size) {
137                 offset = size - length;
138             }
139 
140             drawThumb(canvas, r, offset, length, vertical);
141         }
142     }
143 
144     @Override
onBoundsChange(Rect bounds)145     protected void onBoundsChange(Rect bounds) {
146         super.onBoundsChange(bounds);
147         mChanged = true;
148     }
149 
drawTrack(Canvas canvas, Rect bounds, boolean vertical)150     protected void drawTrack(Canvas canvas, Rect bounds, boolean vertical) {
151         Drawable track;
152         if (vertical) {
153             track = mVerticalTrack;
154         } else {
155             track = mHorizontalTrack;
156         }
157         if (track != null) {
158             if (mChanged) {
159                 track.setBounds(bounds);
160             }
161             track.draw(canvas);
162         }
163     }
164 
drawThumb(Canvas canvas, Rect bounds, int offset, int length, boolean vertical)165     protected void drawThumb(Canvas canvas, Rect bounds, int offset, int length, boolean vertical) {
166         final Rect thumbRect = mTempBounds;
167         final boolean changed = mRangeChanged || mChanged;
168         if (changed) {
169             if (vertical) {
170                 thumbRect.set(bounds.left,  bounds.top + offset,
171                         bounds.right, bounds.top + offset + length);
172             } else {
173                 thumbRect.set(bounds.left + offset, bounds.top,
174                         bounds.left + offset + length, bounds.bottom);
175             }
176         }
177 
178         if (vertical) {
179             if (mVerticalThumb != null) {
180                 final Drawable thumb = mVerticalThumb;
181                 if (changed) thumb.setBounds(thumbRect);
182                 thumb.draw(canvas);
183             }
184         } else {
185             if (mHorizontalThumb != null) {
186                 final Drawable thumb = mHorizontalThumb;
187                 if (changed) thumb.setBounds(thumbRect);
188                 thumb.draw(canvas);
189             }
190         }
191     }
192 
setVerticalThumbDrawable(Drawable thumb)193     public void setVerticalThumbDrawable(Drawable thumb) {
194         if (thumb != null) {
195             if (mMutated) {
196                 thumb.mutate();
197             }
198             thumb.setState(STATE_ENABLED);
199             mVerticalThumb = thumb;
200         }
201     }
202 
setVerticalTrackDrawable(Drawable track)203     public void setVerticalTrackDrawable(Drawable track) {
204         if (track != null) {
205             if (mMutated) {
206                 track.mutate();
207             }
208             track.setState(STATE_ENABLED);
209         }
210         mVerticalTrack = track;
211     }
212 
setHorizontalThumbDrawable(Drawable thumb)213     public void setHorizontalThumbDrawable(Drawable thumb) {
214         if (thumb != null) {
215             if (mMutated) {
216                 thumb.mutate();
217             }
218             thumb.setState(STATE_ENABLED);
219             mHorizontalThumb = thumb;
220         }
221     }
222 
setHorizontalTrackDrawable(Drawable track)223     public void setHorizontalTrackDrawable(Drawable track) {
224         if (track != null) {
225             if (mMutated) {
226                 track.mutate();
227             }
228             track.setState(STATE_ENABLED);
229         }
230         mHorizontalTrack = track;
231     }
232 
getSize(boolean vertical)233     public int getSize(boolean vertical) {
234         if (vertical) {
235             return mVerticalTrack != null ? mVerticalTrack.getIntrinsicWidth() :
236                     mVerticalThumb != null ? mVerticalThumb.getIntrinsicWidth() : 0;
237         } else {
238             return mHorizontalTrack != null ? mHorizontalTrack.getIntrinsicHeight() :
239                     mHorizontalThumb != null ? mHorizontalThumb.getIntrinsicHeight() : 0;
240         }
241     }
242 
243     @Override
mutate()244     public ScrollBarDrawable mutate() {
245         if (!mMutated && super.mutate() == this) {
246             if (mVerticalTrack != null) {
247                 mVerticalTrack.mutate();
248             }
249             if (mVerticalThumb != null) {
250                 mVerticalThumb.mutate();
251             }
252             if (mHorizontalTrack != null) {
253                 mHorizontalTrack.mutate();
254             }
255             if (mHorizontalThumb != null) {
256                 mHorizontalThumb.mutate();
257             }
258             mMutated = true;
259         }
260         return this;
261     }
262 
263     @Override
setAlpha(int alpha)264     public void setAlpha(int alpha) {
265         if (mVerticalTrack != null) {
266             mVerticalTrack.setAlpha(alpha);
267         }
268         if (mVerticalThumb != null) {
269             mVerticalThumb.setAlpha(alpha);
270         }
271         if (mHorizontalTrack != null) {
272             mHorizontalTrack.setAlpha(alpha);
273         }
274         if (mHorizontalThumb != null) {
275             mHorizontalThumb.setAlpha(alpha);
276         }
277     }
278 
279     @Override
getAlpha()280     public int getAlpha() {
281         // All elements should have same alpha, just return one of them
282         return mVerticalThumb.getAlpha();
283     }
284 
285     @Override
setColorFilter(ColorFilter cf)286     public void setColorFilter(ColorFilter cf) {
287         if (mVerticalTrack != null) {
288             mVerticalTrack.setColorFilter(cf);
289         }
290         if (mVerticalThumb != null) {
291             mVerticalThumb.setColorFilter(cf);
292         }
293         if (mHorizontalTrack != null) {
294             mHorizontalTrack.setColorFilter(cf);
295         }
296         if (mHorizontalThumb != null) {
297             mHorizontalThumb.setColorFilter(cf);
298         }
299     }
300 
301     @Override
getOpacity()302     public int getOpacity() {
303         return PixelFormat.TRANSLUCENT;
304     }
305 
306     @Override
toString()307     public String toString() {
308         return "ScrollBarDrawable: range=" + mRange + " offset=" + mOffset +
309                " extent=" + mExtent + (mVertical ? " V" : " H");
310     }
311 }
312 
313 
314