• 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.widget;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.annotation.UnsupportedAppUsage;
22 import android.graphics.Canvas;
23 import android.graphics.ColorFilter;
24 import android.graphics.PixelFormat;
25 import android.graphics.Rect;
26 import android.graphics.drawable.Drawable;
27 import android.os.Build;
28 import android.view.View;
29 
30 import com.android.internal.widget.ScrollBarUtils;
31 
32 /**
33  * This is only used by View for displaying its scroll bars. It should probably
34  * be moved in to the view package since it is used in that lower-level layer.
35  * For now, we'll hide it so it can be cleaned up later.
36  *
37  * {@hide}
38  */
39 public class ScrollBarDrawable extends Drawable implements Drawable.Callback {
40     private Drawable mVerticalTrack;
41     private Drawable mHorizontalTrack;
42     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123768422)
43     private Drawable mVerticalThumb;
44     private Drawable mHorizontalThumb;
45 
46     private int mRange;
47     private int mOffset;
48     private int mExtent;
49 
50     private boolean mVertical;
51     private boolean mBoundsChanged;
52     private boolean mRangeChanged;
53     private boolean mAlwaysDrawHorizontalTrack;
54     private boolean mAlwaysDrawVerticalTrack;
55     private boolean mMutated;
56 
57     private int mAlpha = 255;
58     private boolean mHasSetAlpha;
59 
60     private ColorFilter mColorFilter;
61     private boolean mHasSetColorFilter;
62 
63     /**
64      * Indicate whether the horizontal scrollbar track should always be drawn
65      * regardless of the extent. Defaults to false.
66      *
67      * @param alwaysDrawTrack Whether the track should always be drawn
68      *
69      * @see #getAlwaysDrawHorizontalTrack()
70      */
setAlwaysDrawHorizontalTrack(boolean alwaysDrawTrack)71     public void setAlwaysDrawHorizontalTrack(boolean alwaysDrawTrack) {
72         mAlwaysDrawHorizontalTrack = alwaysDrawTrack;
73     }
74 
75     /**
76      * Indicate whether the vertical scrollbar track should always be drawn
77      * regardless of the extent. Defaults to false.
78      *
79      * @param alwaysDrawTrack Whether the track should always be drawn
80      *
81      * @see #getAlwaysDrawVerticalTrack()
82      */
setAlwaysDrawVerticalTrack(boolean alwaysDrawTrack)83     public void setAlwaysDrawVerticalTrack(boolean alwaysDrawTrack) {
84         mAlwaysDrawVerticalTrack = alwaysDrawTrack;
85     }
86 
87     /**
88      * @return whether the vertical scrollbar track should always be drawn
89      *         regardless of the extent.
90      *
91      * @see #setAlwaysDrawVerticalTrack(boolean)
92      */
getAlwaysDrawVerticalTrack()93     public boolean getAlwaysDrawVerticalTrack() {
94         return mAlwaysDrawVerticalTrack;
95     }
96 
97     /**
98      * @return whether the horizontal scrollbar track should always be drawn
99      *         regardless of the extent.
100      *
101      * @see #setAlwaysDrawHorizontalTrack(boolean)
102      */
getAlwaysDrawHorizontalTrack()103     public boolean getAlwaysDrawHorizontalTrack() {
104         return mAlwaysDrawHorizontalTrack;
105     }
106 
setParameters(int range, int offset, int extent, boolean vertical)107     public void setParameters(int range, int offset, int extent, boolean vertical) {
108         if (mVertical != vertical) {
109             mVertical = vertical;
110 
111             mBoundsChanged = true;
112         }
113 
114         if (mRange != range || mOffset != offset || mExtent != extent) {
115             mRange = range;
116             mOffset = offset;
117             mExtent = extent;
118 
119             mRangeChanged = true;
120         }
121     }
122 
123     @Override
draw(Canvas canvas)124     public void draw(Canvas canvas) {
125         final boolean vertical = mVertical;
126         final int extent = mExtent;
127         final int range = mRange;
128 
129         boolean drawTrack = true;
130         boolean drawThumb = true;
131         if (extent <= 0 || range <= extent) {
132             drawTrack = vertical ? mAlwaysDrawVerticalTrack : mAlwaysDrawHorizontalTrack;
133             drawThumb = false;
134         }
135 
136         final Rect r = getBounds();
137         if (canvas.quickReject(r.left, r.top, r.right, r.bottom, Canvas.EdgeType.AA)) {
138             return;
139         }
140 
141         if (drawTrack) {
142             drawTrack(canvas, r, vertical);
143         }
144 
145         if (drawThumb) {
146             final int scrollBarLength = vertical ? r.height() : r.width();
147             final int thickness = vertical ? r.width() : r.height();
148             final int thumbLength =
149                     ScrollBarUtils.getThumbLength(scrollBarLength, thickness, extent, range);
150             final int thumbOffset =
151                     ScrollBarUtils.getThumbOffset(scrollBarLength, thumbLength, extent, range,
152                             mOffset);
153 
154             drawThumb(canvas, r, thumbOffset, thumbLength, vertical);
155         }
156     }
157 
158     @Override
onBoundsChange(Rect bounds)159     protected void onBoundsChange(Rect bounds) {
160         super.onBoundsChange(bounds);
161         mBoundsChanged = true;
162     }
163 
164     @Override
isStateful()165     public boolean isStateful() {
166         return (mVerticalTrack != null && mVerticalTrack.isStateful())
167                 || (mVerticalThumb != null && mVerticalThumb.isStateful())
168                 || (mHorizontalTrack != null && mHorizontalTrack.isStateful())
169                 || (mHorizontalThumb != null && mHorizontalThumb.isStateful())
170                 || super.isStateful();
171     }
172 
173     @Override
onStateChange(int[] state)174     protected boolean onStateChange(int[] state) {
175         boolean changed = super.onStateChange(state);
176         if (mVerticalTrack != null) {
177             changed |= mVerticalTrack.setState(state);
178         }
179         if (mVerticalThumb != null) {
180             changed |= mVerticalThumb.setState(state);
181         }
182         if (mHorizontalTrack != null) {
183             changed |= mHorizontalTrack.setState(state);
184         }
185         if (mHorizontalThumb != null) {
186             changed |= mHorizontalThumb.setState(state);
187         }
188         return changed;
189     }
190 
drawTrack(Canvas canvas, Rect bounds, boolean vertical)191     private void drawTrack(Canvas canvas, Rect bounds, boolean vertical) {
192         final Drawable track;
193         if (vertical) {
194             track = mVerticalTrack;
195         } else {
196             track = mHorizontalTrack;
197         }
198 
199         if (track != null) {
200             if (mBoundsChanged) {
201                 track.setBounds(bounds);
202             }
203             track.draw(canvas);
204         }
205     }
206 
drawThumb(Canvas canvas, Rect bounds, int offset, int length, boolean vertical)207     private void drawThumb(Canvas canvas, Rect bounds, int offset, int length, boolean vertical) {
208         final boolean changed = mRangeChanged || mBoundsChanged;
209         if (vertical) {
210             if (mVerticalThumb != null) {
211                 final Drawable thumb = mVerticalThumb;
212                 if (changed) {
213                     thumb.setBounds(bounds.left, bounds.top + offset,
214                             bounds.right, bounds.top + offset + length);
215                 }
216 
217                 thumb.draw(canvas);
218             }
219         } else {
220             if (mHorizontalThumb != null) {
221                 final Drawable thumb = mHorizontalThumb;
222                 if (changed) {
223                     thumb.setBounds(bounds.left + offset, bounds.top,
224                             bounds.left + offset + length, bounds.bottom);
225                 }
226 
227                 thumb.draw(canvas);
228             }
229         }
230     }
231 
232     /**
233      * @see android.view.View#setVerticalThumbDrawable(Drawable)
234      */
235     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
setVerticalThumbDrawable(Drawable thumb)236     public void setVerticalThumbDrawable(Drawable thumb) {
237         if (mVerticalThumb != null) {
238             mVerticalThumb.setCallback(null);
239         }
240 
241         propagateCurrentState(thumb);
242         mVerticalThumb = thumb;
243     }
244 
245     /**
246      * @see View#getVerticalTrackDrawable()
247      */
getVerticalTrackDrawable()248     public @Nullable Drawable getVerticalTrackDrawable() {
249         return mVerticalTrack;
250     }
251 
252     /**
253      * @see View#getVerticalThumbDrawable()
254      */
getVerticalThumbDrawable()255     public @Nullable Drawable getVerticalThumbDrawable() {
256         return mVerticalThumb;
257     }
258 
259     /**
260      * @see View#getHorizontalTrackDrawable()
261      */
getHorizontalTrackDrawable()262     public @Nullable Drawable getHorizontalTrackDrawable() {
263         return mHorizontalTrack;
264     }
265 
266     /**
267      * @see View#getHorizontalThumbDrawable()
268      */
getHorizontalThumbDrawable()269     public @Nullable Drawable getHorizontalThumbDrawable() {
270         return mHorizontalThumb;
271     }
272 
273     /**
274      * @see android.view.View#setVerticalTrackDrawable(Drawable)
275      */
setVerticalTrackDrawable(Drawable track)276     public void setVerticalTrackDrawable(Drawable track) {
277         if (mVerticalTrack != null) {
278             mVerticalTrack.setCallback(null);
279         }
280 
281         propagateCurrentState(track);
282         mVerticalTrack = track;
283     }
284 
285     /**
286      * @see android.view.View#setHorizontalThumbDrawable(Drawable)
287      */
288     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
setHorizontalThumbDrawable(Drawable thumb)289     public void setHorizontalThumbDrawable(Drawable thumb) {
290         if (mHorizontalThumb != null) {
291             mHorizontalThumb.setCallback(null);
292         }
293 
294         propagateCurrentState(thumb);
295         mHorizontalThumb = thumb;
296     }
297 
setHorizontalTrackDrawable(Drawable track)298     public void setHorizontalTrackDrawable(Drawable track) {
299         if (mHorizontalTrack != null) {
300             mHorizontalTrack.setCallback(null);
301         }
302 
303         propagateCurrentState(track);
304         mHorizontalTrack = track;
305     }
306 
propagateCurrentState(Drawable d)307     private void propagateCurrentState(Drawable d) {
308         if (d != null) {
309             if (mMutated) {
310                 d.mutate();
311             }
312 
313             d.setState(getState());
314             d.setCallback(this);
315 
316             if (mHasSetAlpha) {
317                 d.setAlpha(mAlpha);
318             }
319 
320             if (mHasSetColorFilter) {
321                 d.setColorFilter(mColorFilter);
322             }
323         }
324     }
325 
getSize(boolean vertical)326     public int getSize(boolean vertical) {
327         if (vertical) {
328             return mVerticalTrack != null ? mVerticalTrack.getIntrinsicWidth() :
329                     mVerticalThumb != null ? mVerticalThumb.getIntrinsicWidth() : 0;
330         } else {
331             return mHorizontalTrack != null ? mHorizontalTrack.getIntrinsicHeight() :
332                     mHorizontalThumb != null ? mHorizontalThumb.getIntrinsicHeight() : 0;
333         }
334     }
335 
336     @Override
mutate()337     public ScrollBarDrawable mutate() {
338         if (!mMutated && super.mutate() == this) {
339             if (mVerticalTrack != null) {
340                 mVerticalTrack.mutate();
341             }
342             if (mVerticalThumb != null) {
343                 mVerticalThumb.mutate();
344             }
345             if (mHorizontalTrack != null) {
346                 mHorizontalTrack.mutate();
347             }
348             if (mHorizontalThumb != null) {
349                 mHorizontalThumb.mutate();
350             }
351             mMutated = true;
352         }
353         return this;
354     }
355 
356     @Override
setAlpha(int alpha)357     public void setAlpha(int alpha) {
358         mAlpha = alpha;
359         mHasSetAlpha = true;
360 
361         if (mVerticalTrack != null) {
362             mVerticalTrack.setAlpha(alpha);
363         }
364         if (mVerticalThumb != null) {
365             mVerticalThumb.setAlpha(alpha);
366         }
367         if (mHorizontalTrack != null) {
368             mHorizontalTrack.setAlpha(alpha);
369         }
370         if (mHorizontalThumb != null) {
371             mHorizontalThumb.setAlpha(alpha);
372         }
373     }
374 
375     @Override
getAlpha()376     public int getAlpha() {
377         return mAlpha;
378     }
379 
380     @Override
setColorFilter(ColorFilter colorFilter)381     public void setColorFilter(ColorFilter colorFilter) {
382         mColorFilter = colorFilter;
383         mHasSetColorFilter = true;
384 
385         if (mVerticalTrack != null) {
386             mVerticalTrack.setColorFilter(colorFilter);
387         }
388         if (mVerticalThumb != null) {
389             mVerticalThumb.setColorFilter(colorFilter);
390         }
391         if (mHorizontalTrack != null) {
392             mHorizontalTrack.setColorFilter(colorFilter);
393         }
394         if (mHorizontalThumb != null) {
395             mHorizontalThumb.setColorFilter(colorFilter);
396         }
397     }
398 
399     @Override
getColorFilter()400     public ColorFilter getColorFilter() {
401         return mColorFilter;
402     }
403 
404     @Override
getOpacity()405     public int getOpacity() {
406         return PixelFormat.TRANSLUCENT;
407     }
408 
409     @Override
invalidateDrawable(@onNull Drawable who)410     public void invalidateDrawable(@NonNull Drawable who) {
411         invalidateSelf();
412     }
413 
414     @Override
scheduleDrawable(@onNull Drawable who, @NonNull Runnable what, long when)415     public void scheduleDrawable(@NonNull Drawable who, @NonNull Runnable what, long when) {
416         scheduleSelf(what, when);
417     }
418 
419     @Override
unscheduleDrawable(@onNull Drawable who, @NonNull Runnable what)420     public void unscheduleDrawable(@NonNull Drawable who, @NonNull Runnable what) {
421         unscheduleSelf(what);
422     }
423 
424     @Override
toString()425     public String toString() {
426         return "ScrollBarDrawable: range=" + mRange + " offset=" + mOffset +
427                " extent=" + mExtent + (mVertical ? " V" : " H");
428     }
429 }
430 
431 
432