Home | History | Annotate | Download | only in widget
      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      */
     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      */
     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      */
     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      */
     95     public boolean getAlwaysDrawHorizontalTrack() {
     96         return mAlwaysDrawHorizontalTrack;
     97     }
     98 
     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
    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
    159     protected void onBoundsChange(Rect bounds) {
    160         super.onBoundsChange(bounds);
    161         mBoundsChanged = true;
    162     }
    163 
    164     @Override
    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
    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 
    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 
    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     public void setVerticalThumbDrawable(Drawable thumb) {
    233         if (mVerticalThumb != null) {
    234             mVerticalThumb.setCallback(null);
    235         }
    236 
    237         propagateCurrentState(thumb);
    238         mVerticalThumb = thumb;
    239     }
    240 
    241     public void setVerticalTrackDrawable(Drawable track) {
    242         if (mVerticalTrack != null) {
    243             mVerticalTrack.setCallback(null);
    244         }
    245 
    246         propagateCurrentState(track);
    247         mVerticalTrack = track;
    248     }
    249 
    250     public void setHorizontalThumbDrawable(Drawable thumb) {
    251         if (mHorizontalThumb != null) {
    252             mHorizontalThumb.setCallback(null);
    253         }
    254 
    255         propagateCurrentState(thumb);
    256         mHorizontalThumb = thumb;
    257     }
    258 
    259     public void setHorizontalTrackDrawable(Drawable track) {
    260         if (mHorizontalTrack != null) {
    261             mHorizontalTrack.setCallback(null);
    262         }
    263 
    264         propagateCurrentState(track);
    265         mHorizontalTrack = track;
    266     }
    267 
    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 
    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
    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
    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
    337     public int getAlpha() {
    338         return mAlpha;
    339     }
    340 
    341     @Override
    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
    361     public ColorFilter getColorFilter() {
    362         return mColorFilter;
    363     }
    364 
    365     @Override
    366     public int getOpacity() {
    367         return PixelFormat.TRANSLUCENT;
    368     }
    369 
    370     @Override
    371     public void invalidateDrawable(Drawable who) {
    372         invalidateSelf();
    373     }
    374 
    375     @Override
    376     public void scheduleDrawable(Drawable who, Runnable what, long when) {
    377         scheduleSelf(what, when);
    378     }
    379 
    380     @Override
    381     public void unscheduleDrawable(Drawable who, Runnable what) {
    382         unscheduleSelf(what);
    383     }
    384 
    385     @Override
    386     public String toString() {
    387         return "ScrollBarDrawable: range=" + mRange + " offset=" + mOffset +
    388                " extent=" + mExtent + (mVertical ? " V" : " H");
    389     }
    390 }
    391 
    392 
    393