blob: 8ea1090e792b6c05f2f4e0c91fef69c6a34d90ac [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
Alan Viverettecc2688d2013-09-17 17:00:12 -070019import android.animation.ObjectAnimator;
Adam Powell12190b32010-11-28 19:07:53 -080020import android.content.Context;
21import android.content.res.ColorStateList;
22import android.content.res.Resources;
23import android.content.res.TypedArray;
24import android.graphics.Canvas;
Alan Viverette661e6362014-05-12 10:55:37 -070025import android.graphics.Insets;
Adam Powell12190b32010-11-28 19:07:53 -080026import android.graphics.Paint;
27import android.graphics.Rect;
28import android.graphics.Typeface;
Alan Viverette661e6362014-05-12 10:55:37 -070029import android.graphics.Region.Op;
Adam Powell12190b32010-11-28 19:07:53 -080030import android.graphics.drawable.Drawable;
31import android.text.Layout;
32import android.text.StaticLayout;
33import android.text.TextPaint;
34import android.text.TextUtils;
Daniel Sandler4c3308d2012-04-19 11:04:39 -040035import android.text.method.AllCapsTransformationMethod;
36import android.text.method.TransformationMethod2;
Adam Powell12190b32010-11-28 19:07:53 -080037import android.util.AttributeSet;
Alan Viverettecc2688d2013-09-17 17:00:12 -070038import android.util.FloatProperty;
39import android.util.MathUtils;
Adam Powell12190b32010-11-28 19:07:53 -080040import android.view.Gravity;
41import android.view.MotionEvent;
42import android.view.VelocityTracker;
43import android.view.ViewConfiguration;
Svetoslav Ganov63bce032011-07-23 19:52:17 -070044import android.view.accessibility.AccessibilityEvent;
Svetoslav Ganov8a78fd42012-01-17 14:36:46 -080045import android.view.accessibility.AccessibilityNodeInfo;
Adam Powell12190b32010-11-28 19:07:53 -080046
Adam Powellbe0a4532010-11-29 17:47:48 -080047import com.android.internal.R;
48
Adam Powell12190b32010-11-28 19:07:53 -080049/**
50 * A Switch is a two-state toggle switch widget that can select between two
51 * options. The user may drag the "thumb" back and forth to choose the selected option,
Chet Haase150176d2011-08-26 09:54:06 -070052 * or simply tap to toggle as if it were a checkbox. The {@link #setText(CharSequence) text}
53 * property controls the text displayed in the label for the switch, whereas the
54 * {@link #setTextOff(CharSequence) off} and {@link #setTextOn(CharSequence) on} text
55 * controls the text on the thumb. Similarly, the
56 * {@link #setTextAppearance(android.content.Context, int) textAppearance} and the related
57 * setTypeface() methods control the typeface and style of label text, whereas the
58 * {@link #setSwitchTextAppearance(android.content.Context, int) switchTextAppearance} and
59 * the related seSwitchTypeface() methods control that of the thumb.
Adam Powell12190b32010-11-28 19:07:53 -080060 *
Scott Main4c359b72012-07-24 15:51:27 -070061 * <p>See the <a href="{@docRoot}guide/topics/ui/controls/togglebutton.html">Toggle Buttons</a>
62 * guide.</p>
63 *
64 * @attr ref android.R.styleable#Switch_textOn
65 * @attr ref android.R.styleable#Switch_textOff
66 * @attr ref android.R.styleable#Switch_switchMinWidth
67 * @attr ref android.R.styleable#Switch_switchPadding
68 * @attr ref android.R.styleable#Switch_switchTextAppearance
69 * @attr ref android.R.styleable#Switch_thumb
70 * @attr ref android.R.styleable#Switch_thumbTextPadding
71 * @attr ref android.R.styleable#Switch_track
Adam Powell12190b32010-11-28 19:07:53 -080072 */
73public class Switch extends CompoundButton {
Alan Viverettecc2688d2013-09-17 17:00:12 -070074 private static final int THUMB_ANIMATION_DURATION = 250;
75
Adam Powell12190b32010-11-28 19:07:53 -080076 private static final int TOUCH_MODE_IDLE = 0;
77 private static final int TOUCH_MODE_DOWN = 1;
78 private static final int TOUCH_MODE_DRAGGING = 2;
79
80 // Enum for the "typeface" XML parameter.
81 private static final int SANS = 1;
82 private static final int SERIF = 2;
83 private static final int MONOSPACE = 3;
84
85 private Drawable mThumbDrawable;
86 private Drawable mTrackDrawable;
87 private int mThumbTextPadding;
88 private int mSwitchMinWidth;
89 private int mSwitchPadding;
Alan Viverette661e6362014-05-12 10:55:37 -070090 private boolean mSplitTrack;
Adam Powell12190b32010-11-28 19:07:53 -080091 private CharSequence mTextOn;
92 private CharSequence mTextOff;
Alan Viverette2a37cf8d2014-06-19 17:09:10 -070093 private boolean mShowText;
Adam Powell12190b32010-11-28 19:07:53 -080094
95 private int mTouchMode;
96 private int mTouchSlop;
97 private float mTouchX;
98 private float mTouchY;
99 private VelocityTracker mVelocityTracker = VelocityTracker.obtain();
100 private int mMinFlingVelocity;
101
102 private float mThumbPosition;
Alan Viverette8bb39902014-07-29 17:22:30 -0700103 private int mSwitchWidth;
Alan Viverette8bb39902014-07-29 17:22:30 -0700104 private int mSwitchHeight;
Alan Viverette9b38f6c2014-07-30 02:39:07 +0000105 private int mThumbWidth; // Does not include padding
Alan Viverette8bb39902014-07-29 17:22:30 -0700106
Adam Powell12190b32010-11-28 19:07:53 -0800107 private int mSwitchLeft;
108 private int mSwitchTop;
109 private int mSwitchRight;
110 private int mSwitchBottom;
111
112 private TextPaint mTextPaint;
113 private ColorStateList mTextColors;
114 private Layout mOnLayout;
115 private Layout mOffLayout;
Daniel Sandler4c3308d2012-04-19 11:04:39 -0400116 private TransformationMethod2 mSwitchTransformationMethod;
Alan Viverettecc2688d2013-09-17 17:00:12 -0700117 private ObjectAnimator mPositionAnimator;
Adam Powell12190b32010-11-28 19:07:53 -0800118
Adam Powellbe0a4532010-11-29 17:47:48 -0800119 @SuppressWarnings("hiding")
Adam Powell12190b32010-11-28 19:07:53 -0800120 private final Rect mTempRect = new Rect();
121
122 private static final int[] CHECKED_STATE_SET = {
123 R.attr.state_checked
124 };
125
126 /**
127 * Construct a new Switch with default styling.
128 *
129 * @param context The Context that will determine this widget's theming.
130 */
131 public Switch(Context context) {
132 this(context, null);
133 }
134
135 /**
136 * Construct a new Switch with default styling, overriding specific style
137 * attributes as requested.
138 *
139 * @param context The Context that will determine this widget's theming.
140 * @param attrs Specification of attributes that should deviate from default styling.
141 */
142 public Switch(Context context, AttributeSet attrs) {
143 this(context, attrs, com.android.internal.R.attr.switchStyle);
144 }
145
146 /**
147 * Construct a new Switch with a default style determined by the given theme attribute,
148 * overriding specific style attributes as requested.
149 *
150 * @param context The Context that will determine this widget's theming.
151 * @param attrs Specification of attributes that should deviate from the default styling.
Alan Viverette617feb92013-09-09 18:09:13 -0700152 * @param defStyleAttr An attribute in the current theme that contains a
153 * reference to a style resource that supplies default values for
154 * the view. Can be 0 to not look for defaults.
Adam Powell12190b32010-11-28 19:07:53 -0800155 */
Alan Viverette617feb92013-09-09 18:09:13 -0700156 public Switch(Context context, AttributeSet attrs, int defStyleAttr) {
157 this(context, attrs, defStyleAttr, 0);
158 }
159
160
161 /**
162 * Construct a new Switch with a default style determined by the given theme
163 * attribute or style resource, overriding specific style attributes as
164 * requested.
165 *
166 * @param context The Context that will determine this widget's theming.
167 * @param attrs Specification of attributes that should deviate from the
168 * default styling.
169 * @param defStyleAttr An attribute in the current theme that contains a
170 * reference to a style resource that supplies default values for
171 * the view. Can be 0 to not look for defaults.
172 * @param defStyleRes A resource identifier of a style resource that
173 * supplies default values for the view, used only if
174 * defStyleAttr is 0 or can not be found in the theme. Can be 0
175 * to not look for defaults.
176 */
177 public Switch(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
178 super(context, attrs, defStyleAttr, defStyleRes);
Adam Powell12190b32010-11-28 19:07:53 -0800179
180 mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
Alan Viverette661e6362014-05-12 10:55:37 -0700181
182 final Resources res = getResources();
Adam Powell12190b32010-11-28 19:07:53 -0800183 mTextPaint.density = res.getDisplayMetrics().density;
184 mTextPaint.setCompatibilityScaling(res.getCompatibilityInfo().applicationScale);
185
Alan Viverette617feb92013-09-09 18:09:13 -0700186 final TypedArray a = context.obtainStyledAttributes(
187 attrs, com.android.internal.R.styleable.Switch, defStyleAttr, defStyleRes);
Chet Haase150176d2011-08-26 09:54:06 -0700188 mThumbDrawable = a.getDrawable(com.android.internal.R.styleable.Switch_thumb);
189 mTrackDrawable = a.getDrawable(com.android.internal.R.styleable.Switch_track);
Adam Powell12190b32010-11-28 19:07:53 -0800190 mTextOn = a.getText(com.android.internal.R.styleable.Switch_textOn);
191 mTextOff = a.getText(com.android.internal.R.styleable.Switch_textOff);
Alan Viverette2a37cf8d2014-06-19 17:09:10 -0700192 mShowText = a.getBoolean(com.android.internal.R.styleable.Switch_showText, true);
Adam Powell12190b32010-11-28 19:07:53 -0800193 mThumbTextPadding = a.getDimensionPixelSize(
194 com.android.internal.R.styleable.Switch_thumbTextPadding, 0);
195 mSwitchMinWidth = a.getDimensionPixelSize(
196 com.android.internal.R.styleable.Switch_switchMinWidth, 0);
197 mSwitchPadding = a.getDimensionPixelSize(
198 com.android.internal.R.styleable.Switch_switchPadding, 0);
Alan Viverette661e6362014-05-12 10:55:37 -0700199 mSplitTrack = a.getBoolean(com.android.internal.R.styleable.Switch_splitTrack, false);
Adam Powell12190b32010-11-28 19:07:53 -0800200
Alan Viverette661e6362014-05-12 10:55:37 -0700201 final int appearance = a.getResourceId(
Adam Powell12190b32010-11-28 19:07:53 -0800202 com.android.internal.R.styleable.Switch_switchTextAppearance, 0);
203 if (appearance != 0) {
Chet Haase150176d2011-08-26 09:54:06 -0700204 setSwitchTextAppearance(context, appearance);
Adam Powell12190b32010-11-28 19:07:53 -0800205 }
206 a.recycle();
207
Alan Viverette661e6362014-05-12 10:55:37 -0700208 final ViewConfiguration config = ViewConfiguration.get(context);
Adam Powell12190b32010-11-28 19:07:53 -0800209 mTouchSlop = config.getScaledTouchSlop();
210 mMinFlingVelocity = config.getScaledMinimumFlingVelocity();
211
212 // Refresh display with current params
Gilles Debunnee724ee42011-08-31 11:20:27 -0700213 refreshDrawableState();
Adam Powell12190b32010-11-28 19:07:53 -0800214 setChecked(isChecked());
215 }
216
217 /**
218 * Sets the switch text color, size, style, hint color, and highlight color
219 * from the specified TextAppearance resource.
Adam Powell6c86e1b2012-03-08 15:11:46 -0800220 *
221 * @attr ref android.R.styleable#Switch_switchTextAppearance
Adam Powell12190b32010-11-28 19:07:53 -0800222 */
Chet Haase150176d2011-08-26 09:54:06 -0700223 public void setSwitchTextAppearance(Context context, int resid) {
Adam Powell12190b32010-11-28 19:07:53 -0800224 TypedArray appearance =
Chet Haase150176d2011-08-26 09:54:06 -0700225 context.obtainStyledAttributes(resid,
Adam Powell12190b32010-11-28 19:07:53 -0800226 com.android.internal.R.styleable.TextAppearance);
227
228 ColorStateList colors;
229 int ts;
230
231 colors = appearance.getColorStateList(com.android.internal.R.styleable.
232 TextAppearance_textColor);
233 if (colors != null) {
234 mTextColors = colors;
Chet Haase150176d2011-08-26 09:54:06 -0700235 } else {
236 // If no color set in TextAppearance, default to the view's textColor
237 mTextColors = getTextColors();
Adam Powell12190b32010-11-28 19:07:53 -0800238 }
239
240 ts = appearance.getDimensionPixelSize(com.android.internal.R.styleable.
241 TextAppearance_textSize, 0);
242 if (ts != 0) {
243 if (ts != mTextPaint.getTextSize()) {
244 mTextPaint.setTextSize(ts);
245 requestLayout();
246 }
247 }
248
249 int typefaceIndex, styleIndex;
250
251 typefaceIndex = appearance.getInt(com.android.internal.R.styleable.
252 TextAppearance_typeface, -1);
253 styleIndex = appearance.getInt(com.android.internal.R.styleable.
254 TextAppearance_textStyle, -1);
255
256 setSwitchTypefaceByIndex(typefaceIndex, styleIndex);
257
Daniel Sandler4c3308d2012-04-19 11:04:39 -0400258 boolean allCaps = appearance.getBoolean(com.android.internal.R.styleable.
259 TextAppearance_textAllCaps, false);
260 if (allCaps) {
261 mSwitchTransformationMethod = new AllCapsTransformationMethod(getContext());
262 mSwitchTransformationMethod.setLengthChangesAllowed(true);
263 } else {
264 mSwitchTransformationMethod = null;
265 }
266
Adam Powell12190b32010-11-28 19:07:53 -0800267 appearance.recycle();
268 }
269
270 private void setSwitchTypefaceByIndex(int typefaceIndex, int styleIndex) {
271 Typeface tf = null;
272 switch (typefaceIndex) {
273 case SANS:
274 tf = Typeface.SANS_SERIF;
275 break;
276
277 case SERIF:
278 tf = Typeface.SERIF;
279 break;
280
281 case MONOSPACE:
282 tf = Typeface.MONOSPACE;
283 break;
284 }
285
286 setSwitchTypeface(tf, styleIndex);
287 }
288
289 /**
290 * Sets the typeface and style in which the text should be displayed on the
291 * switch, and turns on the fake bold and italic bits in the Paint if the
292 * Typeface that you provided does not have all the bits in the
293 * style that you specified.
294 */
295 public void setSwitchTypeface(Typeface tf, int style) {
296 if (style > 0) {
297 if (tf == null) {
298 tf = Typeface.defaultFromStyle(style);
299 } else {
300 tf = Typeface.create(tf, style);
301 }
302
303 setSwitchTypeface(tf);
304 // now compute what (if any) algorithmic styling is needed
305 int typefaceStyle = tf != null ? tf.getStyle() : 0;
306 int need = style & ~typefaceStyle;
307 mTextPaint.setFakeBoldText((need & Typeface.BOLD) != 0);
308 mTextPaint.setTextSkewX((need & Typeface.ITALIC) != 0 ? -0.25f : 0);
309 } else {
Victoria Leaseaa0980a2012-06-11 14:46:04 -0700310 mTextPaint.setFakeBoldText(false);
Adam Powell12190b32010-11-28 19:07:53 -0800311 mTextPaint.setTextSkewX(0);
312 setSwitchTypeface(tf);
313 }
314 }
315
316 /**
Chet Haase150176d2011-08-26 09:54:06 -0700317 * Sets the typeface in which the text should be displayed on the switch.
Adam Powell12190b32010-11-28 19:07:53 -0800318 * Note that not all Typeface families actually have bold and italic
319 * variants, so you may need to use
320 * {@link #setSwitchTypeface(Typeface, int)} to get the appearance
321 * that you actually want.
322 *
323 * @attr ref android.R.styleable#TextView_typeface
324 * @attr ref android.R.styleable#TextView_textStyle
325 */
326 public void setSwitchTypeface(Typeface tf) {
327 if (mTextPaint.getTypeface() != tf) {
328 mTextPaint.setTypeface(tf);
329
330 requestLayout();
331 invalidate();
332 }
333 }
334
335 /**
Adam Powell6c86e1b2012-03-08 15:11:46 -0800336 * Set the amount of horizontal padding between the switch and the associated text.
337 *
338 * @param pixels Amount of padding in pixels
339 *
340 * @attr ref android.R.styleable#Switch_switchPadding
341 */
342 public void setSwitchPadding(int pixels) {
343 mSwitchPadding = pixels;
344 requestLayout();
345 }
346
347 /**
348 * Get the amount of horizontal padding between the switch and the associated text.
349 *
350 * @return Amount of padding in pixels
351 *
352 * @attr ref android.R.styleable#Switch_switchPadding
353 */
354 public int getSwitchPadding() {
355 return mSwitchPadding;
356 }
357
358 /**
359 * Set the minimum width of the switch in pixels. The switch's width will be the maximum
360 * of this value and its measured width as determined by the switch drawables and text used.
361 *
362 * @param pixels Minimum width of the switch in pixels
363 *
364 * @attr ref android.R.styleable#Switch_switchMinWidth
365 */
366 public void setSwitchMinWidth(int pixels) {
367 mSwitchMinWidth = pixels;
368 requestLayout();
369 }
370
371 /**
372 * Get the minimum width of the switch in pixels. The switch's width will be the maximum
373 * of this value and its measured width as determined by the switch drawables and text used.
374 *
375 * @return Minimum width of the switch in pixels
376 *
377 * @attr ref android.R.styleable#Switch_switchMinWidth
378 */
379 public int getSwitchMinWidth() {
380 return mSwitchMinWidth;
381 }
382
383 /**
384 * Set the horizontal padding around the text drawn on the switch itself.
385 *
386 * @param pixels Horizontal padding for switch thumb text in pixels
387 *
388 * @attr ref android.R.styleable#Switch_thumbTextPadding
389 */
390 public void setThumbTextPadding(int pixels) {
391 mThumbTextPadding = pixels;
392 requestLayout();
393 }
394
395 /**
396 * Get the horizontal padding around the text drawn on the switch itself.
397 *
398 * @return Horizontal padding for switch thumb text in pixels
399 *
400 * @attr ref android.R.styleable#Switch_thumbTextPadding
401 */
402 public int getThumbTextPadding() {
403 return mThumbTextPadding;
404 }
405
406 /**
407 * Set the drawable used for the track that the switch slides within.
408 *
409 * @param track Track drawable
410 *
411 * @attr ref android.R.styleable#Switch_track
412 */
413 public void setTrackDrawable(Drawable track) {
414 mTrackDrawable = track;
415 requestLayout();
416 }
417
418 /**
Adam Powelld9c7be62012-03-08 19:43:43 -0800419 * Set the drawable used for the track that the switch slides within.
420 *
Adam Powelldca510e2012-03-08 20:06:39 -0800421 * @param resId Resource ID of a track drawable
Adam Powelld9c7be62012-03-08 19:43:43 -0800422 *
423 * @attr ref android.R.styleable#Switch_track
424 */
425 public void setTrackResource(int resId) {
Alan Viverette8eea3ea2014-02-03 18:40:20 -0800426 setTrackDrawable(getContext().getDrawable(resId));
Adam Powelld9c7be62012-03-08 19:43:43 -0800427 }
428
429 /**
Adam Powell6c86e1b2012-03-08 15:11:46 -0800430 * Get the drawable used for the track that the switch slides within.
431 *
432 * @return Track drawable
433 *
434 * @attr ref android.R.styleable#Switch_track
435 */
436 public Drawable getTrackDrawable() {
437 return mTrackDrawable;
438 }
439
440 /**
441 * Set the drawable used for the switch "thumb" - the piece that the user
442 * can physically touch and drag along the track.
443 *
444 * @param thumb Thumb drawable
445 *
446 * @attr ref android.R.styleable#Switch_thumb
447 */
448 public void setThumbDrawable(Drawable thumb) {
449 mThumbDrawable = thumb;
450 requestLayout();
451 }
452
453 /**
Adam Powelld9c7be62012-03-08 19:43:43 -0800454 * Set the drawable used for the switch "thumb" - the piece that the user
455 * can physically touch and drag along the track.
456 *
Adam Powelldca510e2012-03-08 20:06:39 -0800457 * @param resId Resource ID of a thumb drawable
Adam Powelld9c7be62012-03-08 19:43:43 -0800458 *
459 * @attr ref android.R.styleable#Switch_thumb
460 */
461 public void setThumbResource(int resId) {
Alan Viverette8eea3ea2014-02-03 18:40:20 -0800462 setThumbDrawable(getContext().getDrawable(resId));
Adam Powelld9c7be62012-03-08 19:43:43 -0800463 }
464
465 /**
Adam Powell6c86e1b2012-03-08 15:11:46 -0800466 * Get the drawable used for the switch "thumb" - the piece that the user
467 * can physically touch and drag along the track.
468 *
469 * @return Thumb drawable
470 *
471 * @attr ref android.R.styleable#Switch_thumb
472 */
473 public Drawable getThumbDrawable() {
474 return mThumbDrawable;
475 }
476
477 /**
Alan Viverette661e6362014-05-12 10:55:37 -0700478 * Specifies whether the track should be split by the thumb. When true,
479 * the thumb's optical bounds will be clipped out of the track drawable,
480 * then the thumb will be drawn into the resulting gap.
481 *
482 * @param splitTrack Whether the track should be split by the thumb
483 *
484 * @attr ref android.R.styleable#Switch_splitTrack
485 */
486 public void setSplitTrack(boolean splitTrack) {
487 mSplitTrack = splitTrack;
488 invalidate();
489 }
490
491 /**
492 * Returns whether the track should be split by the thumb.
493 *
494 * @attr ref android.R.styleable#Switch_splitTrack
495 */
496 public boolean getSplitTrack() {
497 return mSplitTrack;
498 }
499
500 /**
Chet Haase150176d2011-08-26 09:54:06 -0700501 * Returns the text displayed when the button is in the checked state.
Adam Powell6c86e1b2012-03-08 15:11:46 -0800502 *
503 * @attr ref android.R.styleable#Switch_textOn
Adam Powell12190b32010-11-28 19:07:53 -0800504 */
505 public CharSequence getTextOn() {
506 return mTextOn;
507 }
508
509 /**
Chet Haase150176d2011-08-26 09:54:06 -0700510 * Sets the text displayed when the button is in the checked state.
Adam Powell6c86e1b2012-03-08 15:11:46 -0800511 *
512 * @attr ref android.R.styleable#Switch_textOn
Adam Powell12190b32010-11-28 19:07:53 -0800513 */
514 public void setTextOn(CharSequence textOn) {
515 mTextOn = textOn;
516 requestLayout();
517 }
518
519 /**
Chet Haase150176d2011-08-26 09:54:06 -0700520 * Returns the text displayed when the button is not in the checked state.
Adam Powell6c86e1b2012-03-08 15:11:46 -0800521 *
522 * @attr ref android.R.styleable#Switch_textOff
Adam Powell12190b32010-11-28 19:07:53 -0800523 */
524 public CharSequence getTextOff() {
525 return mTextOff;
526 }
527
528 /**
Chet Haase150176d2011-08-26 09:54:06 -0700529 * Sets the text displayed when the button is not in the checked state.
Adam Powell6c86e1b2012-03-08 15:11:46 -0800530 *
531 * @attr ref android.R.styleable#Switch_textOff
Adam Powell12190b32010-11-28 19:07:53 -0800532 */
533 public void setTextOff(CharSequence textOff) {
534 mTextOff = textOff;
535 requestLayout();
536 }
537
Alan Viverette2a37cf8d2014-06-19 17:09:10 -0700538 /**
539 * Sets whether the on/off text should be displayed.
540 *
541 * @param showText {@code true} to display on/off text
Alan Viverette9b38f6c2014-07-30 02:39:07 +0000542 * @hide
Alan Viverette2a37cf8d2014-06-19 17:09:10 -0700543 */
544 public void setShowText(boolean showText) {
545 if (mShowText != showText) {
546 mShowText = showText;
547 requestLayout();
548 }
549 }
550
551 /**
552 * @return whether the on/off text should be displayed
Alan Viverette9b38f6c2014-07-30 02:39:07 +0000553 * @hide
Alan Viverette2a37cf8d2014-06-19 17:09:10 -0700554 */
555 public boolean getShowText() {
556 return mShowText;
557 }
558
Adam Powell12190b32010-11-28 19:07:53 -0800559 @Override
560 public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Alan Viverette2a37cf8d2014-06-19 17:09:10 -0700561 if (mShowText) {
562 if (mOnLayout == null) {
563 mOnLayout = makeLayout(mTextOn);
564 }
Alan Viverette5876ff42014-03-03 17:40:46 -0800565
Alan Viverette2a37cf8d2014-06-19 17:09:10 -0700566 if (mOffLayout == null) {
567 mOffLayout = makeLayout(mTextOff);
568 }
Adam Powell12190b32010-11-28 19:07:53 -0800569 }
570
Alan Viverette8bb39902014-07-29 17:22:30 -0700571 final int trackHeight;
Alan Viverette9b38f6c2014-07-30 02:39:07 +0000572 final Rect padding = mTempRect;
Alan Viverette4d065a02014-07-11 15:28:38 -0700573 if (mTrackDrawable != null) {
574 mTrackDrawable.getPadding(padding);
575 trackHeight = mTrackDrawable.getIntrinsicHeight();
576 } else {
577 padding.setEmpty();
578 trackHeight = 0;
579 }
580
Alan Viverette9b38f6c2014-07-30 02:39:07 +0000581 final int thumbWidth;
582 final int thumbHeight;
Alan Viverette4d065a02014-07-11 15:28:38 -0700583 if (mThumbDrawable != null) {
Alan Viverette9b38f6c2014-07-30 02:39:07 +0000584 thumbWidth = mThumbDrawable.getIntrinsicWidth();
585 thumbHeight = mThumbDrawable.getIntrinsicHeight();
586 } else {
587 thumbWidth = 0;
588 thumbHeight = 0;
Alan Viverette4d065a02014-07-11 15:28:38 -0700589 }
Alan Viverette5876ff42014-03-03 17:40:46 -0800590
Alan Viverette9b38f6c2014-07-30 02:39:07 +0000591 final int maxTextWidth = mShowText ? Math.max(mOnLayout.getWidth(), mOffLayout.getWidth())
592 + mThumbTextPadding * 2 : 0;
593 mThumbWidth = Math.max(maxTextWidth, thumbWidth);
594
Adam Powell12190b32010-11-28 19:07:53 -0800595 final int switchWidth = Math.max(mSwitchMinWidth,
Alan Viverette9b38f6c2014-07-30 02:39:07 +0000596 2 * mThumbWidth + padding.left + padding.right);
Alan Viverette4d065a02014-07-11 15:28:38 -0700597 final int switchHeight = Math.max(trackHeight, thumbHeight);
Adam Powell12190b32010-11-28 19:07:53 -0800598 mSwitchWidth = switchWidth;
599 mSwitchHeight = switchHeight;
600
601 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
Alan Viverette4d065a02014-07-11 15:28:38 -0700602
Adam Powell12190b32010-11-28 19:07:53 -0800603 final int measuredHeight = getMeasuredHeight();
604 if (measuredHeight < switchHeight) {
Dianne Hackborn189ee182010-12-02 21:48:53 -0800605 setMeasuredDimension(getMeasuredWidthAndState(), switchHeight);
Adam Powell12190b32010-11-28 19:07:53 -0800606 }
607 }
608
Svetoslav Ganov63bce032011-07-23 19:52:17 -0700609 @Override
610 public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
611 super.onPopulateAccessibilityEvent(event);
Alan Viverette2a37cf8d2014-06-19 17:09:10 -0700612
613 final CharSequence text = isChecked() ? mTextOn : mTextOff;
614 if (text != null) {
615 event.getText().add(text);
Svetoslav Ganov76502592011-07-29 10:44:59 -0700616 }
Svetoslav Ganov63bce032011-07-23 19:52:17 -0700617 }
618
Adam Powell12190b32010-11-28 19:07:53 -0800619 private Layout makeLayout(CharSequence text) {
Daniel Sandler4c3308d2012-04-19 11:04:39 -0400620 final CharSequence transformed = (mSwitchTransformationMethod != null)
621 ? mSwitchTransformationMethod.getTransformation(text, this)
622 : text;
623
624 return new StaticLayout(transformed, mTextPaint,
625 (int) Math.ceil(Layout.getDesiredWidth(transformed, mTextPaint)),
Adam Powell12190b32010-11-28 19:07:53 -0800626 Layout.Alignment.ALIGN_NORMAL, 1.f, 0, true);
627 }
628
629 /**
630 * @return true if (x, y) is within the target area of the switch thumb
631 */
632 private boolean hitThumb(float x, float y) {
Alan Viverettecc2688d2013-09-17 17:00:12 -0700633 // Relies on mTempRect, MUST be called first!
634 final int thumbOffset = getThumbOffset();
635
Adam Powell12190b32010-11-28 19:07:53 -0800636 mThumbDrawable.getPadding(mTempRect);
637 final int thumbTop = mSwitchTop - mTouchSlop;
Alan Viverettecc2688d2013-09-17 17:00:12 -0700638 final int thumbLeft = mSwitchLeft + thumbOffset - mTouchSlop;
Adam Powell12190b32010-11-28 19:07:53 -0800639 final int thumbRight = thumbLeft + mThumbWidth +
640 mTempRect.left + mTempRect.right + mTouchSlop;
641 final int thumbBottom = mSwitchBottom + mTouchSlop;
642 return x > thumbLeft && x < thumbRight && y > thumbTop && y < thumbBottom;
643 }
644
645 @Override
646 public boolean onTouchEvent(MotionEvent ev) {
647 mVelocityTracker.addMovement(ev);
648 final int action = ev.getActionMasked();
649 switch (action) {
650 case MotionEvent.ACTION_DOWN: {
651 final float x = ev.getX();
652 final float y = ev.getY();
Gilles Debunnec2ab0d62011-06-13 12:52:48 -0700653 if (isEnabled() && hitThumb(x, y)) {
Adam Powell12190b32010-11-28 19:07:53 -0800654 mTouchMode = TOUCH_MODE_DOWN;
655 mTouchX = x;
656 mTouchY = y;
657 }
658 break;
659 }
660
661 case MotionEvent.ACTION_MOVE: {
662 switch (mTouchMode) {
663 case TOUCH_MODE_IDLE:
664 // Didn't target the thumb, treat normally.
665 break;
666
667 case TOUCH_MODE_DOWN: {
668 final float x = ev.getX();
669 final float y = ev.getY();
670 if (Math.abs(x - mTouchX) > mTouchSlop ||
671 Math.abs(y - mTouchY) > mTouchSlop) {
672 mTouchMode = TOUCH_MODE_DRAGGING;
673 getParent().requestDisallowInterceptTouchEvent(true);
674 mTouchX = x;
675 mTouchY = y;
676 return true;
677 }
678 break;
679 }
680
681 case TOUCH_MODE_DRAGGING: {
682 final float x = ev.getX();
Alan Viverettecc2688d2013-09-17 17:00:12 -0700683 final int thumbScrollRange = getThumbScrollRange();
684 final float thumbScrollOffset = x - mTouchX;
685 float dPos;
686 if (thumbScrollRange != 0) {
687 dPos = thumbScrollOffset / thumbScrollRange;
688 } else {
689 // If the thumb scroll range is empty, just use the
690 // movement direction to snap on or off.
691 dPos = thumbScrollOffset > 0 ? 1 : -1;
692 }
693 if (isLayoutRtl()) {
694 dPos = -dPos;
695 }
696 final float newPos = MathUtils.constrain(mThumbPosition + dPos, 0, 1);
Adam Powell12190b32010-11-28 19:07:53 -0800697 if (newPos != mThumbPosition) {
Adam Powell12190b32010-11-28 19:07:53 -0800698 mTouchX = x;
Alan Viverettecc2688d2013-09-17 17:00:12 -0700699 setThumbPosition(newPos);
Adam Powell12190b32010-11-28 19:07:53 -0800700 }
701 return true;
702 }
703 }
704 break;
705 }
706
707 case MotionEvent.ACTION_UP:
708 case MotionEvent.ACTION_CANCEL: {
709 if (mTouchMode == TOUCH_MODE_DRAGGING) {
710 stopDrag(ev);
Alan Viverettead2f8e32014-05-16 13:28:33 -0700711 // Allow super class to handle pressed state, etc.
712 super.onTouchEvent(ev);
Adam Powell12190b32010-11-28 19:07:53 -0800713 return true;
714 }
715 mTouchMode = TOUCH_MODE_IDLE;
716 mVelocityTracker.clear();
717 break;
718 }
719 }
720
721 return super.onTouchEvent(ev);
722 }
723
724 private void cancelSuperTouch(MotionEvent ev) {
725 MotionEvent cancel = MotionEvent.obtain(ev);
726 cancel.setAction(MotionEvent.ACTION_CANCEL);
727 super.onTouchEvent(cancel);
728 cancel.recycle();
729 }
730
731 /**
732 * Called from onTouchEvent to end a drag operation.
733 *
734 * @param ev Event that triggered the end of drag mode - ACTION_UP or ACTION_CANCEL
735 */
736 private void stopDrag(MotionEvent ev) {
737 mTouchMode = TOUCH_MODE_IDLE;
Adam Powell12190b32010-11-28 19:07:53 -0800738
Alan Viverette86453ff2013-09-26 14:46:08 -0700739 // Commit the change if the event is up and not canceled and the switch
740 // has not been disabled during the drag.
741 final boolean commitChange = ev.getAction() == MotionEvent.ACTION_UP && isEnabled();
742 final boolean newState;
Adam Powell12190b32010-11-28 19:07:53 -0800743 if (commitChange) {
Adam Powell12190b32010-11-28 19:07:53 -0800744 mVelocityTracker.computeCurrentVelocity(1000);
Alan Viverette86453ff2013-09-26 14:46:08 -0700745 final float xvel = mVelocityTracker.getXVelocity();
Adam Powell12190b32010-11-28 19:07:53 -0800746 if (Math.abs(xvel) > mMinFlingVelocity) {
Fabrice Di Meglio28efba32012-06-01 16:52:31 -0700747 newState = isLayoutRtl() ? (xvel < 0) : (xvel > 0);
Adam Powell12190b32010-11-28 19:07:53 -0800748 } else {
749 newState = getTargetCheckedState();
750 }
Adam Powell12190b32010-11-28 19:07:53 -0800751 } else {
Alan Viverette86453ff2013-09-26 14:46:08 -0700752 newState = isChecked();
Adam Powell12190b32010-11-28 19:07:53 -0800753 }
Alan Viverette86453ff2013-09-26 14:46:08 -0700754
755 setChecked(newState);
756 cancelSuperTouch(ev);
Adam Powell12190b32010-11-28 19:07:53 -0800757 }
758
759 private void animateThumbToCheckedState(boolean newCheckedState) {
Alan Viverettecc2688d2013-09-17 17:00:12 -0700760 final float targetPosition = newCheckedState ? 1 : 0;
761 mPositionAnimator = ObjectAnimator.ofFloat(this, THUMB_POS, targetPosition);
762 mPositionAnimator.setDuration(THUMB_ANIMATION_DURATION);
763 mPositionAnimator.setAutoCancel(true);
764 mPositionAnimator.start();
765 }
766
767 private void cancelPositionAnimator() {
768 if (mPositionAnimator != null) {
769 mPositionAnimator.cancel();
770 }
Adam Powell12190b32010-11-28 19:07:53 -0800771 }
772
773 private boolean getTargetCheckedState() {
Alan Viverettecc2688d2013-09-17 17:00:12 -0700774 return mThumbPosition > 0.5f;
Fabrice Di Meglio28efba32012-06-01 16:52:31 -0700775 }
776
Alan Viverettecc2688d2013-09-17 17:00:12 -0700777 /**
778 * Sets the thumb position as a decimal value between 0 (off) and 1 (on).
779 *
780 * @param position new position between [0,1]
781 */
782 private void setThumbPosition(float position) {
783 mThumbPosition = position;
784 invalidate();
785 }
786
787 @Override
788 public void toggle() {
Alan Viverette86453ff2013-09-26 14:46:08 -0700789 setChecked(!isChecked());
Adam Powell12190b32010-11-28 19:07:53 -0800790 }
791
792 @Override
793 public void setChecked(boolean checked) {
794 super.setChecked(checked);
Alan Viverettecc2688d2013-09-17 17:00:12 -0700795
Alan Viverette86453ff2013-09-26 14:46:08 -0700796 if (isAttachedToWindow() && isLaidOut()) {
797 animateThumbToCheckedState(checked);
798 } else {
799 // Immediately move the thumb to the new position.
800 cancelPositionAnimator();
801 setThumbPosition(checked ? 1 : 0);
802 }
Adam Powell12190b32010-11-28 19:07:53 -0800803 }
804
805 @Override
806 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
807 super.onLayout(changed, left, top, right, bottom);
808
Alan Viverette9b38f6c2014-07-30 02:39:07 +0000809 int switchRight;
810 int switchLeft;
Fabrice Di Meglio28efba32012-06-01 16:52:31 -0700811
Alan Viverette8bb39902014-07-29 17:22:30 -0700812 if (isLayoutRtl()) {
Alan Viverette9b38f6c2014-07-30 02:39:07 +0000813 switchLeft = getPaddingLeft();
814 switchRight = switchLeft + mSwitchWidth;
Alan Viverette8bb39902014-07-29 17:22:30 -0700815 } else {
Alan Viverette9b38f6c2014-07-30 02:39:07 +0000816 switchRight = getWidth() - getPaddingRight();
817 switchLeft = switchRight - mSwitchWidth;
Alan Viverette8bb39902014-07-29 17:22:30 -0700818 }
819
Alan Viverette9b38f6c2014-07-30 02:39:07 +0000820 int switchTop = 0;
821 int switchBottom = 0;
Adam Powell12190b32010-11-28 19:07:53 -0800822 switch (getGravity() & Gravity.VERTICAL_GRAVITY_MASK) {
823 default:
824 case Gravity.TOP:
825 switchTop = getPaddingTop();
826 switchBottom = switchTop + mSwitchHeight;
827 break;
828
829 case Gravity.CENTER_VERTICAL:
830 switchTop = (getPaddingTop() + getHeight() - getPaddingBottom()) / 2 -
831 mSwitchHeight / 2;
832 switchBottom = switchTop + mSwitchHeight;
833 break;
834
835 case Gravity.BOTTOM:
836 switchBottom = getHeight() - getPaddingBottom();
837 switchTop = switchBottom - mSwitchHeight;
838 break;
839 }
840
841 mSwitchLeft = switchLeft;
842 mSwitchTop = switchTop;
843 mSwitchBottom = switchBottom;
844 mSwitchRight = switchRight;
845 }
846
847 @Override
Alan Viverettead2f8e32014-05-16 13:28:33 -0700848 public void draw(Canvas c) {
Alan Viverette4d065a02014-07-11 15:28:38 -0700849 final Rect padding = mTempRect;
Alan Viverette9b38f6c2014-07-30 02:39:07 +0000850
851 // Layout the track.
Alan Viverette5876ff42014-03-03 17:40:46 -0800852 final int switchLeft = mSwitchLeft;
853 final int switchTop = mSwitchTop;
854 final int switchRight = mSwitchRight;
855 final int switchBottom = mSwitchBottom;
Alan Viverette8bb39902014-07-29 17:22:30 -0700856 if (mTrackDrawable != null) {
Alan Viverette9b38f6c2014-07-30 02:39:07 +0000857 mTrackDrawable.setBounds(switchLeft, switchTop, switchRight, switchBottom);
Alan Viverette8bb39902014-07-29 17:22:30 -0700858 mTrackDrawable.getPadding(padding);
Alan Viverette8bb39902014-07-29 17:22:30 -0700859 }
Alan Viverettecc2688d2013-09-17 17:00:12 -0700860
Alan Viverette9b38f6c2014-07-30 02:39:07 +0000861 final int switchInnerLeft = switchLeft + padding.left;
862
863 // Relies on mTempRect, MUST be called first!
864 final int thumbPos = getThumbOffset();
865
Alan Viverette661e6362014-05-12 10:55:37 -0700866 // Layout the thumb.
Alan Viverette4d065a02014-07-11 15:28:38 -0700867 if (mThumbDrawable != null) {
868 mThumbDrawable.getPadding(padding);
Alan Viverette9b38f6c2014-07-30 02:39:07 +0000869 final int thumbLeft = switchInnerLeft - padding.left + thumbPos;
870 final int thumbRight = switchInnerLeft + thumbPos + mThumbWidth + padding.right;
Alan Viverette4d065a02014-07-11 15:28:38 -0700871 mThumbDrawable.setBounds(thumbLeft, switchTop, thumbRight, switchBottom);
Alan Viverette61956602014-04-22 19:07:06 -0700872
Alan Viverette4d065a02014-07-11 15:28:38 -0700873 final Drawable background = getBackground();
874 if (background != null) {
875 background.setHotspotBounds(thumbLeft, switchTop, thumbRight, switchBottom);
876 }
Alan Viverette61956602014-04-22 19:07:06 -0700877 }
878
Alan Viverettead2f8e32014-05-16 13:28:33 -0700879 // Draw the background.
880 super.draw(c);
881 }
882
883 @Override
884 protected void onDraw(Canvas canvas) {
Alan Viverette61956602014-04-22 19:07:06 -0700885 super.onDraw(canvas);
886
Alan Viverette4d065a02014-07-11 15:28:38 -0700887 final Rect padding = mTempRect;
Alan Viverettead2f8e32014-05-16 13:28:33 -0700888 final Drawable trackDrawable = mTrackDrawable;
Alan Viverette4d065a02014-07-11 15:28:38 -0700889 if (trackDrawable != null) {
890 trackDrawable.getPadding(padding);
891 } else {
892 padding.setEmpty();
893 }
Alan Viverettead2f8e32014-05-16 13:28:33 -0700894
895 final int switchTop = mSwitchTop;
896 final int switchBottom = mSwitchBottom;
Alan Viverette9b38f6c2014-07-30 02:39:07 +0000897 final int switchInnerLeft = mSwitchLeft + padding.left;
Alan Viverette4d065a02014-07-11 15:28:38 -0700898 final int switchInnerTop = switchTop + padding.top;
Alan Viverette9b38f6c2014-07-30 02:39:07 +0000899 final int switchInnerRight = mSwitchRight - padding.right;
Alan Viverette4d065a02014-07-11 15:28:38 -0700900 final int switchInnerBottom = switchBottom - padding.bottom;
Alan Viverettead2f8e32014-05-16 13:28:33 -0700901
Alan Viverette4d065a02014-07-11 15:28:38 -0700902 final Drawable thumbDrawable = mThumbDrawable;
903 if (trackDrawable != null) {
904 if (mSplitTrack && thumbDrawable != null) {
905 final Insets insets = thumbDrawable.getOpticalInsets();
906 thumbDrawable.copyBounds(padding);
907 padding.left += insets.left;
908 padding.right -= insets.right;
Alan Viverette661e6362014-05-12 10:55:37 -0700909
Alan Viverette4d065a02014-07-11 15:28:38 -0700910 final int saveCount = canvas.save();
911 canvas.clipRect(padding, Op.DIFFERENCE);
912 trackDrawable.draw(canvas);
913 canvas.restoreToCount(saveCount);
914 } else {
915 trackDrawable.draw(canvas);
916 }
Alan Viverette661e6362014-05-12 10:55:37 -0700917 }
Alan Viverette61956602014-04-22 19:07:06 -0700918
919 final int saveCount = canvas.save();
Alan Viverette4d065a02014-07-11 15:28:38 -0700920
921 if (thumbDrawable != null) {
Alan Viverette9b38f6c2014-07-30 02:39:07 +0000922 canvas.clipRect(switchInnerLeft, switchTop, switchInnerRight, switchBottom);
Alan Viverette4d065a02014-07-11 15:28:38 -0700923 thumbDrawable.draw(canvas);
924 }
Adam Powell12190b32010-11-28 19:07:53 -0800925
Alan Viverette5876ff42014-03-03 17:40:46 -0800926 final Layout switchText = getTargetCheckedState() ? mOnLayout : mOffLayout;
Fabrice Di Megliobe06e322012-09-11 17:42:45 -0700927 if (switchText != null) {
Alan Viverette661e6362014-05-12 10:55:37 -0700928 final int drawableState[] = getDrawableState();
929 if (mTextColors != null) {
930 mTextPaint.setColor(mTextColors.getColorForState(drawableState, 0));
931 }
932 mTextPaint.drawableState = drawableState;
933
Alan Viverette4d065a02014-07-11 15:28:38 -0700934 final int cX;
935 if (thumbDrawable != null) {
936 final Rect bounds = thumbDrawable.getBounds();
937 cX = bounds.left + bounds.right;
938 } else {
Alan Viverettedec17292014-07-12 00:26:36 -0700939 cX = getWidth();
Alan Viverette4d065a02014-07-11 15:28:38 -0700940 }
941
942 final int left = cX / 2 - switchText.getWidth() / 2;
Alan Viverette5876ff42014-03-03 17:40:46 -0800943 final int top = (switchInnerTop + switchInnerBottom) / 2 - switchText.getHeight() / 2;
944 canvas.translate(left, top);
Fabrice Di Megliobe06e322012-09-11 17:42:45 -0700945 switchText.draw(canvas);
946 }
Adam Powell12190b32010-11-28 19:07:53 -0800947
Alan Viverette5876ff42014-03-03 17:40:46 -0800948 canvas.restoreToCount(saveCount);
Adam Powell12190b32010-11-28 19:07:53 -0800949 }
950
951 @Override
Fabrice Di Meglio28efba32012-06-01 16:52:31 -0700952 public int getCompoundPaddingLeft() {
953 if (!isLayoutRtl()) {
954 return super.getCompoundPaddingLeft();
955 }
956 int padding = super.getCompoundPaddingLeft() + mSwitchWidth;
957 if (!TextUtils.isEmpty(getText())) {
958 padding += mSwitchPadding;
959 }
960 return padding;
961 }
962
963 @Override
Adam Powell12190b32010-11-28 19:07:53 -0800964 public int getCompoundPaddingRight() {
Fabrice Di Meglio28efba32012-06-01 16:52:31 -0700965 if (isLayoutRtl()) {
966 return super.getCompoundPaddingRight();
967 }
Adam Powell12190b32010-11-28 19:07:53 -0800968 int padding = super.getCompoundPaddingRight() + mSwitchWidth;
969 if (!TextUtils.isEmpty(getText())) {
970 padding += mSwitchPadding;
971 }
972 return padding;
973 }
974
Alan Viverettecc2688d2013-09-17 17:00:12 -0700975 /**
976 * Translates thumb position to offset according to current RTL setting and
Alan Viverette9b38f6c2014-07-30 02:39:07 +0000977 * thumb scroll range.
Alan Viverettecc2688d2013-09-17 17:00:12 -0700978 *
979 * @return thumb offset
980 */
981 private int getThumbOffset() {
982 final float thumbPosition;
983 if (isLayoutRtl()) {
984 thumbPosition = 1 - mThumbPosition;
985 } else {
986 thumbPosition = mThumbPosition;
987 }
988 return (int) (thumbPosition * getThumbScrollRange() + 0.5f);
989 }
990
Adam Powell12190b32010-11-28 19:07:53 -0800991 private int getThumbScrollRange() {
Alan Viverette4d065a02014-07-11 15:28:38 -0700992 if (mTrackDrawable != null) {
Alan Viverette9b38f6c2014-07-30 02:39:07 +0000993 mTrackDrawable.getPadding(mTempRect);
994 return mSwitchWidth - mThumbWidth - mTempRect.left - mTempRect.right;
Alan Viverette4d065a02014-07-11 15:28:38 -0700995 } else {
Adam Powell12190b32010-11-28 19:07:53 -0800996 return 0;
997 }
Adam Powell12190b32010-11-28 19:07:53 -0800998 }
999
1000 @Override
1001 protected int[] onCreateDrawableState(int extraSpace) {
1002 final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
1003 if (isChecked()) {
1004 mergeDrawableStates(drawableState, CHECKED_STATE_SET);
1005 }
1006 return drawableState;
1007 }
1008
1009 @Override
1010 protected void drawableStateChanged() {
1011 super.drawableStateChanged();
1012
Alan Viverette661e6362014-05-12 10:55:37 -07001013 final int[] myDrawableState = getDrawableState();
Adam Powell12190b32010-11-28 19:07:53 -08001014
Alan Viverette2356c5e2014-05-22 22:43:59 -07001015 if (mThumbDrawable != null) {
1016 mThumbDrawable.setState(myDrawableState);
Alan Viverette661e6362014-05-12 10:55:37 -07001017 }
1018
1019 if (mTrackDrawable != null) {
1020 mTrackDrawable.setState(myDrawableState);
1021 }
Adam Powell12190b32010-11-28 19:07:53 -08001022
1023 invalidate();
1024 }
1025
Alan Viverettecebc6ba2014-06-13 15:52:13 -07001026 @Override
Alan Viverette8de14942014-06-18 18:05:15 -07001027 public void drawableHotspotChanged(float x, float y) {
1028 super.drawableHotspotChanged(x, y);
Alan Viverettecebc6ba2014-06-13 15:52:13 -07001029
1030 if (mThumbDrawable != null) {
1031 mThumbDrawable.setHotspot(x, y);
1032 }
1033
1034 if (mTrackDrawable != null) {
1035 mTrackDrawable.setHotspot(x, y);
1036 }
1037 }
1038
Adam Powell12190b32010-11-28 19:07:53 -08001039 @Override
1040 protected boolean verifyDrawable(Drawable who) {
1041 return super.verifyDrawable(who) || who == mThumbDrawable || who == mTrackDrawable;
1042 }
1043
1044 @Override
1045 public void jumpDrawablesToCurrentState() {
1046 super.jumpDrawablesToCurrentState();
Alan Viverette4d065a02014-07-11 15:28:38 -07001047
1048 if (mThumbDrawable != null) {
1049 mThumbDrawable.jumpToCurrentState();
1050 }
1051
1052 if (mTrackDrawable != null) {
1053 mTrackDrawable.jumpToCurrentState();
1054 }
1055
1056 if (mPositionAnimator != null && mPositionAnimator.isRunning()) {
1057 mPositionAnimator.end();
1058 mPositionAnimator = null;
1059 }
Adam Powell12190b32010-11-28 19:07:53 -08001060 }
Svetoslav Ganov8a78fd42012-01-17 14:36:46 -08001061
1062 @Override
1063 public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
1064 super.onInitializeAccessibilityEvent(event);
1065 event.setClassName(Switch.class.getName());
1066 }
1067
1068 @Override
1069 public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
1070 super.onInitializeAccessibilityNodeInfo(info);
1071 info.setClassName(Switch.class.getName());
Svetoslav Ganov78bcc152012-04-12 17:17:19 -07001072 CharSequence switchText = isChecked() ? mTextOn : mTextOff;
1073 if (!TextUtils.isEmpty(switchText)) {
1074 CharSequence oldText = info.getText();
1075 if (TextUtils.isEmpty(oldText)) {
1076 info.setText(switchText);
1077 } else {
1078 StringBuilder newText = new StringBuilder();
1079 newText.append(oldText).append(' ').append(switchText);
1080 info.setText(newText);
1081 }
1082 }
Svetoslav Ganov8a78fd42012-01-17 14:36:46 -08001083 }
Alan Viverettecc2688d2013-09-17 17:00:12 -07001084
1085 private static final FloatProperty<Switch> THUMB_POS = new FloatProperty<Switch>("thumbPos") {
1086 @Override
1087 public Float get(Switch object) {
1088 return object.mThumbPosition;
1089 }
1090
1091 @Override
1092 public void setValue(Switch object, float value) {
1093 object.setThumbPosition(value);
1094 }
1095 };
Adam Powell12190b32010-11-28 19:07:53 -08001096}