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