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