blob: 02c9d0353265bdf20a705a0fec00b6141fc7f388 [file] [log] [blame]
Adam Powell12190b32010-11-28 19:07:53 -08001/*
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
17package android.widget;
18
Adam Powell12190b32010-11-28 19:07:53 -080019import android.content.Context;
20import android.content.res.ColorStateList;
21import android.content.res.Resources;
22import android.content.res.TypedArray;
23import android.graphics.Canvas;
24import android.graphics.Paint;
25import android.graphics.Rect;
26import android.graphics.Typeface;
27import android.graphics.drawable.Drawable;
28import android.text.Layout;
29import android.text.StaticLayout;
30import android.text.TextPaint;
31import android.text.TextUtils;
32import android.util.AttributeSet;
33import android.view.Gravity;
34import android.view.MotionEvent;
35import android.view.VelocityTracker;
36import android.view.ViewConfiguration;
Svetoslav Ganov63bce032011-07-23 19:52:17 -070037import android.view.accessibility.AccessibilityEvent;
Adam Powell12190b32010-11-28 19:07:53 -080038
Adam Powellbe0a4532010-11-29 17:47:48 -080039import com.android.internal.R;
40
Adam Powell12190b32010-11-28 19:07:53 -080041/**
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,
Chet Haase150176d2011-08-26 09:54:06 -070044 * 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.
Adam Powell12190b32010-11-28 19:07:53 -080052 *
Adam Powell12190b32010-11-28 19:07:53 -080053 */
54public 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
Adam Powellbe0a4532010-11-29 17:47:48 -080094 @SuppressWarnings("hiding")
Adam Powell12190b32010-11-28 19:07:53 -080095 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
Chet Haase150176d2011-08-26 09:54:06 -0700141 mThumbDrawable = a.getDrawable(com.android.internal.R.styleable.Switch_thumb);
142 mTrackDrawable = a.getDrawable(com.android.internal.R.styleable.Switch_track);
Adam Powell12190b32010-11-28 19:07:53 -0800143 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) {
Chet Haase150176d2011-08-26 09:54:06 -0700155 setSwitchTextAppearance(context, appearance);
Adam Powell12190b32010-11-28 19:07:53 -0800156 }
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
Gilles Debunnee724ee42011-08-31 11:20:27 -0700164 refreshDrawableState();
Adam Powell12190b32010-11-28 19:07:53 -0800165 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 */
Chet Haase150176d2011-08-26 09:54:06 -0700172 public void setSwitchTextAppearance(Context context, int resid) {
Adam Powell12190b32010-11-28 19:07:53 -0800173 TypedArray appearance =
Chet Haase150176d2011-08-26 09:54:06 -0700174 context.obtainStyledAttributes(resid,
Adam Powell12190b32010-11-28 19:07:53 -0800175 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;
Chet Haase150176d2011-08-26 09:54:06 -0700184 } else {
185 // If no color set in TextAppearance, default to the view's textColor
186 mTextColors = getTextColors();
Adam Powell12190b32010-11-28 19:07:53 -0800187 }
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
Adam Powell12190b32010-11-28 19:07:53 -0800207 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 /**
Chet Haase150176d2011-08-26 09:54:06 -0700257 * Sets the typeface in which the text should be displayed on the switch.
Adam Powell12190b32010-11-28 19:07:53 -0800258 * 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 /**
Chet Haase150176d2011-08-26 09:54:06 -0700276 * Returns the text displayed when the button is in the checked state.
Adam Powell12190b32010-11-28 19:07:53 -0800277 */
278 public CharSequence getTextOn() {
279 return mTextOn;
280 }
281
282 /**
Chet Haase150176d2011-08-26 09:54:06 -0700283 * Sets the text displayed when the button is in the checked state.
Adam Powell12190b32010-11-28 19:07:53 -0800284 */
285 public void setTextOn(CharSequence textOn) {
286 mTextOn = textOn;
287 requestLayout();
288 }
289
290 /**
Chet Haase150176d2011-08-26 09:54:06 -0700291 * Returns the text displayed when the button is not in the checked state.
Adam Powell12190b32010-11-28 19:07:53 -0800292 */
293 public CharSequence getTextOff() {
294 return mTextOff;
295 }
296
297 /**
Chet Haase150176d2011-08-26 09:54:06 -0700298 * Sets the text displayed when the button is not in the checked state.
Adam Powell12190b32010-11-28 19:07:53 -0800299 */
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);
Adam Powell12190b32010-11-28 19:07:53 -0800360 final int measuredHeight = getMeasuredHeight();
361 if (measuredHeight < switchHeight) {
Dianne Hackborn189ee182010-12-02 21:48:53 -0800362 setMeasuredDimension(getMeasuredWidthAndState(), switchHeight);
Adam Powell12190b32010-11-28 19:07:53 -0800363 }
364 }
365
Svetoslav Ganov63bce032011-07-23 19:52:17 -0700366 @Override
367 public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
368 super.onPopulateAccessibilityEvent(event);
Svetoslav Ganov76502592011-07-29 10:44:59 -0700369 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 }
Svetoslav Ganov63bce032011-07-23 19:52:17 -0700382 }
383
Adam Powell12190b32010-11-28 19:07:53 -0800384 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();
Gilles Debunnec2ab0d62011-06-13 12:52:48 -0700411 if (isEnabled() && hitThumb(x, y)) {
Adam Powell12190b32010-11-28 19:07:53 -0800412 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;
Gilles Debunnec2ab0d62011-06-13 12:52:48 -0700484 // Up and not canceled, also checks the switch has not been disabled during the drag
485 boolean commitChange = ev.getAction() == MotionEvent.ACTION_UP && isEnabled();
Adam Powell12190b32010-11-28 19:07:53 -0800486
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) {
Adam Powell01d11ed2011-08-09 16:32:12 -0700494 newState = xvel > 0;
Adam Powell12190b32010-11-28 19:07:53 -0800495 } else {
496 newState = getTargetCheckedState();
497 }
498 animateThumbToCheckedState(newState);
499 } else {
500 animateThumbToCheckedState(isChecked());
501 }
502 }
503
504 private void animateThumbToCheckedState(boolean newCheckedState) {
Adam Powell12190b32010-11-28 19:07:53 -0800505 // TODO animate!
Joe Onoratoc3eabb92011-01-07 15:58:44 -0800506 //float targetPos = newCheckedState ? 0 : getThumbScrollRange();
507 //mThumbPosition = targetPos;
Adam Powell12190b32010-11-28 19:07:53 -0800508 setChecked(newCheckedState);
509 }
510
511 private boolean getTargetCheckedState() {
Adam Powellec1d6032011-08-07 18:03:39 -0700512 return mThumbPosition >= getThumbScrollRange() / 2;
Adam Powell12190b32010-11-28 19:07:53 -0800513 }
514
515 @Override
516 public void setChecked(boolean checked) {
517 super.setChecked(checked);
Adam Powellec1d6032011-08-07 18:03:39 -0700518 mThumbPosition = checked ? getThumbScrollRange() : 0;
Joe Onoratoc3eabb92011-01-07 15:58:44 -0800519 invalidate();
Adam Powell12190b32010-11-28 19:07:53 -0800520 }
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
Adam Powellec1d6032011-08-07 18:03:39 -0700526 mThumbPosition = isChecked() ? getThumbScrollRange() : 0;
Joe Onoratoc3eabb92011-01-07 15:58:44 -0800527
Adam Powell12190b32010-11-28 19:07:53 -0800528 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
Chet Haase150176d2011-08-26 09:54:06 -0700587 // mTextColors should not be null, but just in case
588 if (mTextColors != null) {
589 mTextPaint.setColor(mTextColors.getColorForState(getDrawableState(),
590 mTextColors.getDefaultColor()));
591 }
Adam Powell12190b32010-11-28 19:07:53 -0800592 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
Gilles Debunnee724ee42011-08-31 11:20:27 -0700636 // 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);
Adam Powell12190b32010-11-28 19:07:53 -0800639
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}