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; 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.util.AttributeSet; 26 import android.view.accessibility.AccessibilityEvent; 27 import android.view.accessibility.AccessibilityNodeInfo; 28 import android.widget.Button; 29 import android.widget.TextView; 30 31 import androidx.annotation.DrawableRes; 32 import androidx.annotation.NonNull; 33 import androidx.annotation.Nullable; 34 import androidx.annotation.RestrictTo; 35 import androidx.appcompat.R; 36 import androidx.core.view.TintableBackgroundView; 37 import androidx.core.widget.AutoSizeableTextView; 38 import androidx.core.widget.TextViewCompat; 39 40 /** 41 * A {@link Button} which supports compatible features on older versions of the platform, 42 * including: 43 * <ul> 44 * <li>Allows dynamic tint of its background via the background tint methods in 45 * {@link androidx.core.view.ViewCompat}.</li> 46 * <li>Allows setting of the background tint using {@link R.attr#backgroundTint} and 47 * {@link R.attr#backgroundTintMode}.</li> 48 * </ul> 49 * 50 * <p>This will automatically be used when you use {@link Button} in your layouts 51 * and the top-level activity / dialog is provided by 52 * <a href="{@docRoot}topic/libraries/support-library/packages.html#v7-appcompat">appcompat</a>. 53 * You should only need to manually use this class when writing custom views.</p> 54 */ 55 public class AppCompatButton extends Button implements TintableBackgroundView, 56 AutoSizeableTextView { 57 58 private final AppCompatBackgroundHelper mBackgroundTintHelper; 59 private final AppCompatTextHelper mTextHelper; 60 AppCompatButton(Context context)61 public AppCompatButton(Context context) { 62 this(context, null); 63 } 64 AppCompatButton(Context context, AttributeSet attrs)65 public AppCompatButton(Context context, AttributeSet attrs) { 66 this(context, attrs, R.attr.buttonStyle); 67 } 68 AppCompatButton(Context context, AttributeSet attrs, int defStyleAttr)69 public AppCompatButton(Context context, AttributeSet attrs, int defStyleAttr) { 70 super(TintContextWrapper.wrap(context), attrs, defStyleAttr); 71 72 mBackgroundTintHelper = new AppCompatBackgroundHelper(this); 73 mBackgroundTintHelper.loadFromAttributes(attrs, defStyleAttr); 74 75 mTextHelper = new AppCompatTextHelper(this); 76 mTextHelper.loadFromAttributes(attrs, defStyleAttr); 77 mTextHelper.applyCompoundDrawablesTints(); 78 } 79 80 @Override setBackgroundResource(@rawableRes int resId)81 public void setBackgroundResource(@DrawableRes int resId) { 82 super.setBackgroundResource(resId); 83 if (mBackgroundTintHelper != null) { 84 mBackgroundTintHelper.onSetBackgroundResource(resId); 85 } 86 } 87 88 @Override setBackgroundDrawable(Drawable background)89 public void setBackgroundDrawable(Drawable background) { 90 super.setBackgroundDrawable(background); 91 if (mBackgroundTintHelper != null) { 92 mBackgroundTintHelper.onSetBackgroundDrawable(background); 93 } 94 } 95 96 /** 97 * This should be accessed via 98 * {@link androidx.core.view.ViewCompat#setBackgroundTintList(android.view.View, ColorStateList)} 99 * 100 * @hide 101 */ 102 @RestrictTo(LIBRARY_GROUP) 103 @Override setSupportBackgroundTintList(@ullable ColorStateList tint)104 public void setSupportBackgroundTintList(@Nullable ColorStateList tint) { 105 if (mBackgroundTintHelper != null) { 106 mBackgroundTintHelper.setSupportBackgroundTintList(tint); 107 } 108 } 109 110 /** 111 * This should be accessed via 112 * {@link androidx.core.view.ViewCompat#getBackgroundTintList(android.view.View)} 113 * 114 * @hide 115 */ 116 @RestrictTo(LIBRARY_GROUP) 117 @Override 118 @Nullable getSupportBackgroundTintList()119 public ColorStateList getSupportBackgroundTintList() { 120 return mBackgroundTintHelper != null 121 ? mBackgroundTintHelper.getSupportBackgroundTintList() : null; 122 } 123 124 /** 125 * This should be accessed via 126 * {@link androidx.core.view.ViewCompat#setBackgroundTintMode(android.view.View, PorterDuff.Mode)} 127 * 128 * @hide 129 */ 130 @RestrictTo(LIBRARY_GROUP) 131 @Override setSupportBackgroundTintMode(@ullable PorterDuff.Mode tintMode)132 public void setSupportBackgroundTintMode(@Nullable PorterDuff.Mode tintMode) { 133 if (mBackgroundTintHelper != null) { 134 mBackgroundTintHelper.setSupportBackgroundTintMode(tintMode); 135 } 136 } 137 138 /** 139 * This should be accessed via 140 * {@link androidx.core.view.ViewCompat#getBackgroundTintMode(android.view.View)} 141 * 142 * @hide 143 */ 144 @RestrictTo(LIBRARY_GROUP) 145 @Override 146 @Nullable getSupportBackgroundTintMode()147 public PorterDuff.Mode getSupportBackgroundTintMode() { 148 return mBackgroundTintHelper != null 149 ? mBackgroundTintHelper.getSupportBackgroundTintMode() : null; 150 } 151 152 @Override drawableStateChanged()153 protected void drawableStateChanged() { 154 super.drawableStateChanged(); 155 if (mBackgroundTintHelper != null) { 156 mBackgroundTintHelper.applySupportBackgroundTint(); 157 } 158 if (mTextHelper != null) { 159 mTextHelper.applyCompoundDrawablesTints(); 160 } 161 } 162 163 @Override setTextAppearance(Context context, int resId)164 public void setTextAppearance(Context context, int resId) { 165 super.setTextAppearance(context, resId); 166 if (mTextHelper != null) { 167 mTextHelper.onSetTextAppearance(context, resId); 168 } 169 } 170 171 @Override onInitializeAccessibilityEvent(AccessibilityEvent event)172 public void onInitializeAccessibilityEvent(AccessibilityEvent event) { 173 super.onInitializeAccessibilityEvent(event); 174 event.setClassName(Button.class.getName()); 175 } 176 177 @Override onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info)178 public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { 179 super.onInitializeAccessibilityNodeInfo(info); 180 info.setClassName(Button.class.getName()); 181 } 182 183 @Override onLayout(boolean changed, int left, int top, int right, int bottom)184 protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 185 super.onLayout(changed, left, top, right, bottom); 186 if (mTextHelper != null) { 187 mTextHelper.onLayout(changed, left, top, right, bottom); 188 } 189 } 190 191 @Override setTextSize(int unit, float size)192 public void setTextSize(int unit, float size) { 193 if (PLATFORM_SUPPORTS_AUTOSIZE) { 194 super.setTextSize(unit, size); 195 } else { 196 if (mTextHelper != null) { 197 mTextHelper.setTextSize(unit, size); 198 } 199 } 200 } 201 202 @Override onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter)203 protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) { 204 super.onTextChanged(text, start, lengthBefore, lengthAfter); 205 if (mTextHelper != null && !PLATFORM_SUPPORTS_AUTOSIZE && mTextHelper.isAutoSizeEnabled()) { 206 mTextHelper.autoSizeText(); 207 } 208 } 209 210 /** 211 * @hide 212 */ 213 @RestrictTo(LIBRARY_GROUP) 214 @Override setAutoSizeTextTypeWithDefaults( @extViewCompat.AutoSizeTextType int autoSizeTextType)215 public void setAutoSizeTextTypeWithDefaults( 216 @TextViewCompat.AutoSizeTextType int autoSizeTextType) { 217 if (PLATFORM_SUPPORTS_AUTOSIZE) { 218 super.setAutoSizeTextTypeWithDefaults(autoSizeTextType); 219 } else { 220 if (mTextHelper != null) { 221 mTextHelper.setAutoSizeTextTypeWithDefaults(autoSizeTextType); 222 } 223 } 224 } 225 226 /** 227 * @hide 228 */ 229 @RestrictTo(LIBRARY_GROUP) 230 @Override setAutoSizeTextTypeUniformWithConfiguration( int autoSizeMinTextSize, int autoSizeMaxTextSize, int autoSizeStepGranularity, int unit)231 public void setAutoSizeTextTypeUniformWithConfiguration( 232 int autoSizeMinTextSize, 233 int autoSizeMaxTextSize, 234 int autoSizeStepGranularity, 235 int unit) throws IllegalArgumentException { 236 if (PLATFORM_SUPPORTS_AUTOSIZE) { 237 super.setAutoSizeTextTypeUniformWithConfiguration( 238 autoSizeMinTextSize, autoSizeMaxTextSize, autoSizeStepGranularity, unit); 239 } else { 240 if (mTextHelper != null) { 241 mTextHelper.setAutoSizeTextTypeUniformWithConfiguration( 242 autoSizeMinTextSize, autoSizeMaxTextSize, autoSizeStepGranularity, unit); 243 } 244 } 245 } 246 247 /** 248 * @hide 249 */ 250 @RestrictTo(LIBRARY_GROUP) 251 @Override setAutoSizeTextTypeUniformWithPresetSizes(@onNull int[] presetSizes, int unit)252 public void setAutoSizeTextTypeUniformWithPresetSizes(@NonNull int[] presetSizes, int unit) 253 throws IllegalArgumentException { 254 if (PLATFORM_SUPPORTS_AUTOSIZE) { 255 super.setAutoSizeTextTypeUniformWithPresetSizes(presetSizes, unit); 256 } else { 257 if (mTextHelper != null) { 258 mTextHelper.setAutoSizeTextTypeUniformWithPresetSizes(presetSizes, unit); 259 } 260 } 261 } 262 263 /** 264 * @hide 265 */ 266 @RestrictTo(LIBRARY_GROUP) 267 @Override 268 @TextViewCompat.AutoSizeTextType getAutoSizeTextType()269 public int getAutoSizeTextType() { 270 if (PLATFORM_SUPPORTS_AUTOSIZE) { 271 return super.getAutoSizeTextType() == TextView.AUTO_SIZE_TEXT_TYPE_UNIFORM 272 ? TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM 273 : TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE; 274 } else { 275 if (mTextHelper != null) { 276 return mTextHelper.getAutoSizeTextType(); 277 } 278 } 279 return TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE; 280 } 281 282 /** 283 * @hide 284 */ 285 @RestrictTo(LIBRARY_GROUP) 286 @Override getAutoSizeStepGranularity()287 public int getAutoSizeStepGranularity() { 288 if (PLATFORM_SUPPORTS_AUTOSIZE) { 289 return super.getAutoSizeStepGranularity(); 290 } else { 291 if (mTextHelper != null) { 292 return mTextHelper.getAutoSizeStepGranularity(); 293 } 294 } 295 return -1; 296 } 297 298 /** 299 * @hide 300 */ 301 @RestrictTo(LIBRARY_GROUP) 302 @Override getAutoSizeMinTextSize()303 public int getAutoSizeMinTextSize() { 304 if (PLATFORM_SUPPORTS_AUTOSIZE) { 305 return super.getAutoSizeMinTextSize(); 306 } else { 307 if (mTextHelper != null) { 308 return mTextHelper.getAutoSizeMinTextSize(); 309 } 310 } 311 return -1; 312 } 313 314 /** 315 * @hide 316 */ 317 @RestrictTo(LIBRARY_GROUP) 318 @Override getAutoSizeMaxTextSize()319 public int getAutoSizeMaxTextSize() { 320 if (PLATFORM_SUPPORTS_AUTOSIZE) { 321 return super.getAutoSizeMaxTextSize(); 322 } else { 323 if (mTextHelper != null) { 324 return mTextHelper.getAutoSizeMaxTextSize(); 325 } 326 } 327 return -1; 328 } 329 330 /** 331 * @hide 332 */ 333 @RestrictTo(LIBRARY_GROUP) 334 @Override getAutoSizeTextAvailableSizes()335 public int[] getAutoSizeTextAvailableSizes() { 336 if (PLATFORM_SUPPORTS_AUTOSIZE) { 337 return super.getAutoSizeTextAvailableSizes(); 338 } else { 339 if (mTextHelper != null) { 340 return mTextHelper.getAutoSizeTextAvailableSizes(); 341 } 342 } 343 return new int[0]; 344 } 345 346 /** 347 * Sets the properties of this field to transform input to ALL CAPS 348 * display. This may use a "small caps" formatting if available. 349 * This setting will be ignored if this field is editable or selectable. 350 * 351 * This call replaces the current transformation method. Disabling this 352 * will not necessarily restore the previous behavior from before this 353 * was enabled. 354 */ setSupportAllCaps(boolean allCaps)355 public void setSupportAllCaps(boolean allCaps) { 356 if (mTextHelper != null) { 357 mTextHelper.setAllCaps(allCaps); 358 } 359 } 360 } 361