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 * 30 * {@hide} 31 */ 32 public class ScrollBarDrawable extends Drawable implements Drawable.Callback { 33 private Drawable mVerticalTrack; 34 private Drawable mHorizontalTrack; 35 private Drawable mVerticalThumb; 36 private Drawable mHorizontalThumb; 37 38 private int mRange; 39 private int mOffset; 40 private int mExtent; 41 42 private boolean mVertical; 43 private boolean mBoundsChanged; 44 private boolean mRangeChanged; 45 private boolean mAlwaysDrawHorizontalTrack; 46 private boolean mAlwaysDrawVerticalTrack; 47 private boolean mMutated; 48 49 private int mAlpha = 255; 50 private boolean mHasSetAlpha; 51 52 private ColorFilter mColorFilter; 53 private boolean mHasSetColorFilter; 54 55 /** 56 * Indicate whether the horizontal scrollbar track should always be drawn 57 * regardless of the extent. Defaults to false. 58 * 59 * @param alwaysDrawTrack Whether the track should always be drawn 60 * 61 * @see #getAlwaysDrawHorizontalTrack() 62 */ setAlwaysDrawHorizontalTrack(boolean alwaysDrawTrack)63 public void setAlwaysDrawHorizontalTrack(boolean alwaysDrawTrack) { 64 mAlwaysDrawHorizontalTrack = alwaysDrawTrack; 65 } 66 67 /** 68 * Indicate whether the vertical scrollbar track should always be drawn 69 * regardless of the extent. Defaults to false. 70 * 71 * @param alwaysDrawTrack Whether the track should always be drawn 72 * 73 * @see #getAlwaysDrawVerticalTrack() 74 */ setAlwaysDrawVerticalTrack(boolean alwaysDrawTrack)75 public void setAlwaysDrawVerticalTrack(boolean alwaysDrawTrack) { 76 mAlwaysDrawVerticalTrack = alwaysDrawTrack; 77 } 78 79 /** 80 * @return whether the vertical scrollbar track should always be drawn 81 * regardless of the extent. 82 * 83 * @see #setAlwaysDrawVerticalTrack(boolean) 84 */ getAlwaysDrawVerticalTrack()85 public boolean getAlwaysDrawVerticalTrack() { 86 return mAlwaysDrawVerticalTrack; 87 } 88 89 /** 90 * @return whether the horizontal scrollbar track should always be drawn 91 * regardless of the extent. 92 * 93 * @see #setAlwaysDrawHorizontalTrack(boolean) 94 */ getAlwaysDrawHorizontalTrack()95 public boolean getAlwaysDrawHorizontalTrack() { 96 return mAlwaysDrawHorizontalTrack; 97 } 98 setParameters(int range, int offset, int extent, boolean vertical)99 public void setParameters(int range, int offset, int extent, boolean vertical) { 100 if (mVertical != vertical) { 101 mVertical = vertical; 102 103 mBoundsChanged = true; 104 } 105 106 if (mRange != range || mOffset != offset || mExtent != extent) { 107 mRange = range; 108 mOffset = offset; 109 mExtent = extent; 110 111 mRangeChanged = true; 112 } 113 } 114 115 @Override draw(Canvas canvas)116 public void draw(Canvas canvas) { 117 final boolean vertical = mVertical; 118 final int extent = mExtent; 119 final int range = mRange; 120 121 boolean drawTrack = true; 122 boolean drawThumb = true; 123 if (extent <= 0 || range <= extent) { 124 drawTrack = vertical ? mAlwaysDrawVerticalTrack : mAlwaysDrawHorizontalTrack; 125 drawThumb = false; 126 } 127 128 final Rect r = getBounds(); 129 if (canvas.quickReject(r.left, r.top, r.right, r.bottom, Canvas.EdgeType.AA)) { 130 return; 131 } 132 133 if (drawTrack) { 134 drawTrack(canvas, r, vertical); 135 } 136 137 if (drawThumb) { 138 final int size = vertical ? r.height() : r.width(); 139 final int thickness = vertical ? r.width() : r.height(); 140 final int minLength = thickness * 2; 141 142 // Avoid the tiny thumb. 143 int length = Math.round((float) size * extent / range); 144 if (length < minLength) { 145 length = minLength; 146 } 147 148 // Avoid the too-big thumb. 149 int offset = Math.round((float) (size - length) * mOffset / (range - extent)); 150 if (offset > size - length) { 151 offset = size - length; 152 } 153 154 drawThumb(canvas, r, offset, length, 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 setVerticalThumbDrawable(Drawable thumb)232 public void setVerticalThumbDrawable(Drawable thumb) { 233 if (mVerticalThumb != null) { 234 mVerticalThumb.setCallback(null); 235 } 236 237 propagateCurrentState(thumb); 238 mVerticalThumb = thumb; 239 } 240 setVerticalTrackDrawable(Drawable track)241 public void setVerticalTrackDrawable(Drawable track) { 242 if (mVerticalTrack != null) { 243 mVerticalTrack.setCallback(null); 244 } 245 246 propagateCurrentState(track); 247 mVerticalTrack = track; 248 } 249 setHorizontalThumbDrawable(Drawable thumb)250 public void setHorizontalThumbDrawable(Drawable thumb) { 251 if (mHorizontalThumb != null) { 252 mHorizontalThumb.setCallback(null); 253 } 254 255 propagateCurrentState(thumb); 256 mHorizontalThumb = thumb; 257 } 258 setHorizontalTrackDrawable(Drawable track)259 public void setHorizontalTrackDrawable(Drawable track) { 260 if (mHorizontalTrack != null) { 261 mHorizontalTrack.setCallback(null); 262 } 263 264 propagateCurrentState(track); 265 mHorizontalTrack = track; 266 } 267 propagateCurrentState(Drawable d)268 private void propagateCurrentState(Drawable d) { 269 if (d != null) { 270 if (mMutated) { 271 d.mutate(); 272 } 273 274 d.setState(getState()); 275 d.setCallback(this); 276 277 if (mHasSetAlpha) { 278 d.setAlpha(mAlpha); 279 } 280 281 if (mHasSetColorFilter) { 282 d.setColorFilter(mColorFilter); 283 } 284 } 285 } 286 getSize(boolean vertical)287 public int getSize(boolean vertical) { 288 if (vertical) { 289 return mVerticalTrack != null ? mVerticalTrack.getIntrinsicWidth() : 290 mVerticalThumb != null ? mVerticalThumb.getIntrinsicWidth() : 0; 291 } else { 292 return mHorizontalTrack != null ? mHorizontalTrack.getIntrinsicHeight() : 293 mHorizontalThumb != null ? mHorizontalThumb.getIntrinsicHeight() : 0; 294 } 295 } 296 297 @Override mutate()298 public ScrollBarDrawable mutate() { 299 if (!mMutated && super.mutate() == this) { 300 if (mVerticalTrack != null) { 301 mVerticalTrack.mutate(); 302 } 303 if (mVerticalThumb != null) { 304 mVerticalThumb.mutate(); 305 } 306 if (mHorizontalTrack != null) { 307 mHorizontalTrack.mutate(); 308 } 309 if (mHorizontalThumb != null) { 310 mHorizontalThumb.mutate(); 311 } 312 mMutated = true; 313 } 314 return this; 315 } 316 317 @Override setAlpha(int alpha)318 public void setAlpha(int alpha) { 319 mAlpha = alpha; 320 mHasSetAlpha = true; 321 322 if (mVerticalTrack != null) { 323 mVerticalTrack.setAlpha(alpha); 324 } 325 if (mVerticalThumb != null) { 326 mVerticalThumb.setAlpha(alpha); 327 } 328 if (mHorizontalTrack != null) { 329 mHorizontalTrack.setAlpha(alpha); 330 } 331 if (mHorizontalThumb != null) { 332 mHorizontalThumb.setAlpha(alpha); 333 } 334 } 335 336 @Override getAlpha()337 public int getAlpha() { 338 return mAlpha; 339 } 340 341 @Override setColorFilter(ColorFilter colorFilter)342 public void setColorFilter(ColorFilter colorFilter) { 343 mColorFilter = colorFilter; 344 mHasSetColorFilter = true; 345 346 if (mVerticalTrack != null) { 347 mVerticalTrack.setColorFilter(colorFilter); 348 } 349 if (mVerticalThumb != null) { 350 mVerticalThumb.setColorFilter(colorFilter); 351 } 352 if (mHorizontalTrack != null) { 353 mHorizontalTrack.setColorFilter(colorFilter); 354 } 355 if (mHorizontalThumb != null) { 356 mHorizontalThumb.setColorFilter(colorFilter); 357 } 358 } 359 360 @Override getColorFilter()361 public ColorFilter getColorFilter() { 362 return mColorFilter; 363 } 364 365 @Override getOpacity()366 public int getOpacity() { 367 return PixelFormat.TRANSLUCENT; 368 } 369 370 @Override invalidateDrawable(Drawable who)371 public void invalidateDrawable(Drawable who) { 372 invalidateSelf(); 373 } 374 375 @Override scheduleDrawable(Drawable who, Runnable what, long when)376 public void scheduleDrawable(Drawable who, Runnable what, long when) { 377 scheduleSelf(what, when); 378 } 379 380 @Override unscheduleDrawable(Drawable who, Runnable what)381 public void unscheduleDrawable(Drawable who, Runnable what) { 382 unscheduleSelf(what); 383 } 384 385 @Override toString()386 public String toString() { 387 return "ScrollBarDrawable: range=" + mRange + " offset=" + mOffset + 388 " extent=" + mExtent + (mVertical ? " V" : " H"); 389 } 390 } 391 392 393