Home | History | Annotate | Download | only in widget
      1 /*
      2  * Copyright (C) 2010 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.content.Context;
     20 import android.content.res.ColorStateList;
     21 import android.content.res.Resources;
     22 import android.content.res.TypedArray;
     23 import android.graphics.Canvas;
     24 import android.graphics.Paint;
     25 import android.graphics.Rect;
     26 import android.graphics.Typeface;
     27 import android.graphics.drawable.Drawable;
     28 import android.text.Layout;
     29 import android.text.StaticLayout;
     30 import android.text.TextPaint;
     31 import android.text.TextUtils;
     32 import android.util.AttributeSet;
     33 import android.view.Gravity;
     34 import android.view.MotionEvent;
     35 import android.view.VelocityTracker;
     36 import android.view.ViewConfiguration;
     37 import android.view.accessibility.AccessibilityEvent;
     38 
     39 import com.android.internal.R;
     40 
     41 /**
     42  * A Switch is a two-state toggle switch widget that can select between two
     43  * options. The user may drag the "thumb" back and forth to choose the selected option,
     44  * or simply tap to toggle as if it were a checkbox. The {@link #setText(CharSequence) text}
     45  * property controls the text displayed in the label for the switch, whereas the
     46  * {@link #setTextOff(CharSequence) off} and {@link #setTextOn(CharSequence) on} text
     47  * controls the text on the thumb. Similarly, the
     48  * {@link #setTextAppearance(android.content.Context, int) textAppearance} and the related
     49  * setTypeface() methods control the typeface and style of label text, whereas the
     50  * {@link #setSwitchTextAppearance(android.content.Context, int) switchTextAppearance} and
     51  * the related seSwitchTypeface() methods control that of the thumb.
     52  *
     53  */
     54 public class Switch extends CompoundButton {
     55     private static final int TOUCH_MODE_IDLE = 0;
     56     private static final int TOUCH_MODE_DOWN = 1;
     57     private static final int TOUCH_MODE_DRAGGING = 2;
     58 
     59     // Enum for the "typeface" XML parameter.
     60     private static final int SANS = 1;
     61     private static final int SERIF = 2;
     62     private static final int MONOSPACE = 3;
     63 
     64     private Drawable mThumbDrawable;
     65     private Drawable mTrackDrawable;
     66     private int mThumbTextPadding;
     67     private int mSwitchMinWidth;
     68     private int mSwitchPadding;
     69     private CharSequence mTextOn;
     70     private CharSequence mTextOff;
     71 
     72     private int mTouchMode;
     73     private int mTouchSlop;
     74     private float mTouchX;
     75     private float mTouchY;
     76     private VelocityTracker mVelocityTracker = VelocityTracker.obtain();
     77     private int mMinFlingVelocity;
     78 
     79     private float mThumbPosition;
     80     private int mSwitchWidth;
     81     private int mSwitchHeight;
     82     private int mThumbWidth; // Does not include padding
     83 
     84     private int mSwitchLeft;
     85     private int mSwitchTop;
     86     private int mSwitchRight;
     87     private int mSwitchBottom;
     88 
     89     private TextPaint mTextPaint;
     90     private ColorStateList mTextColors;
     91     private Layout mOnLayout;
     92     private Layout mOffLayout;
     93 
     94     @SuppressWarnings("hiding")
     95     private final Rect mTempRect = new Rect();
     96 
     97     private static final int[] CHECKED_STATE_SET = {
     98         R.attr.state_checked
     99     };
    100 
    101     /**
    102      * Construct a new Switch with default styling.
    103      *
    104      * @param context The Context that will determine this widget's theming.
    105      */
    106     public Switch(Context context) {
    107         this(context, null);
    108     }
    109 
    110     /**
    111      * Construct a new Switch with default styling, overriding specific style
    112      * attributes as requested.
    113      *
    114      * @param context The Context that will determine this widget's theming.
    115      * @param attrs Specification of attributes that should deviate from default styling.
    116      */
    117     public Switch(Context context, AttributeSet attrs) {
    118         this(context, attrs, com.android.internal.R.attr.switchStyle);
    119     }
    120 
    121     /**
    122      * Construct a new Switch with a default style determined by the given theme attribute,
    123      * overriding specific style attributes as requested.
    124      *
    125      * @param context The Context that will determine this widget's theming.
    126      * @param attrs Specification of attributes that should deviate from the default styling.
    127      * @param defStyle An attribute ID within the active theme containing a reference to the
    128      *                 default style for this widget. e.g. android.R.attr.switchStyle.
    129      */
    130     public Switch(Context context, AttributeSet attrs, int defStyle) {
    131         super(context, attrs, defStyle);
    132 
    133         mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
    134         Resources res = getResources();
    135         mTextPaint.density = res.getDisplayMetrics().density;
    136         mTextPaint.setCompatibilityScaling(res.getCompatibilityInfo().applicationScale);
    137 
    138         TypedArray a = context.obtainStyledAttributes(attrs,
    139                 com.android.internal.R.styleable.Switch, defStyle, 0);
    140 
    141         mThumbDrawable = a.getDrawable(com.android.internal.R.styleable.Switch_thumb);
    142         mTrackDrawable = a.getDrawable(com.android.internal.R.styleable.Switch_track);
    143         mTextOn = a.getText(com.android.internal.R.styleable.Switch_textOn);
    144         mTextOff = a.getText(com.android.internal.R.styleable.Switch_textOff);
    145         mThumbTextPadding = a.getDimensionPixelSize(
    146                 com.android.internal.R.styleable.Switch_thumbTextPadding, 0);
    147         mSwitchMinWidth = a.getDimensionPixelSize(
    148                 com.android.internal.R.styleable.Switch_switchMinWidth, 0);
    149         mSwitchPadding = a.getDimensionPixelSize(
    150                 com.android.internal.R.styleable.Switch_switchPadding, 0);
    151 
    152         int appearance = a.getResourceId(
    153                 com.android.internal.R.styleable.Switch_switchTextAppearance, 0);
    154         if (appearance != 0) {
    155             setSwitchTextAppearance(context, appearance);
    156         }
    157         a.recycle();
    158 
    159         ViewConfiguration config = ViewConfiguration.get(context);
    160         mTouchSlop = config.getScaledTouchSlop();
    161         mMinFlingVelocity = config.getScaledMinimumFlingVelocity();
    162 
    163         // Refresh display with current params
    164         refreshDrawableState();
    165         setChecked(isChecked());
    166     }
    167 
    168     /**
    169      * Sets the switch text color, size, style, hint color, and highlight color
    170      * from the specified TextAppearance resource.
    171      */
    172     public void setSwitchTextAppearance(Context context, int resid) {
    173         TypedArray appearance =
    174                 context.obtainStyledAttributes(resid,
    175                         com.android.internal.R.styleable.TextAppearance);
    176 
    177         ColorStateList colors;
    178         int ts;
    179 
    180         colors = appearance.getColorStateList(com.android.internal.R.styleable.
    181                 TextAppearance_textColor);
    182         if (colors != null) {
    183             mTextColors = colors;
    184         } else {
    185             // If no color set in TextAppearance, default to the view's textColor
    186             mTextColors = getTextColors();
    187         }
    188 
    189         ts = appearance.getDimensionPixelSize(com.android.internal.R.styleable.
    190                 TextAppearance_textSize, 0);
    191         if (ts != 0) {
    192             if (ts != mTextPaint.getTextSize()) {
    193                 mTextPaint.setTextSize(ts);
    194                 requestLayout();
    195             }
    196         }
    197 
    198         int typefaceIndex, styleIndex;
    199 
    200         typefaceIndex = appearance.getInt(com.android.internal.R.styleable.
    201                 TextAppearance_typeface, -1);
    202         styleIndex = appearance.getInt(com.android.internal.R.styleable.
    203                 TextAppearance_textStyle, -1);
    204 
    205         setSwitchTypefaceByIndex(typefaceIndex, styleIndex);
    206 
    207         appearance.recycle();
    208     }
    209 
    210     private void setSwitchTypefaceByIndex(int typefaceIndex, int styleIndex) {
    211         Typeface tf = null;
    212         switch (typefaceIndex) {
    213             case SANS:
    214                 tf = Typeface.SANS_SERIF;
    215                 break;
    216 
    217             case SERIF:
    218                 tf = Typeface.SERIF;
    219                 break;
    220 
    221             case MONOSPACE:
    222                 tf = Typeface.MONOSPACE;
    223                 break;
    224         }
    225 
    226         setSwitchTypeface(tf, styleIndex);
    227     }
    228 
    229     /**
    230      * Sets the typeface and style in which the text should be displayed on the
    231      * switch, and turns on the fake bold and italic bits in the Paint if the
    232      * Typeface that you provided does not have all the bits in the
    233      * style that you specified.
    234      */
    235     public void setSwitchTypeface(Typeface tf, int style) {
    236         if (style > 0) {
    237             if (tf == null) {
    238                 tf = Typeface.defaultFromStyle(style);
    239             } else {
    240                 tf = Typeface.create(tf, style);
    241             }
    242 
    243             setSwitchTypeface(tf);
    244             // now compute what (if any) algorithmic styling is needed
    245             int typefaceStyle = tf != null ? tf.getStyle() : 0;
    246             int need = style & ~typefaceStyle;
    247             mTextPaint.setFakeBoldText((need & Typeface.BOLD) != 0);
    248             mTextPaint.setTextSkewX((need & Typeface.ITALIC) != 0 ? -0.25f : 0);
    249         } else {
    250             mTextPaint.setFakeBoldText(false);
    251             mTextPaint.setTextSkewX(0);
    252             setSwitchTypeface(tf);
    253         }
    254     }
    255 
    256     /**
    257      * Sets the typeface in which the text should be displayed on the switch.
    258      * Note that not all Typeface families actually have bold and italic
    259      * variants, so you may need to use
    260      * {@link #setSwitchTypeface(Typeface, int)} to get the appearance
    261      * that you actually want.
    262      *
    263      * @attr ref android.R.styleable#TextView_typeface
    264      * @attr ref android.R.styleable#TextView_textStyle
    265      */
    266     public void setSwitchTypeface(Typeface tf) {
    267         if (mTextPaint.getTypeface() != tf) {
    268             mTextPaint.setTypeface(tf);
    269 
    270             requestLayout();
    271             invalidate();
    272         }
    273     }
    274 
    275     /**
    276      * Returns the text displayed when the button is in the checked state.
    277      */
    278     public CharSequence getTextOn() {
    279         return mTextOn;
    280     }
    281 
    282     /**
    283      * Sets the text displayed when the button is in the checked state.
    284      */
    285     public void setTextOn(CharSequence textOn) {
    286         mTextOn = textOn;
    287         requestLayout();
    288     }
    289 
    290     /**
    291      * Returns the text displayed when the button is not in the checked state.
    292      */
    293     public CharSequence getTextOff() {
    294         return mTextOff;
    295     }
    296 
    297     /**
    298      * Sets the text displayed when the button is not in the checked state.
    299      */
    300     public void setTextOff(CharSequence textOff) {
    301         mTextOff = textOff;
    302         requestLayout();
    303     }
    304 
    305     @Override
    306     public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    307         final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
    308         final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
    309         int widthSize = MeasureSpec.getSize(widthMeasureSpec);
    310         int heightSize = MeasureSpec.getSize(heightMeasureSpec);
    311 
    312 
    313         if (mOnLayout == null) {
    314             mOnLayout = makeLayout(mTextOn);
    315         }
    316         if (mOffLayout == null) {
    317             mOffLayout = makeLayout(mTextOff);
    318         }
    319 
    320         mTrackDrawable.getPadding(mTempRect);
    321         final int maxTextWidth = Math.max(mOnLayout.getWidth(), mOffLayout.getWidth());
    322         final int switchWidth = Math.max(mSwitchMinWidth,
    323                 maxTextWidth * 2 + mThumbTextPadding * 4 + mTempRect.left + mTempRect.right);
    324         final int switchHeight = mTrackDrawable.getIntrinsicHeight();
    325 
    326         mThumbWidth = maxTextWidth + mThumbTextPadding * 2;
    327 
    328         switch (widthMode) {
    329             case MeasureSpec.AT_MOST:
    330                 widthSize = Math.min(widthSize, switchWidth);
    331                 break;
    332 
    333             case MeasureSpec.UNSPECIFIED:
    334                 widthSize = switchWidth;
    335                 break;
    336 
    337             case MeasureSpec.EXACTLY:
    338                 // Just use what we were given
    339                 break;
    340         }
    341 
    342         switch (heightMode) {
    343             case MeasureSpec.AT_MOST:
    344                 heightSize = Math.min(heightSize, switchHeight);
    345                 break;
    346 
    347             case MeasureSpec.UNSPECIFIED:
    348                 heightSize = switchHeight;
    349                 break;
    350 
    351             case MeasureSpec.EXACTLY:
    352                 // Just use what we were given
    353                 break;
    354         }
    355 
    356         mSwitchWidth = switchWidth;
    357         mSwitchHeight = switchHeight;
    358 
    359         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    360         final int measuredHeight = getMeasuredHeight();
    361         if (measuredHeight < switchHeight) {
    362             setMeasuredDimension(getMeasuredWidthAndState(), switchHeight);
    363         }
    364     }
    365 
    366     @Override
    367     public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
    368         super.onPopulateAccessibilityEvent(event);
    369         if (isChecked()) {
    370             CharSequence text = mOnLayout.getText();
    371             if (TextUtils.isEmpty(text)) {
    372                 text = mContext.getString(R.string.switch_on);
    373             }
    374             event.getText().add(text);
    375         } else {
    376             CharSequence text = mOffLayout.getText();
    377             if (TextUtils.isEmpty(text)) {
    378                 text = mContext.getString(R.string.switch_off);
    379             }
    380             event.getText().add(text);
    381         }
    382     }
    383 
    384     private Layout makeLayout(CharSequence text) {
    385         return new StaticLayout(text, mTextPaint,
    386                 (int) Math.ceil(Layout.getDesiredWidth(text, mTextPaint)),
    387                 Layout.Alignment.ALIGN_NORMAL, 1.f, 0, true);
    388     }
    389 
    390     /**
    391      * @return true if (x, y) is within the target area of the switch thumb
    392      */
    393     private boolean hitThumb(float x, float y) {
    394         mThumbDrawable.getPadding(mTempRect);
    395         final int thumbTop = mSwitchTop - mTouchSlop;
    396         final int thumbLeft = mSwitchLeft + (int) (mThumbPosition + 0.5f) - mTouchSlop;
    397         final int thumbRight = thumbLeft + mThumbWidth +
    398                 mTempRect.left + mTempRect.right + mTouchSlop;
    399         final int thumbBottom = mSwitchBottom + mTouchSlop;
    400         return x > thumbLeft && x < thumbRight && y > thumbTop && y < thumbBottom;
    401     }
    402 
    403     @Override
    404     public boolean onTouchEvent(MotionEvent ev) {
    405         mVelocityTracker.addMovement(ev);
    406         final int action = ev.getActionMasked();
    407         switch (action) {
    408             case MotionEvent.ACTION_DOWN: {
    409                 final float x = ev.getX();
    410                 final float y = ev.getY();
    411                 if (isEnabled() && hitThumb(x, y)) {
    412                     mTouchMode = TOUCH_MODE_DOWN;
    413                     mTouchX = x;
    414                     mTouchY = y;
    415                 }
    416                 break;
    417             }
    418 
    419             case MotionEvent.ACTION_MOVE: {
    420                 switch (mTouchMode) {
    421                     case TOUCH_MODE_IDLE:
    422                         // Didn't target the thumb, treat normally.
    423                         break;
    424 
    425                     case TOUCH_MODE_DOWN: {
    426                         final float x = ev.getX();
    427                         final float y = ev.getY();
    428                         if (Math.abs(x - mTouchX) > mTouchSlop ||
    429                                 Math.abs(y - mTouchY) > mTouchSlop) {
    430                             mTouchMode = TOUCH_MODE_DRAGGING;
    431                             getParent().requestDisallowInterceptTouchEvent(true);
    432                             mTouchX = x;
    433                             mTouchY = y;
    434                             return true;
    435                         }
    436                         break;
    437                     }
    438 
    439                     case TOUCH_MODE_DRAGGING: {
    440                         final float x = ev.getX();
    441                         final float dx = x - mTouchX;
    442                         float newPos = Math.max(0,
    443                                 Math.min(mThumbPosition + dx, getThumbScrollRange()));
    444                         if (newPos != mThumbPosition) {
    445                             mThumbPosition = newPos;
    446                             mTouchX = x;
    447                             invalidate();
    448                         }
    449                         return true;
    450                     }
    451                 }
    452                 break;
    453             }
    454 
    455             case MotionEvent.ACTION_UP:
    456             case MotionEvent.ACTION_CANCEL: {
    457                 if (mTouchMode == TOUCH_MODE_DRAGGING) {
    458                     stopDrag(ev);
    459                     return true;
    460                 }
    461                 mTouchMode = TOUCH_MODE_IDLE;
    462                 mVelocityTracker.clear();
    463                 break;
    464             }
    465         }
    466 
    467         return super.onTouchEvent(ev);
    468     }
    469 
    470     private void cancelSuperTouch(MotionEvent ev) {
    471         MotionEvent cancel = MotionEvent.obtain(ev);
    472         cancel.setAction(MotionEvent.ACTION_CANCEL);
    473         super.onTouchEvent(cancel);
    474         cancel.recycle();
    475     }
    476 
    477     /**
    478      * Called from onTouchEvent to end a drag operation.
    479      *
    480      * @param ev Event that triggered the end of drag mode - ACTION_UP or ACTION_CANCEL
    481      */
    482     private void stopDrag(MotionEvent ev) {
    483         mTouchMode = TOUCH_MODE_IDLE;
    484         // Up and not canceled, also checks the switch has not been disabled during the drag
    485         boolean commitChange = ev.getAction() == MotionEvent.ACTION_UP && isEnabled();
    486 
    487         cancelSuperTouch(ev);
    488 
    489         if (commitChange) {
    490             boolean newState;
    491             mVelocityTracker.computeCurrentVelocity(1000);
    492             float xvel = mVelocityTracker.getXVelocity();
    493             if (Math.abs(xvel) > mMinFlingVelocity) {
    494                 newState = xvel > 0;
    495             } else {
    496                 newState = getTargetCheckedState();
    497             }
    498             animateThumbToCheckedState(newState);
    499         } else {
    500             animateThumbToCheckedState(isChecked());
    501         }
    502     }
    503 
    504     private void animateThumbToCheckedState(boolean newCheckedState) {
    505         // TODO animate!
    506         //float targetPos = newCheckedState ? 0 : getThumbScrollRange();
    507         //mThumbPosition = targetPos;
    508         setChecked(newCheckedState);
    509     }
    510 
    511     private boolean getTargetCheckedState() {
    512         return mThumbPosition >= getThumbScrollRange() / 2;
    513     }
    514 
    515     @Override
    516     public void setChecked(boolean checked) {
    517         super.setChecked(checked);
    518         mThumbPosition = checked ? getThumbScrollRange() : 0;
    519         invalidate();
    520     }
    521 
    522     @Override
    523     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    524         super.onLayout(changed, left, top, right, bottom);
    525 
    526         mThumbPosition = isChecked() ? getThumbScrollRange() : 0;
    527 
    528         int switchRight = getWidth() - getPaddingRight();
    529         int switchLeft = switchRight - mSwitchWidth;
    530         int switchTop = 0;
    531         int switchBottom = 0;
    532         switch (getGravity() & Gravity.VERTICAL_GRAVITY_MASK) {
    533             default:
    534             case Gravity.TOP:
    535                 switchTop = getPaddingTop();
    536                 switchBottom = switchTop + mSwitchHeight;
    537                 break;
    538 
    539             case Gravity.CENTER_VERTICAL:
    540                 switchTop = (getPaddingTop() + getHeight() - getPaddingBottom()) / 2 -
    541                         mSwitchHeight / 2;
    542                 switchBottom = switchTop + mSwitchHeight;
    543                 break;
    544 
    545             case Gravity.BOTTOM:
    546                 switchBottom = getHeight() - getPaddingBottom();
    547                 switchTop = switchBottom - mSwitchHeight;
    548                 break;
    549         }
    550 
    551         mSwitchLeft = switchLeft;
    552         mSwitchTop = switchTop;
    553         mSwitchBottom = switchBottom;
    554         mSwitchRight = switchRight;
    555     }
    556 
    557     @Override
    558     protected void onDraw(Canvas canvas) {
    559         super.onDraw(canvas);
    560 
    561         // Draw the switch
    562         int switchLeft = mSwitchLeft;
    563         int switchTop = mSwitchTop;
    564         int switchRight = mSwitchRight;
    565         int switchBottom = mSwitchBottom;
    566 
    567         mTrackDrawable.setBounds(switchLeft, switchTop, switchRight, switchBottom);
    568         mTrackDrawable.draw(canvas);
    569 
    570         canvas.save();
    571 
    572         mTrackDrawable.getPadding(mTempRect);
    573         int switchInnerLeft = switchLeft + mTempRect.left;
    574         int switchInnerTop = switchTop + mTempRect.top;
    575         int switchInnerRight = switchRight - mTempRect.right;
    576         int switchInnerBottom = switchBottom - mTempRect.bottom;
    577         canvas.clipRect(switchInnerLeft, switchTop, switchInnerRight, switchBottom);
    578 
    579         mThumbDrawable.getPadding(mTempRect);
    580         final int thumbPos = (int) (mThumbPosition + 0.5f);
    581         int thumbLeft = switchInnerLeft - mTempRect.left + thumbPos;
    582         int thumbRight = switchInnerLeft + thumbPos + mThumbWidth + mTempRect.right;
    583 
    584         mThumbDrawable.setBounds(thumbLeft, switchTop, thumbRight, switchBottom);
    585         mThumbDrawable.draw(canvas);
    586 
    587         // mTextColors should not be null, but just in case
    588         if (mTextColors != null) {
    589             mTextPaint.setColor(mTextColors.getColorForState(getDrawableState(),
    590                     mTextColors.getDefaultColor()));
    591         }
    592         mTextPaint.drawableState = getDrawableState();
    593 
    594         Layout switchText = getTargetCheckedState() ? mOnLayout : mOffLayout;
    595 
    596         canvas.translate((thumbLeft + thumbRight) / 2 - switchText.getWidth() / 2,
    597                 (switchInnerTop + switchInnerBottom) / 2 - switchText.getHeight() / 2);
    598         switchText.draw(canvas);
    599 
    600         canvas.restore();
    601     }
    602 
    603     @Override
    604     public int getCompoundPaddingRight() {
    605         int padding = super.getCompoundPaddingRight() + mSwitchWidth;
    606         if (!TextUtils.isEmpty(getText())) {
    607             padding += mSwitchPadding;
    608         }
    609         return padding;
    610     }
    611 
    612     private int getThumbScrollRange() {
    613         if (mTrackDrawable == null) {
    614             return 0;
    615         }
    616         mTrackDrawable.getPadding(mTempRect);
    617         return mSwitchWidth - mThumbWidth - mTempRect.left - mTempRect.right;
    618     }
    619 
    620     @Override
    621     protected int[] onCreateDrawableState(int extraSpace) {
    622         final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
    623         if (isChecked()) {
    624             mergeDrawableStates(drawableState, CHECKED_STATE_SET);
    625         }
    626         return drawableState;
    627     }
    628 
    629     @Override
    630     protected void drawableStateChanged() {
    631         super.drawableStateChanged();
    632 
    633         int[] myDrawableState = getDrawableState();
    634 
    635         // Set the state of the Drawable
    636         // Drawable may be null when checked state is set from XML, from super constructor
    637         if (mThumbDrawable != null) mThumbDrawable.setState(myDrawableState);
    638         if (mTrackDrawable != null) mTrackDrawable.setState(myDrawableState);
    639 
    640         invalidate();
    641     }
    642 
    643     @Override
    644     protected boolean verifyDrawable(Drawable who) {
    645         return super.verifyDrawable(who) || who == mThumbDrawable || who == mTrackDrawable;
    646     }
    647 
    648     @Override
    649     public void jumpDrawablesToCurrentState() {
    650         super.jumpDrawablesToCurrentState();
    651         mThumbDrawable.jumpToCurrentState();
    652         mTrackDrawable.jumpToCurrentState();
    653     }
    654 }
    655