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