1 /* 2 * Copyright (C) 2015 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.Bitmap; 24 import android.graphics.PorterDuff; 25 import android.graphics.drawable.Drawable; 26 import android.net.Uri; 27 import android.util.AttributeSet; 28 import android.widget.ImageButton; 29 import android.widget.ImageView; 30 31 import androidx.annotation.DrawableRes; 32 import androidx.annotation.RestrictTo; 33 import androidx.appcompat.R; 34 import androidx.core.view.TintableBackgroundView; 35 import androidx.core.widget.ImageViewCompat; 36 import androidx.core.widget.TintableImageSourceView; 37 import androidx.resourceinspection.annotation.AppCompatShadowedAttributes; 38 39 import org.jspecify.annotations.NonNull; 40 import org.jspecify.annotations.Nullable; 41 42 /** 43 * A {@link ImageButton} which supports compatible features on older versions of the platform, 44 * including: 45 * <ul> 46 * <li>Allows dynamic tint of its background via the background tint methods in 47 * {@link androidx.core.view.ViewCompat}.</li> 48 * <li>Allows setting of the background tint using {@link R.attr#backgroundTint} and 49 * {@link R.attr#backgroundTintMode}.</li> 50 * <li>Allows dynamic tint of its image via the image tint methods in 51 * {@link ImageViewCompat}.</li> 52 * <li>Allows setting of the image tint using {@link R.attr#tint} and 53 * {@link R.attr#tintMode}.</li> 54 * </ul> 55 * 56 * <p>This will automatically be used when you use {@link ImageButton} in your layouts 57 * and the top-level activity / dialog is provided by 58 * <a href="{@docRoot}topic/libraries/support-library/packages.html#v7-appcompat">appcompat</a>. 59 * You should only need to manually use this class when writing custom views.</p> 60 */ 61 @AppCompatShadowedAttributes 62 public class AppCompatImageButton extends ImageButton implements TintableBackgroundView, 63 TintableImageSourceView { 64 65 private final AppCompatBackgroundHelper mBackgroundTintHelper; 66 private final AppCompatImageHelper mImageHelper; 67 private boolean mHasLevel = false; 68 AppCompatImageButton(@onNull Context context)69 public AppCompatImageButton(@NonNull Context context) { 70 this(context, null); 71 } 72 AppCompatImageButton(@onNull Context context, @Nullable AttributeSet attrs)73 public AppCompatImageButton(@NonNull Context context, @Nullable AttributeSet attrs) { 74 this(context, attrs, R.attr.imageButtonStyle); 75 } 76 AppCompatImageButton( @onNull Context context, @Nullable AttributeSet attrs, int defStyleAttr)77 public AppCompatImageButton( 78 @NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { 79 super(TintContextWrapper.wrap(context), attrs, defStyleAttr); 80 81 ThemeUtils.checkAppCompatTheme(this, getContext()); 82 83 mBackgroundTintHelper = new AppCompatBackgroundHelper(this); 84 mBackgroundTintHelper.loadFromAttributes(attrs, defStyleAttr); 85 86 mImageHelper = new AppCompatImageHelper(this); 87 mImageHelper.loadFromAttributes(attrs, defStyleAttr); 88 } 89 90 @Override setImageResource(@rawableRes int resId)91 public void setImageResource(@DrawableRes int resId) { 92 // Intercept this call and instead retrieve the Drawable via the image helper 93 mImageHelper.setImageResource(resId); 94 } 95 96 @Override setImageDrawable(@ullable Drawable drawable)97 public void setImageDrawable(@Nullable Drawable drawable) { 98 if (mImageHelper != null && drawable != null && !mHasLevel) { 99 // If there is no level set already then obtain the level from the drawable 100 mImageHelper.obtainLevelFromDrawable(drawable); 101 } 102 super.setImageDrawable(drawable); 103 if (mImageHelper != null) { 104 mImageHelper.applySupportImageTint(); 105 if (!mHasLevel) { 106 // Apply the level from drawable 107 mImageHelper.applyImageLevel(); 108 } 109 } 110 } 111 112 @Override setImageBitmap(Bitmap bm)113 public void setImageBitmap(Bitmap bm) { 114 super.setImageBitmap(bm); 115 if (mImageHelper != null) { 116 mImageHelper.applySupportImageTint(); 117 } 118 } 119 120 @Override setImageURI(@ullable Uri uri)121 public void setImageURI(@Nullable Uri uri) { 122 super.setImageURI(uri); 123 if (mImageHelper != null) { 124 mImageHelper.applySupportImageTint(); 125 } 126 } 127 128 @Override setBackgroundResource(@rawableRes int resId)129 public void setBackgroundResource(@DrawableRes int resId) { 130 super.setBackgroundResource(resId); 131 if (mBackgroundTintHelper != null) { 132 mBackgroundTintHelper.onSetBackgroundResource(resId); 133 } 134 } 135 136 @Override setBackgroundDrawable(@ullable Drawable background)137 public void setBackgroundDrawable(@Nullable Drawable background) { 138 super.setBackgroundDrawable(background); 139 if (mBackgroundTintHelper != null) { 140 mBackgroundTintHelper.onSetBackgroundDrawable(background); 141 } 142 } 143 144 /** 145 * This should be accessed via 146 * {@link androidx.core.view.ViewCompat#setBackgroundTintList(android.view.View, ColorStateList)} 147 * 148 */ 149 @RestrictTo(LIBRARY_GROUP_PREFIX) 150 @Override setSupportBackgroundTintList(@ullable ColorStateList tint)151 public void setSupportBackgroundTintList(@Nullable ColorStateList tint) { 152 if (mBackgroundTintHelper != null) { 153 mBackgroundTintHelper.setSupportBackgroundTintList(tint); 154 } 155 } 156 157 /** 158 * This should be accessed via 159 * {@link androidx.core.view.ViewCompat#getBackgroundTintList(android.view.View)} 160 * 161 */ 162 @RestrictTo(LIBRARY_GROUP_PREFIX) 163 @Override getSupportBackgroundTintList()164 public @Nullable ColorStateList getSupportBackgroundTintList() { 165 return mBackgroundTintHelper != null 166 ? mBackgroundTintHelper.getSupportBackgroundTintList() : null; 167 } 168 169 /** 170 * This should be accessed via 171 * {@link androidx.core.view.ViewCompat#setBackgroundTintMode(android.view.View, PorterDuff.Mode)} 172 * 173 */ 174 @RestrictTo(LIBRARY_GROUP_PREFIX) 175 @Override setSupportBackgroundTintMode(PorterDuff.@ullable Mode tintMode)176 public void setSupportBackgroundTintMode(PorterDuff.@Nullable Mode tintMode) { 177 if (mBackgroundTintHelper != null) { 178 mBackgroundTintHelper.setSupportBackgroundTintMode(tintMode); 179 } 180 } 181 182 /** 183 * This should be accessed via 184 * {@link androidx.core.view.ViewCompat#getBackgroundTintMode(android.view.View)} 185 * 186 */ 187 @RestrictTo(LIBRARY_GROUP_PREFIX) 188 @Override getSupportBackgroundTintMode()189 public PorterDuff.@Nullable Mode getSupportBackgroundTintMode() { 190 return mBackgroundTintHelper != null 191 ? mBackgroundTintHelper.getSupportBackgroundTintMode() : null; 192 } 193 /** 194 * This should be accessed via 195 * {@link androidx.core.widget.ImageViewCompat#setImageTintList(ImageView, ColorStateList)} 196 * 197 */ 198 @RestrictTo(LIBRARY_GROUP_PREFIX) 199 @Override setSupportImageTintList(@ullable ColorStateList tint)200 public void setSupportImageTintList(@Nullable ColorStateList tint) { 201 if (mImageHelper != null) { 202 mImageHelper.setSupportImageTintList(tint); 203 } 204 } 205 206 /** 207 * This should be accessed via 208 * {@link androidx.core.widget.ImageViewCompat#getImageTintList(ImageView)} 209 * 210 */ 211 @RestrictTo(LIBRARY_GROUP_PREFIX) 212 @Override getSupportImageTintList()213 public @Nullable ColorStateList getSupportImageTintList() { 214 return mImageHelper != null 215 ? mImageHelper.getSupportImageTintList() : null; 216 } 217 218 /** 219 * This should be accessed via 220 * {@link androidx.core.widget.ImageViewCompat#setImageTintMode(ImageView, PorterDuff.Mode)} 221 * 222 */ 223 @RestrictTo(LIBRARY_GROUP_PREFIX) 224 @Override setSupportImageTintMode(PorterDuff.@ullable Mode tintMode)225 public void setSupportImageTintMode(PorterDuff.@Nullable Mode tintMode) { 226 if (mImageHelper != null) { 227 mImageHelper.setSupportImageTintMode(tintMode); 228 } 229 } 230 231 /** 232 * This should be accessed via 233 * {@link androidx.core.widget.ImageViewCompat#getImageTintMode(ImageView)} 234 * 235 */ 236 @RestrictTo(LIBRARY_GROUP_PREFIX) 237 @Override getSupportImageTintMode()238 public PorterDuff.@Nullable Mode getSupportImageTintMode() { 239 return mImageHelper != null 240 ? mImageHelper.getSupportImageTintMode() : null; 241 } 242 243 @Override drawableStateChanged()244 protected void drawableStateChanged() { 245 super.drawableStateChanged(); 246 if (mBackgroundTintHelper != null) { 247 mBackgroundTintHelper.applySupportBackgroundTint(); 248 } 249 if (mImageHelper != null) { 250 mImageHelper.applySupportImageTint(); 251 } 252 } 253 254 @Override hasOverlappingRendering()255 public boolean hasOverlappingRendering() { 256 return mImageHelper.hasOverlappingRendering() && super.hasOverlappingRendering(); 257 } 258 259 @Override setImageLevel(int level)260 public void setImageLevel(int level) { 261 super.setImageLevel(level); 262 mHasLevel = true; 263 } 264 } 265