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