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