blob: 422d2d37321cc6a64e52e6864dee1f29b36ada08 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2007 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
Tor Norbye7b9c9122013-05-30 16:48:33 -070019import android.annotation.DrawableRes;
Aurimas Liutikas99441c52016-10-11 16:48:32 -070020import android.annotation.NonNull;
Alan Viverette4f64c042014-07-21 17:49:13 -070021import android.annotation.Nullable;
Artur Satayevad9254c2019-12-10 17:47:54 +000022import android.compat.annotation.UnsupportedAppUsage;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080023import android.content.Context;
Alan Viverette4f64c042014-07-21 17:49:13 -070024import android.content.res.ColorStateList;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080025import android.content.res.TypedArray;
Nader Jawad8e31c3e2019-04-14 21:58:04 -070026import android.graphics.BlendMode;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080027import android.graphics.Canvas;
Alan Viverette4f64c042014-07-21 17:49:13 -070028import android.graphics.PorterDuff;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080029import android.graphics.drawable.Drawable;
Aurimas Liutikase3fa1762015-11-17 14:28:04 -080030import android.os.Parcel;
31import android.os.Parcelable;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080032import android.util.AttributeSet;
33import android.view.Gravity;
Alan Viverette2356c5e2014-05-22 22:43:59 -070034import android.view.RemotableViewMethod;
Steve Zeigler7a367882010-02-23 16:39:08 -080035import android.view.ViewDebug;
Aurimas Liutikas99441c52016-10-11 16:48:32 -070036import android.view.ViewHierarchyEncoder;
svetoslavganov75986cf2009-05-14 22:28:01 -070037import android.view.accessibility.AccessibilityEvent;
Svetoslav Ganov34ffaab2011-09-12 16:19:32 -070038import android.view.accessibility.AccessibilityNodeInfo;
Ashley Rose55f9f922019-01-28 19:29:36 -050039import android.view.inspector.InspectableProperty;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080040
Aurimas Liutikas99441c52016-10-11 16:48:32 -070041import com.android.internal.R;
42
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080043/**
Alan Viverette49f118e2015-02-23 17:15:27 -080044 * An extension to {@link TextView} that supports the {@link Checkable}
45 * interface and displays.
46 * <p>
47 * This is useful when used in a {@link android.widget.ListView ListView} where
48 * the {@link android.widget.ListView#setChoiceMode(int) setChoiceMode} has
49 * been set to something other than
50 * {@link android.widget.ListView#CHOICE_MODE_NONE CHOICE_MODE_NONE}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080051 *
Gilles Debunne2fb40282012-05-01 12:07:06 -070052 * @attr ref android.R.styleable#CheckedTextView_checked
53 * @attr ref android.R.styleable#CheckedTextView_checkMark
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080054 */
Romain Guy8b5e7c02009-04-29 11:48:22 -070055public class CheckedTextView extends TextView implements Checkable {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080056 private boolean mChecked;
Alan Viverette4f64c042014-07-21 17:49:13 -070057
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080058 private int mCheckMarkResource;
Mathew Inwood978c6e22018-08-21 15:58:55 +010059 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080060 private Drawable mCheckMarkDrawable;
Alan Viverettea4264452014-07-28 16:02:55 -070061 private ColorStateList mCheckMarkTintList = null;
Nader Jawad8e31c3e2019-04-14 21:58:04 -070062 private BlendMode mCheckMarkBlendMode = null;
Alan Viverette4f64c042014-07-21 17:49:13 -070063 private boolean mHasCheckMarkTint = false;
Alan Viveretteb56f5d22014-09-14 15:48:50 -070064 private boolean mHasCheckMarkTintMode = false;
Alan Viverette4f64c042014-07-21 17:49:13 -070065
Fabrice Di Meglioaff599b2011-07-20 19:05:01 -070066 private int mBasePadding;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080067 private int mCheckMarkWidth;
Mathew Inwood978c6e22018-08-21 15:58:55 +010068 @UnsupportedAppUsage
Adam Powell16f2b902014-08-11 17:30:19 -070069 private int mCheckMarkGravity = Gravity.END;
70
Fabrice Di Meglioaff599b2011-07-20 19:05:01 -070071 private boolean mNeedRequestlayout;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080072
73 private static final int[] CHECKED_STATE_SET = {
74 R.attr.state_checked
75 };
76
77 public CheckedTextView(Context context) {
78 this(context, null);
79 }
80
81 public CheckedTextView(Context context, AttributeSet attrs) {
Fabrice Di Megliob023a582012-09-30 15:31:06 -070082 this(context, attrs, R.attr.checkedTextViewStyle);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080083 }
84
Alan Viverette617feb92013-09-09 18:09:13 -070085 public CheckedTextView(Context context, AttributeSet attrs, int defStyleAttr) {
86 this(context, attrs, defStyleAttr, 0);
87 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080088
Alan Viverette617feb92013-09-09 18:09:13 -070089 public CheckedTextView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
90 super(context, attrs, defStyleAttr, defStyleRes);
91
92 final TypedArray a = context.obtainStyledAttributes(
93 attrs, R.styleable.CheckedTextView, defStyleAttr, defStyleRes);
Aurimas Liutikasab324cf2019-02-07 16:46:38 -080094 saveAttributeDataForStyleable(context, R.styleable.CheckedTextView,
95 attrs, a, defStyleAttr, defStyleRes);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080096
Alan Viveretteb56f5d22014-09-14 15:48:50 -070097 final Drawable d = a.getDrawable(R.styleable.CheckedTextView_checkMark);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080098 if (d != null) {
99 setCheckMarkDrawable(d);
100 }
101
Alan Viveretteb56f5d22014-09-14 15:48:50 -0700102 if (a.hasValue(R.styleable.CheckedTextView_checkMarkTintMode)) {
Nader Jawad8e31c3e2019-04-14 21:58:04 -0700103 mCheckMarkBlendMode = Drawable.parseBlendMode(a.getInt(
104 R.styleable.CheckedTextView_checkMarkTintMode, -1),
105 mCheckMarkBlendMode);
Alan Viveretteb56f5d22014-09-14 15:48:50 -0700106 mHasCheckMarkTintMode = true;
107 }
Alan Viverette4f64c042014-07-21 17:49:13 -0700108
Adam Powell16f2b902014-08-11 17:30:19 -0700109 if (a.hasValue(R.styleable.CheckedTextView_checkMarkTint)) {
110 mCheckMarkTintList = a.getColorStateList(R.styleable.CheckedTextView_checkMarkTint);
Alan Viverette4f64c042014-07-21 17:49:13 -0700111 mHasCheckMarkTint = true;
Alan Viverette4f64c042014-07-21 17:49:13 -0700112 }
113
Adam Powell16f2b902014-08-11 17:30:19 -0700114 mCheckMarkGravity = a.getInt(R.styleable.CheckedTextView_checkMarkGravity, Gravity.END);
115
Alan Viveretteb56f5d22014-09-14 15:48:50 -0700116 final boolean checked = a.getBoolean(R.styleable.CheckedTextView_checked, false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800117 setChecked(checked);
118
119 a.recycle();
Alan Viveretteb56f5d22014-09-14 15:48:50 -0700120
121 applyCheckMarkTint();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800122 }
123
124 public void toggle() {
125 setChecked(!mChecked);
126 }
Steve Zeigler7a367882010-02-23 16:39:08 -0800127
128 @ViewDebug.ExportedProperty
Ashley Rose55f9f922019-01-28 19:29:36 -0500129 @InspectableProperty
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800130 public boolean isChecked() {
131 return mChecked;
132 }
133
134 /**
Alan Viverette49f118e2015-02-23 17:15:27 -0800135 * Sets the checked state of this view.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800136 *
Alan Viverette49f118e2015-02-23 17:15:27 -0800137 * @param checked {@code true} set the state to checked, {@code false} to
138 * uncheck
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800139 */
140 public void setChecked(boolean checked) {
141 if (mChecked != checked) {
142 mChecked = checked;
143 refreshDrawableState();
Eugene Susla72c510f2018-01-23 21:12:11 +0000144 notifyViewAccessibilityStateChangedIfNeeded(
Alan Viverette77e9a282013-09-12 17:16:09 -0700145 AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800146 }
147 }
148
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800149 /**
Alan Viverette49f118e2015-02-23 17:15:27 -0800150 * Sets the check mark to the drawable with the specified resource ID.
151 * <p>
152 * When this view is checked, the drawable's state set will include
153 * {@link android.R.attr#state_checked}.
Fabrice Di Meglio343e1132012-09-28 18:01:17 -0700154 *
Alan Viverette49f118e2015-02-23 17:15:27 -0800155 * @param resId the resource identifier of drawable to use as the check
156 * mark
157 * @attr ref android.R.styleable#CheckedTextView_checkMark
Gilles Debunne2fb40282012-05-01 12:07:06 -0700158 * @see #setCheckMarkDrawable(Drawable)
159 * @see #getCheckMarkDrawable()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800160 */
Alan Viverette49f118e2015-02-23 17:15:27 -0800161 public void setCheckMarkDrawable(@DrawableRes int resId) {
162 if (resId != 0 && resId == mCheckMarkResource) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800163 return;
164 }
165
Alan Viverettec60feca2015-08-12 16:26:37 -0400166 final Drawable d = resId != 0 ? getContext().getDrawable(resId) : null;
167 setCheckMarkDrawableInternal(d, resId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800168 }
169
170 /**
Alan Viverette49f118e2015-02-23 17:15:27 -0800171 * Set the check mark to the specified drawable.
172 * <p>
173 * When this view is checked, the drawable's state set will include
174 * {@link android.R.attr#state_checked}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800175 *
Alan Viverette49f118e2015-02-23 17:15:27 -0800176 * @param d the drawable to use for the check mark
177 * @attr ref android.R.styleable#CheckedTextView_checkMark
Gilles Debunne2fb40282012-05-01 12:07:06 -0700178 * @see #setCheckMarkDrawable(int)
179 * @see #getCheckMarkDrawable()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800180 */
Alan Viverettec60feca2015-08-12 16:26:37 -0400181 public void setCheckMarkDrawable(@Nullable Drawable d) {
182 setCheckMarkDrawableInternal(d, 0);
183 }
184
185 private void setCheckMarkDrawableInternal(@Nullable Drawable d, @DrawableRes int resId) {
Leon Scrogginsa8da1732009-10-19 19:04:30 -0400186 if (mCheckMarkDrawable != null) {
187 mCheckMarkDrawable.setCallback(null);
188 unscheduleDrawable(mCheckMarkDrawable);
189 }
Alan Viverettec60feca2015-08-12 16:26:37 -0400190
Fabrice Di Meglioaff599b2011-07-20 19:05:01 -0700191 mNeedRequestlayout = (d != mCheckMarkDrawable);
Alan Viverettec60feca2015-08-12 16:26:37 -0400192
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800193 if (d != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800194 d.setCallback(this);
195 d.setVisible(getVisibility() == VISIBLE, false);
196 d.setState(CHECKED_STATE_SET);
Fabrice Di Meglio343e1132012-09-28 18:01:17 -0700197
Alan Viverette28fabe52016-04-25 13:15:39 -0400198 // Record the intrinsic dimensions when in "checked" state.
199 setMinHeight(d.getIntrinsicHeight());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800200 mCheckMarkWidth = d.getIntrinsicWidth();
Alan Viverette28fabe52016-04-25 13:15:39 -0400201
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800202 d.setState(getDrawableState());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800203 } else {
Fabrice Di Meglioaff599b2011-07-20 19:05:01 -0700204 mCheckMarkWidth = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800205 }
Alan Viverettec60feca2015-08-12 16:26:37 -0400206
Leon Scrogginsa8da1732009-10-19 19:04:30 -0400207 mCheckMarkDrawable = d;
Alan Viverettec60feca2015-08-12 16:26:37 -0400208 mCheckMarkResource = resId;
Alan Viverette2356c5e2014-05-22 22:43:59 -0700209
Alan Viverette28fabe52016-04-25 13:15:39 -0400210 applyCheckMarkTint();
211
Alan Viverette2356c5e2014-05-22 22:43:59 -0700212 // Do padding resolution. This will call internalSetPadding() and do a
213 // requestLayout() if needed.
Fabrice Di Meglioaff599b2011-07-20 19:05:01 -0700214 resolvePadding();
215 }
216
Alan Viverette4f64c042014-07-21 17:49:13 -0700217 /**
218 * Applies a tint to the check mark drawable. Does not modify the
Alan Viveretteb56f5d22014-09-14 15:48:50 -0700219 * current tint mode, which is {@link PorterDuff.Mode#SRC_IN} by default.
Alan Viverette4f64c042014-07-21 17:49:13 -0700220 * <p>
221 * Subsequent calls to {@link #setCheckMarkDrawable(Drawable)} will
222 * automatically mutate the drawable and apply the specified tint and
223 * tint mode using
Alan Viverettea4264452014-07-28 16:02:55 -0700224 * {@link Drawable#setTintList(ColorStateList)}.
Alan Viverette4f64c042014-07-21 17:49:13 -0700225 *
226 * @param tint the tint to apply, may be {@code null} to clear tint
227 *
228 * @attr ref android.R.styleable#CheckedTextView_checkMarkTint
Alan Viverettea4264452014-07-28 16:02:55 -0700229 * @see #getCheckMarkTintList()
230 * @see Drawable#setTintList(ColorStateList)
Alan Viverette4f64c042014-07-21 17:49:13 -0700231 */
Alan Viverettea4264452014-07-28 16:02:55 -0700232 public void setCheckMarkTintList(@Nullable ColorStateList tint) {
233 mCheckMarkTintList = tint;
Alan Viverette4f64c042014-07-21 17:49:13 -0700234 mHasCheckMarkTint = true;
235
236 applyCheckMarkTint();
237 }
238
239 /**
Alan Viveretteb56f5d22014-09-14 15:48:50 -0700240 * Returns the tint applied to the check mark drawable, if specified.
241 *
Alan Viverette4f64c042014-07-21 17:49:13 -0700242 * @return the tint applied to the check mark drawable
243 * @attr ref android.R.styleable#CheckedTextView_checkMarkTint
Alan Viverettea4264452014-07-28 16:02:55 -0700244 * @see #setCheckMarkTintList(ColorStateList)
Alan Viverette4f64c042014-07-21 17:49:13 -0700245 */
Ashley Rose55f9f922019-01-28 19:29:36 -0500246 @InspectableProperty(name = "checkMarkTint")
Alan Viverette4f64c042014-07-21 17:49:13 -0700247 @Nullable
Alan Viverettea4264452014-07-28 16:02:55 -0700248 public ColorStateList getCheckMarkTintList() {
249 return mCheckMarkTintList;
Alan Viverette4f64c042014-07-21 17:49:13 -0700250 }
251
252 /**
253 * Specifies the blending mode used to apply the tint specified by
Alan Viverettea4264452014-07-28 16:02:55 -0700254 * {@link #setCheckMarkTintList(ColorStateList)} to the check mark
Alan Viveretteb56f5d22014-09-14 15:48:50 -0700255 * drawable. The default mode is {@link PorterDuff.Mode#SRC_IN}.
Alan Viverette4f64c042014-07-21 17:49:13 -0700256 *
257 * @param tintMode the blending mode used to apply the tint, may be
258 * {@code null} to clear tint
259 * @attr ref android.R.styleable#CheckedTextView_checkMarkTintMode
Alan Viverettea4264452014-07-28 16:02:55 -0700260 * @see #setCheckMarkTintList(ColorStateList)
261 * @see Drawable#setTintMode(PorterDuff.Mode)
Alan Viverette4f64c042014-07-21 17:49:13 -0700262 */
263 public void setCheckMarkTintMode(@Nullable PorterDuff.Mode tintMode) {
Nader Jawad8e31c3e2019-04-14 21:58:04 -0700264 setCheckMarkTintBlendMode(tintMode != null
265 ? BlendMode.fromValue(tintMode.nativeInt) : null);
266 }
267
268 /**
269 * Specifies the blending mode used to apply the tint specified by
270 * {@link #setCheckMarkTintList(ColorStateList)} to the check mark
271 * drawable. The default mode is {@link PorterDuff.Mode#SRC_IN}.
272 *
273 * @param tintMode the blending mode used to apply the tint, may be
274 * {@code null} to clear tint
275 * @attr ref android.R.styleable#CheckedTextView_checkMarkTintMode
276 * @see #setCheckMarkTintList(ColorStateList)
277 * @see Drawable#setTintBlendMode(BlendMode)
278 */
279 public void setCheckMarkTintBlendMode(@Nullable BlendMode tintMode) {
280 mCheckMarkBlendMode = tintMode;
Alan Viveretteb56f5d22014-09-14 15:48:50 -0700281 mHasCheckMarkTintMode = true;
Alan Viverette4f64c042014-07-21 17:49:13 -0700282
283 applyCheckMarkTint();
284 }
285
286 /**
Alan Viveretteb56f5d22014-09-14 15:48:50 -0700287 * Returns the blending mode used to apply the tint to the check mark
288 * drawable, if specified.
289 *
290 * @return the blending mode used to apply the tint to the check mark
291 * drawable
Alan Viverette4f64c042014-07-21 17:49:13 -0700292 * @attr ref android.R.styleable#CheckedTextView_checkMarkTintMode
293 * @see #setCheckMarkTintMode(PorterDuff.Mode)
294 */
Ashley Rose55f9f922019-01-28 19:29:36 -0500295 @InspectableProperty
Alan Viverette4f64c042014-07-21 17:49:13 -0700296 @Nullable
297 public PorterDuff.Mode getCheckMarkTintMode() {
Nader Jawad8e31c3e2019-04-14 21:58:04 -0700298 return mCheckMarkBlendMode != null
299 ? BlendMode.blendModeToPorterDuffMode(mCheckMarkBlendMode) : null;
300 }
301
302 /**
303 * Returns the blending mode used to apply the tint to the check mark
304 * drawable, if specified.
305 *
306 * @return the blending mode used to apply the tint to the check mark
307 * drawable
308 * @attr ref android.R.styleable#CheckedTextView_checkMarkTintMode
309 * @see #setCheckMarkTintMode(PorterDuff.Mode)
310 */
311 @InspectableProperty(attributeId = android.R.styleable.CheckedTextView_checkMarkTintMode)
312 @Nullable
313 public BlendMode getCheckMarkTintBlendMode() {
314 return mCheckMarkBlendMode;
Alan Viverette4f64c042014-07-21 17:49:13 -0700315 }
316
317 private void applyCheckMarkTint() {
Alan Viveretteb56f5d22014-09-14 15:48:50 -0700318 if (mCheckMarkDrawable != null && (mHasCheckMarkTint || mHasCheckMarkTintMode)) {
Alan Viverette4f64c042014-07-21 17:49:13 -0700319 mCheckMarkDrawable = mCheckMarkDrawable.mutate();
Alan Viveretteb56f5d22014-09-14 15:48:50 -0700320
321 if (mHasCheckMarkTint) {
322 mCheckMarkDrawable.setTintList(mCheckMarkTintList);
323 }
324
325 if (mHasCheckMarkTintMode) {
Nader Jawad8e31c3e2019-04-14 21:58:04 -0700326 mCheckMarkDrawable.setTintBlendMode(mCheckMarkBlendMode);
Alan Viveretteb56f5d22014-09-14 15:48:50 -0700327 }
Alan Viveretted5133792014-10-28 14:41:36 -0700328
329 // The drawable (or one of its children) may not have been
330 // stateful before applying the tint, so let's try again.
331 if (mCheckMarkDrawable.isStateful()) {
332 mCheckMarkDrawable.setState(getDrawableState());
333 }
Alan Viverette4f64c042014-07-21 17:49:13 -0700334 }
335 }
336
Alan Viverette2356c5e2014-05-22 22:43:59 -0700337 @RemotableViewMethod
338 @Override
339 public void setVisibility(int visibility) {
340 super.setVisibility(visibility);
341
342 if (mCheckMarkDrawable != null) {
343 mCheckMarkDrawable.setVisible(visibility == VISIBLE, false);
344 }
345 }
346
347 @Override
348 public void jumpDrawablesToCurrentState() {
349 super.jumpDrawablesToCurrentState();
350
351 if (mCheckMarkDrawable != null) {
352 mCheckMarkDrawable.jumpToCurrentState();
353 }
354 }
355
356 @Override
Alan Viverettef6d87ec2016-03-11 10:09:14 -0500357 protected boolean verifyDrawable(@NonNull Drawable who) {
Alan Viverette2356c5e2014-05-22 22:43:59 -0700358 return who == mCheckMarkDrawable || super.verifyDrawable(who);
359 }
360
Gilles Debunne2fb40282012-05-01 12:07:06 -0700361 /**
362 * Gets the checkmark drawable
363 *
364 * @return The drawable use to represent the checkmark, if any.
365 *
366 * @see #setCheckMarkDrawable(Drawable)
367 * @see #setCheckMarkDrawable(int)
368 *
369 * @attr ref android.R.styleable#CheckedTextView_checkMark
370 */
Ashley Rose55f9f922019-01-28 19:29:36 -0500371 @InspectableProperty(name = "checkMark")
Gilles Debunne2fb40282012-05-01 12:07:06 -0700372 public Drawable getCheckMarkDrawable() {
373 return mCheckMarkDrawable;
374 }
375
Fabrice Di Meglio23c89fd2012-08-13 12:17:42 -0700376 /**
377 * @hide
378 */
379 @Override
380 protected void internalSetPadding(int left, int top, int right, int bottom) {
381 super.internalSetPadding(left, top, right, bottom);
Adam Powell16f2b902014-08-11 17:30:19 -0700382 setBasePadding(isCheckMarkAtStart());
Fabrice Di Meglio23c89fd2012-08-13 12:17:42 -0700383 }
384
Fabrice Di Meglioaff599b2011-07-20 19:05:01 -0700385 @Override
Fabrice Di Meglio343e1132012-09-28 18:01:17 -0700386 public void onRtlPropertiesChanged(int layoutDirection) {
387 super.onRtlPropertiesChanged(layoutDirection);
Fabrice Di Meglio15bbde42012-09-28 15:49:38 -0700388 updatePadding();
389 }
390
391 private void updatePadding() {
Fabrice Di Meglio47fb1912012-09-28 19:50:18 -0700392 resetPaddingToInitialValues();
Fabrice Di Meglioaff599b2011-07-20 19:05:01 -0700393 int newPadding = (mCheckMarkDrawable != null) ?
394 mCheckMarkWidth + mBasePadding : mBasePadding;
Adam Powell16f2b902014-08-11 17:30:19 -0700395 if (isCheckMarkAtStart()) {
Fabrice Di Meglio84ebb352012-10-11 16:27:37 -0700396 mNeedRequestlayout |= (mPaddingLeft != newPadding);
Fabrice Di Meglioe2386c12012-07-26 15:17:16 -0700397 mPaddingLeft = newPadding;
398 } else {
Fabrice Di Meglio84ebb352012-10-11 16:27:37 -0700399 mNeedRequestlayout |= (mPaddingRight != newPadding);
Fabrice Di Meglioe2386c12012-07-26 15:17:16 -0700400 mPaddingRight = newPadding;
401 }
Fabrice Di Meglioaff599b2011-07-20 19:05:01 -0700402 if (mNeedRequestlayout) {
403 requestLayout();
404 mNeedRequestlayout = false;
405 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800406 }
Fabrice Di Meglio15bbde42012-09-28 15:49:38 -0700407
Adam Powell16f2b902014-08-11 17:30:19 -0700408 private void setBasePadding(boolean checkmarkAtStart) {
409 if (checkmarkAtStart) {
Fabrice Di Meglio0dc96462012-08-24 12:25:28 -0700410 mBasePadding = mPaddingLeft;
411 } else {
412 mBasePadding = mPaddingRight;
413 }
Fabrice Di Megliobf923eb2012-03-07 16:20:22 -0800414 }
415
Adam Powell16f2b902014-08-11 17:30:19 -0700416 private boolean isCheckMarkAtStart() {
417 final int gravity = Gravity.getAbsoluteGravity(mCheckMarkGravity, getLayoutDirection());
418 final int hgrav = gravity & Gravity.HORIZONTAL_GRAVITY_MASK;
419 return hgrav == Gravity.LEFT;
420 }
421
Fabrice Di Megliobf923eb2012-03-07 16:20:22 -0800422 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800423 protected void onDraw(Canvas canvas) {
424 super.onDraw(canvas);
425
426 final Drawable checkMarkDrawable = mCheckMarkDrawable;
427 if (checkMarkDrawable != null) {
428 final int verticalGravity = getGravity() & Gravity.VERTICAL_GRAVITY_MASK;
429 final int height = checkMarkDrawable.getIntrinsicHeight();
430
431 int y = 0;
432
433 switch (verticalGravity) {
434 case Gravity.BOTTOM:
435 y = getHeight() - height;
436 break;
437 case Gravity.CENTER_VERTICAL:
438 y = (getHeight() - height) / 2;
439 break;
440 }
Aurimas Liutikas99441c52016-10-11 16:48:32 -0700441
Adam Powell16f2b902014-08-11 17:30:19 -0700442 final boolean checkMarkAtStart = isCheckMarkAtStart();
Fabrice Di Meglioe2386c12012-07-26 15:17:16 -0700443 final int width = getWidth();
444 final int top = y;
445 final int bottom = top + height;
Fabrice Di Meglio23c89fd2012-08-13 12:17:42 -0700446 final int left;
447 final int right;
Adam Powell16f2b902014-08-11 17:30:19 -0700448 if (checkMarkAtStart) {
Fabrice Di Meglio0dc96462012-08-24 12:25:28 -0700449 left = mBasePadding;
Fabrice Di Meglio23c89fd2012-08-13 12:17:42 -0700450 right = left + mCheckMarkWidth;
Fabrice Di Meglio0dc96462012-08-24 12:25:28 -0700451 } else {
452 right = width - mBasePadding;
453 left = right - mCheckMarkWidth;
Fabrice Di Meglio23c89fd2012-08-13 12:17:42 -0700454 }
Jorn Jacobsson78cdc552013-01-31 17:52:20 +0100455 checkMarkDrawable.setBounds(mScrollX + left, top, mScrollX + right, bottom);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800456 checkMarkDrawable.draw(canvas);
Alan Viverette2356c5e2014-05-22 22:43:59 -0700457
458 final Drawable background = getBackground();
459 if (background != null) {
460 background.setHotspotBounds(mScrollX + left, top, mScrollX + right, bottom);
461 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800462 }
463 }
Aurimas Liutikas99441c52016-10-11 16:48:32 -0700464
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800465 @Override
466 protected int[] onCreateDrawableState(int extraSpace) {
467 final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
468 if (isChecked()) {
469 mergeDrawableStates(drawableState, CHECKED_STATE_SET);
470 }
471 return drawableState;
472 }
473
474 @Override
475 protected void drawableStateChanged() {
476 super.drawableStateChanged();
Alan Viverettead0020f2015-09-04 10:10:42 -0400477
478 final Drawable checkMarkDrawable = mCheckMarkDrawable;
479 if (checkMarkDrawable != null && checkMarkDrawable.isStateful()
480 && checkMarkDrawable.setState(getDrawableState())) {
481 invalidateDrawable(checkMarkDrawable);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800482 }
483 }
svetoslavganov75986cf2009-05-14 22:28:01 -0700484
Alan Viverettecebc6ba2014-06-13 15:52:13 -0700485 @Override
Alan Viverette8de14942014-06-18 18:05:15 -0700486 public void drawableHotspotChanged(float x, float y) {
487 super.drawableHotspotChanged(x, y);
Alan Viverettecebc6ba2014-06-13 15:52:13 -0700488
489 if (mCheckMarkDrawable != null) {
490 mCheckMarkDrawable.setHotspot(x, y);
491 }
492 }
493
Dianne Hackborna7bb6fb2015-02-03 18:13:40 -0800494 @Override
495 public CharSequence getAccessibilityClassName() {
496 return CheckedTextView.class.getName();
497 }
498
Aurimas Liutikase3fa1762015-11-17 14:28:04 -0800499 static class SavedState extends BaseSavedState {
500 boolean checked;
501
502 /**
503 * Constructor called from {@link CheckedTextView#onSaveInstanceState()}
504 */
505 SavedState(Parcelable superState) {
506 super(superState);
507 }
508
509 /**
510 * Constructor called from {@link #CREATOR}
511 */
512 private SavedState(Parcel in) {
513 super(in);
514 checked = (Boolean)in.readValue(null);
515 }
516
517 @Override
518 public void writeToParcel(Parcel out, int flags) {
519 super.writeToParcel(out, flags);
520 out.writeValue(checked);
521 }
522
523 @Override
524 public String toString() {
525 return "CheckedTextView.SavedState{"
526 + Integer.toHexString(System.identityHashCode(this))
527 + " checked=" + checked + "}";
528 }
529
Jeff Sharkey9e8f83d2019-02-28 12:06:45 -0700530 public static final @android.annotation.NonNull Parcelable.Creator<SavedState> CREATOR
Aurimas Liutikase3fa1762015-11-17 14:28:04 -0800531 = new Parcelable.Creator<SavedState>() {
532 public SavedState createFromParcel(Parcel in) {
533 return new SavedState(in);
534 }
535
536 public SavedState[] newArray(int size) {
537 return new SavedState[size];
538 }
539 };
540 }
541
542 @Override
543 public Parcelable onSaveInstanceState() {
544 Parcelable superState = super.onSaveInstanceState();
545
546 SavedState ss = new SavedState(superState);
547
548 ss.checked = isChecked();
549 return ss;
550 }
551
552 @Override
553 public void onRestoreInstanceState(Parcelable state) {
554 SavedState ss = (SavedState) state;
555
556 super.onRestoreInstanceState(ss.getSuperState());
557 setChecked(ss.checked);
558 requestLayout();
559 }
560
Alan Viverettea54956a2015-01-07 16:05:02 -0800561 /** @hide */
svetoslavganov75986cf2009-05-14 22:28:01 -0700562 @Override
Alan Viverettea54956a2015-01-07 16:05:02 -0800563 public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
564 super.onInitializeAccessibilityEventInternal(event);
Svetoslav Ganov736c2752011-04-22 18:30:36 -0700565 event.setChecked(mChecked);
svetoslavganov75986cf2009-05-14 22:28:01 -0700566 }
Svetoslav Ganov76502592011-07-29 10:44:59 -0700567
Alan Viverettea54956a2015-01-07 16:05:02 -0800568 /** @hide */
Svetoslav Ganov76502592011-07-29 10:44:59 -0700569 @Override
Alan Viverettea54956a2015-01-07 16:05:02 -0800570 public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
571 super.onInitializeAccessibilityNodeInfoInternal(info);
Svetoslav Ganovc85015c2012-04-30 11:37:43 -0700572 info.setCheckable(true);
Svetoslav Ganov34ffaab2011-09-12 16:19:32 -0700573 info.setChecked(mChecked);
574 }
Siva Velusamy94a6d152015-05-05 15:07:00 -0700575
576 /** @hide */
577 @Override
578 protected void encodeProperties(@NonNull ViewHierarchyEncoder stream) {
579 super.encodeProperties(stream);
580 stream.addProperty("text:checked", isChecked());
581 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800582}