1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package androidx.appcompat.widget; 18 19 import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX; 20 21 import android.content.Context; 22 import android.content.res.ColorStateList; 23 import android.graphics.PorterDuff; 24 import android.graphics.drawable.Drawable; 25 import android.text.InputFilter; 26 import android.util.AttributeSet; 27 import android.view.View; 28 import android.widget.CheckBox; 29 import android.widget.TextView; 30 31 import androidx.annotation.DrawableRes; 32 import androidx.annotation.RestrictTo; 33 import androidx.appcompat.R; 34 import androidx.appcompat.content.res.AppCompatResources; 35 import androidx.core.view.TintableBackgroundView; 36 import androidx.core.view.ViewCompat; 37 import androidx.core.widget.TintableCompoundButton; 38 import androidx.core.widget.TintableCompoundDrawablesView; 39 import androidx.resourceinspection.annotation.AppCompatShadowedAttributes; 40 41 import org.jspecify.annotations.NonNull; 42 import org.jspecify.annotations.Nullable; 43 44 /** 45 * A {@link CheckBox} which supports compatible features on older versions of the platform, 46 * including: 47 * <ul> 48 * <li>Allows dynamic tint of its background via the background tint methods in 49 * {@link androidx.core.widget.CompoundButtonCompat}.</li> 50 * <li>Allows setting of the background tint using {@link R.attr#buttonTint} and 51 * {@link R.attr#buttonTintMode}.</li> 52 * </ul> 53 * 54 * <p>This will automatically be used when you use {@link CheckBox} in your layouts 55 * and the top-level activity / dialog is provided by 56 * <a href="{@docRoot}topic/libraries/support-library/packages.html#v7-appcompat">appcompat</a>. 57 * You should only need to manually use this class when writing custom views.</p> 58 */ 59 @AppCompatShadowedAttributes 60 public class AppCompatCheckBox extends CheckBox implements TintableCompoundButton, 61 TintableBackgroundView, EmojiCompatConfigurationView, TintableCompoundDrawablesView { 62 63 private final AppCompatCompoundButtonHelper mCompoundButtonHelper; 64 private final AppCompatBackgroundHelper mBackgroundTintHelper; 65 private final AppCompatTextHelper mTextHelper; 66 private AppCompatEmojiTextHelper mAppCompatEmojiTextHelper; 67 AppCompatCheckBox(@onNull Context context)68 public AppCompatCheckBox(@NonNull Context context) { 69 this(context, null); 70 } 71 AppCompatCheckBox(@onNull Context context, @Nullable AttributeSet attrs)72 public AppCompatCheckBox(@NonNull Context context, @Nullable AttributeSet attrs) { 73 this(context, attrs, R.attr.checkboxStyle); 74 } 75 AppCompatCheckBox( @onNull Context context, @Nullable AttributeSet attrs, int defStyleAttr)76 public AppCompatCheckBox( 77 @NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { 78 super(TintContextWrapper.wrap(context), attrs, defStyleAttr); 79 80 ThemeUtils.checkAppCompatTheme(this, getContext()); 81 82 mCompoundButtonHelper = new AppCompatCompoundButtonHelper(this); 83 mCompoundButtonHelper.loadFromAttributes(attrs, defStyleAttr); 84 85 mBackgroundTintHelper = new AppCompatBackgroundHelper(this); 86 mBackgroundTintHelper.loadFromAttributes(attrs, defStyleAttr); 87 88 mTextHelper = new AppCompatTextHelper(this); 89 mTextHelper.loadFromAttributes(attrs, defStyleAttr); 90 91 AppCompatEmojiTextHelper emojiTextViewHelper = getEmojiTextViewHelper(); 92 emojiTextViewHelper.loadFromAttributes(attrs, defStyleAttr); 93 } 94 95 /** 96 * This may be called from super constructors. 97 */ getEmojiTextViewHelper()98 private @NonNull AppCompatEmojiTextHelper getEmojiTextViewHelper() { 99 if (mAppCompatEmojiTextHelper == null) { 100 mAppCompatEmojiTextHelper = new AppCompatEmojiTextHelper(this); 101 } 102 return mAppCompatEmojiTextHelper; 103 } 104 105 @Override setButtonDrawable(Drawable buttonDrawable)106 public void setButtonDrawable(Drawable buttonDrawable) { 107 super.setButtonDrawable(buttonDrawable); 108 if (mCompoundButtonHelper != null) { 109 mCompoundButtonHelper.onSetButtonDrawable(); 110 } 111 } 112 113 @Override setButtonDrawable(@rawableRes int resId)114 public void setButtonDrawable(@DrawableRes int resId) { 115 setButtonDrawable(AppCompatResources.getDrawable(getContext(), resId)); 116 } 117 118 /** 119 * This should be accessed from {@link androidx.core.widget.CompoundButtonCompat} 120 */ 121 @RestrictTo(LIBRARY_GROUP_PREFIX) 122 @Override setSupportButtonTintList(@ullable ColorStateList tint)123 public void setSupportButtonTintList(@Nullable ColorStateList tint) { 124 if (mCompoundButtonHelper != null) { 125 mCompoundButtonHelper.setSupportButtonTintList(tint); 126 } 127 } 128 129 /** 130 * This should be accessed from {@link androidx.core.widget.CompoundButtonCompat} 131 */ 132 @RestrictTo(LIBRARY_GROUP_PREFIX) 133 @Override getSupportButtonTintList()134 public @Nullable ColorStateList getSupportButtonTintList() { 135 return mCompoundButtonHelper != null 136 ? mCompoundButtonHelper.getSupportButtonTintList() 137 : null; 138 } 139 140 /** 141 * This should be accessed from {@link androidx.core.widget.CompoundButtonCompat} 142 */ 143 @RestrictTo(LIBRARY_GROUP_PREFIX) 144 @Override setSupportButtonTintMode(PorterDuff.@ullable Mode tintMode)145 public void setSupportButtonTintMode(PorterDuff.@Nullable Mode tintMode) { 146 if (mCompoundButtonHelper != null) { 147 mCompoundButtonHelper.setSupportButtonTintMode(tintMode); 148 } 149 } 150 151 /** 152 * This should be accessed from {@link androidx.core.widget.CompoundButtonCompat} 153 */ 154 @RestrictTo(LIBRARY_GROUP_PREFIX) 155 @Override getSupportButtonTintMode()156 public PorterDuff.@Nullable Mode getSupportButtonTintMode() { 157 return mCompoundButtonHelper != null 158 ? mCompoundButtonHelper.getSupportButtonTintMode() 159 : null; 160 } 161 162 /** 163 * This should be accessed via 164 * {@link ViewCompat#setBackgroundTintList(View, ColorStateList)} 165 */ 166 @RestrictTo(LIBRARY_GROUP_PREFIX) 167 @Override setSupportBackgroundTintList(@ullable ColorStateList tint)168 public void setSupportBackgroundTintList(@Nullable ColorStateList tint) { 169 if (mBackgroundTintHelper != null) { 170 mBackgroundTintHelper.setSupportBackgroundTintList(tint); 171 } 172 } 173 174 /** 175 * This should be accessed via 176 * {@link androidx.core.view.ViewCompat#getBackgroundTintList(android.view.View)} 177 * 178 */ 179 @RestrictTo(LIBRARY_GROUP_PREFIX) 180 @Override getSupportBackgroundTintList()181 public @Nullable ColorStateList getSupportBackgroundTintList() { 182 return mBackgroundTintHelper != null 183 ? mBackgroundTintHelper.getSupportBackgroundTintList() : null; 184 } 185 186 /** 187 * This should be accessed via 188 * {@link ViewCompat#setBackgroundTintMode(View, PorterDuff.Mode)} 189 * 190 */ 191 @RestrictTo(LIBRARY_GROUP_PREFIX) 192 @Override setSupportBackgroundTintMode(PorterDuff.@ullable Mode tintMode)193 public void setSupportBackgroundTintMode(PorterDuff.@Nullable Mode tintMode) { 194 if (mBackgroundTintHelper != null) { 195 mBackgroundTintHelper.setSupportBackgroundTintMode(tintMode); 196 } 197 } 198 199 /** 200 * This should be accessed via 201 * {@link androidx.core.view.ViewCompat#getBackgroundTintMode(android.view.View)} 202 * 203 */ 204 @RestrictTo(LIBRARY_GROUP_PREFIX) 205 @Override getSupportBackgroundTintMode()206 public PorterDuff.@Nullable Mode getSupportBackgroundTintMode() { 207 return mBackgroundTintHelper != null 208 ? mBackgroundTintHelper.getSupportBackgroundTintMode() : null; 209 } 210 211 @Override setBackgroundDrawable(@ullable Drawable background)212 public void setBackgroundDrawable(@Nullable Drawable background) { 213 super.setBackgroundDrawable(background); 214 if (mBackgroundTintHelper != null) { 215 mBackgroundTintHelper.onSetBackgroundDrawable(background); 216 } 217 } 218 219 @Override setBackgroundResource(@rawableRes int resId)220 public void setBackgroundResource(@DrawableRes int resId) { 221 super.setBackgroundResource(resId); 222 if (mBackgroundTintHelper != null) { 223 mBackgroundTintHelper.onSetBackgroundResource(resId); 224 } 225 } 226 227 @Override drawableStateChanged()228 protected void drawableStateChanged() { 229 super.drawableStateChanged(); 230 if (mBackgroundTintHelper != null) { 231 mBackgroundTintHelper.applySupportBackgroundTint(); 232 } 233 if (mTextHelper != null) { 234 mTextHelper.applyCompoundDrawablesTints(); 235 } 236 } 237 238 @Override setFilters(@uppressWarnings"ArrayReturn") InputFilter @onNull [] filters)239 public void setFilters(@SuppressWarnings("ArrayReturn") InputFilter @NonNull [] filters) { 240 super.setFilters(getEmojiTextViewHelper().getFilters(filters)); 241 } 242 243 @Override setAllCaps(boolean allCaps)244 public void setAllCaps(boolean allCaps) { 245 super.setAllCaps(allCaps); 246 getEmojiTextViewHelper().setAllCaps(allCaps); 247 } 248 249 @Override setEmojiCompatEnabled(boolean enabled)250 public void setEmojiCompatEnabled(boolean enabled) { 251 getEmojiTextViewHelper().setEnabled(enabled); 252 } 253 254 @Override isEmojiCompatEnabled()255 public boolean isEmojiCompatEnabled() { 256 return getEmojiTextViewHelper().isEnabled(); 257 } 258 259 @Override setCompoundDrawables(@ullable Drawable left, @Nullable Drawable top, @Nullable Drawable right, @Nullable Drawable bottom)260 public void setCompoundDrawables(@Nullable Drawable left, @Nullable Drawable top, 261 @Nullable Drawable right, @Nullable Drawable bottom) { 262 super.setCompoundDrawables(left, top, right, bottom); 263 if (mTextHelper != null) { 264 mTextHelper.onSetCompoundDrawables(); 265 } 266 } 267 268 @Override setCompoundDrawablesRelative(@ullable Drawable start, @Nullable Drawable top, @Nullable Drawable end, @Nullable Drawable bottom)269 public void setCompoundDrawablesRelative(@Nullable Drawable start, @Nullable Drawable top, 270 @Nullable Drawable end, @Nullable Drawable bottom) { 271 super.setCompoundDrawablesRelative(start, top, end, bottom); 272 if (mTextHelper != null) { 273 mTextHelper.onSetCompoundDrawables(); 274 } 275 } 276 277 /** 278 * This should be accessed via 279 * {@link androidx.core.widget.TextViewCompat#getCompoundDrawableTintList(TextView)} 280 * 281 * @return the tint applied to the compound drawables 282 * @attr ref androidx.appcompat.R.styleable#AppCompatTextView_drawableTint 283 * @see #setSupportCompoundDrawablesTintList(ColorStateList) 284 * 285 */ 286 @Override 287 @RestrictTo(LIBRARY_GROUP_PREFIX) getSupportCompoundDrawablesTintList()288 public @Nullable ColorStateList getSupportCompoundDrawablesTintList() { 289 return mTextHelper.getCompoundDrawableTintList(); 290 } 291 292 /** 293 * This should be accessed via {@link 294 * androidx.core.widget.TextViewCompat#setCompoundDrawableTintList(TextView, ColorStateList)} 295 * 296 * Applies a tint to the compound drawables. Does not modify the current tint mode, which is 297 * {@link PorterDuff.Mode#SRC_IN} by default. 298 * <p> 299 * Subsequent calls to {@link #setCompoundDrawables(Drawable, Drawable, Drawable, Drawable)} and 300 * related methods will automatically mutate the drawables and apply the specified tint and tint 301 * mode using {@link Drawable#setTintList(ColorStateList)}. 302 * 303 * @param tintList the tint to apply, may be {@code null} to clear tint 304 * @attr ref androidx.appcompat.R.styleable#AppCompatTextView_drawableTint 305 * @see #getSupportCompoundDrawablesTintList() 306 * 307 */ 308 @Override 309 @RestrictTo(LIBRARY_GROUP_PREFIX) setSupportCompoundDrawablesTintList(@ullable ColorStateList tintList)310 public void setSupportCompoundDrawablesTintList(@Nullable ColorStateList tintList) { 311 mTextHelper.setCompoundDrawableTintList(tintList); 312 mTextHelper.applyCompoundDrawablesTints(); 313 } 314 315 /** 316 * This should be accessed via 317 * {@link androidx.core.widget.TextViewCompat#getCompoundDrawableTintMode(TextView)} 318 * 319 * Returns the blending mode used to apply the tint to the compound drawables, if specified. 320 * 321 * @return the blending mode used to apply the tint to the compound drawables 322 * @attr ref androidx.appcompat.R.styleable#AppCompatTextView_drawableTintMode 323 * @see #setSupportCompoundDrawablesTintMode(PorterDuff.Mode) 324 * 325 */ 326 @Override 327 @RestrictTo(LIBRARY_GROUP_PREFIX) getSupportCompoundDrawablesTintMode()328 public PorterDuff.@Nullable Mode getSupportCompoundDrawablesTintMode() { 329 return mTextHelper.getCompoundDrawableTintMode(); 330 } 331 332 /** 333 * This should be accessed via {@link 334 * androidx.core.widget.TextViewCompat#setCompoundDrawableTintMode(TextView, PorterDuff.Mode)} 335 * 336 * Specifies the blending mode used to apply the tint specified by 337 * {@link #setSupportCompoundDrawablesTintList(ColorStateList)} to the compound drawables. The 338 * default mode is {@link PorterDuff.Mode#SRC_IN}. 339 * 340 * @param tintMode the blending mode used to apply the tint, may be {@code null} to clear tint 341 * @attr ref androidx.appcompat.R.styleable#AppCompatTextView_drawableTintMode 342 * @see #setSupportCompoundDrawablesTintList(ColorStateList) 343 * 344 */ 345 @Override 346 @RestrictTo(LIBRARY_GROUP_PREFIX) setSupportCompoundDrawablesTintMode(PorterDuff.@ullable Mode tintMode)347 public void setSupportCompoundDrawablesTintMode(PorterDuff.@Nullable Mode tintMode) { 348 mTextHelper.setCompoundDrawableTintMode(tintMode); 349 mTextHelper.applyCompoundDrawablesTints(); 350 } 351 } 352