• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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      */
Switch(Context context)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      */
Switch(Context context, AttributeSet attrs)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      */
Switch(Context context, AttributeSet attrs, int defStyle)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      */
setSwitchTextAppearance(Context context, int resid)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 
setSwitchTypefaceByIndex(int typefaceIndex, int styleIndex)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      */
setSwitchTypeface(Typeface tf, int style)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      */
setSwitchTypeface(Typeface tf)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      */
getTextOn()278     public CharSequence getTextOn() {
279         return mTextOn;
280     }
281 
282     /**
283      * Sets the text displayed when the button is in the checked state.
284      */
setTextOn(CharSequence textOn)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      */
getTextOff()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      */
setTextOff(CharSequence textOff)300     public void setTextOff(CharSequence textOff) {
301         mTextOff = textOff;
302         requestLayout();
303     }
304 
305     @Override
onMeasure(int widthMeasureSpec, int heightMeasureSpec)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
onPopulateAccessibilityEvent(AccessibilityEvent event)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 
makeLayout(CharSequence text)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      */
hitThumb(float x, float y)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
onTouchEvent(MotionEvent ev)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 
cancelSuperTouch(MotionEvent ev)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      */
stopDrag(MotionEvent ev)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 
animateThumbToCheckedState(boolean newCheckedState)504     private void animateThumbToCheckedState(boolean newCheckedState) {
505         // TODO animate!
506         //float targetPos = newCheckedState ? 0 : getThumbScrollRange();
507         //mThumbPosition = targetPos;
508         setChecked(newCheckedState);
509     }
510 
getTargetCheckedState()511     private boolean getTargetCheckedState() {
512         return mThumbPosition >= getThumbScrollRange() / 2;
513     }
514 
515     @Override
setChecked(boolean checked)516     public void setChecked(boolean checked) {
517         super.setChecked(checked);
518         mThumbPosition = checked ? getThumbScrollRange() : 0;
519         invalidate();
520     }
521 
522     @Override
onLayout(boolean changed, int left, int top, int right, int bottom)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
onDraw(Canvas canvas)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
getCompoundPaddingRight()604     public int getCompoundPaddingRight() {
605         int padding = super.getCompoundPaddingRight() + mSwitchWidth;
606         if (!TextUtils.isEmpty(getText())) {
607             padding += mSwitchPadding;
608         }
609         return padding;
610     }
611 
getThumbScrollRange()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
onCreateDrawableState(int extraSpace)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
drawableStateChanged()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
verifyDrawable(Drawable who)644     protected boolean verifyDrawable(Drawable who) {
645         return super.verifyDrawable(who) || who == mThumbDrawable || who == mTrackDrawable;
646     }
647 
648     @Override
jumpDrawablesToCurrentState()649     public void jumpDrawablesToCurrentState() {
650         super.jumpDrawablesToCurrentState();
651         mThumbDrawable.jumpToCurrentState();
652         mTrackDrawable.jumpToCurrentState();
653     }
654 }
655