8

I was debugging my App and found while hovering on an ImageView reference that, it is an AppCompatImageView instead of an ImageView. The same happened with a TextView(with AppCompatTextView).

enter image description here

While I don't particularly have a problem with this behavior because its AppCompat after all but when inspecting the code of fellow developers, I saw, extends Activity instead of AppCompatActivity and I almost marked it as a "bad practice".

On the other hand, while working on vector images, I was using an ImageView and there was a problem because I hadn't used an AppCompatImageView and using it was the solution:

ImageView not displaying correctly in the device

This inconsistent behavior has really confused me as to the practices I should follow. Should I just extend from an Activity from now on?

Manish Kumar Sharma
  • 12,982
  • 9
  • 58
  • 105
  • 1
    An `AppCompatActivity` will automatically create the `AppCompat*` versions of certain platform `View`s when its inflating its lay out. A regular `Activity` will not. If you're using any appcompat components, stick with `AppCompatActivity`. – Mike M. Aug 17 '17 at 00:43
  • **To the downvoter:** Could you kindly explain the reasoning for downvoting? – Manish Kumar Sharma Aug 23 '17 at 11:16
  • LayoutInflater uses reflection to dynamically return the view. Activity may generate AppCompat views because of AppCompat styles and themes. – Jon Goodwin Aug 23 '17 at 12:47
  • 1
    Couldn't get what's your concern. Do you see problems using `AppCompatImageView`? If no, than what's your question? – azizbekian Aug 24 '17 at 07:19
  • @MikeM.If you use a `AppCompatActivity` but your views in the java file and xml layout are not the AppCompat versions, will the platform convert those to AppCompat versions? Or do you need to define AppCompat in the xml layout and/or java file? – Micro Jun 03 '18 at 22:00
  • Old question, however I stumble on it because my app now crashes with this: androidx.appcompat.widget.AppCompatImageView can't use method with RemoteViews: setImageResource(int). My activity is an AppCompatActivity however it creates a RemoteViews and result in this issue, any way to solve? – 3c71 Mar 03 '22 at 07:08

3 Answers3

8

Short answer to "Should I just extend from an Activity from now on?" is no, you should keep extending AppCompatActivity as it provides backwards compatible features to older devices. In the case of AppCompatImageView:

A ImageView which supports compatible features on older versions of the platform, including:

  • Allows dynamic tint of its background via the background tint methods in ViewCompat.
  • Allows setting of the background tint using backgroundTint and backgroundTintMode.
  • Allows dynamic tint of its image via the image tint methods in ImageViewCompat.
  • Allows setting of the image tint using tint and tintMode.

Also, it adds compatibility with vector drawables for older Android versions.

Explanation about the inconsistencies

As it is explained in AppCompatImageView:

This will automatically be used when you use ImageView in your layouts and the top-level activity / dialog is provided by appcompat.

So, it's not unexpected.

How it works

AppCompatActivity installs a LayoutInflater.Factory2 to intercept the inflation of certain views. The code of this inflater can be seen in AppCompatViewInflater.java.

The function responsible for creating the Views is AppCompatViewInflater#createView(View, String, Context, AttributeSet, boolean, boolean, boolean, boolean), and as you can see here it checks for simple view names (without the package prefixing it), and creates the AppCompat* version instead:

public final View createView(View parent, final String name, @NonNull Context context,
        @NonNull AttributeSet attrs, boolean inheritContext,
        boolean readAndroidTheme, boolean readAppTheme, boolean wrapContext) {
    final Context originalContext = context;

    // ...

    View view = null;

    // We need to 'inject' our tint aware Views in place of the standard framework versions
    switch (name) {
        case "TextView":
            view = new AppCompatTextView(context, attrs);
            break;
        case "ImageView":
            view = new AppCompatImageView(context, attrs);
            break;
        case "Button":
            view = new AppCompatButton(context, attrs);
            break;
        case "EditText":
            view = new AppCompatEditText(context, attrs);
            break;
        case "Spinner":
            view = new AppCompatSpinner(context, attrs);
            break;
        case "ImageButton":
            view = new AppCompatImageButton(context, attrs);
            break;
        case "CheckBox":
            view = new AppCompatCheckBox(context, attrs);
            break;
        case "RadioButton":
            view = new AppCompatRadioButton(context, attrs);
            break;
        case "CheckedTextView":
            view = new AppCompatCheckedTextView(context, attrs);
            break;
        case "AutoCompleteTextView":
            view = new AppCompatAutoCompleteTextView(context, attrs);
            break;
        case "MultiAutoCompleteTextView":
            view = new AppCompatMultiAutoCompleteTextView(context, attrs);
            break;
        case "RatingBar":
            view = new AppCompatRatingBar(context, attrs);
            break;
        case "SeekBar":
            view = new AppCompatSeekBar(context, attrs);
            break;
    }

    if (view == null && originalContext != context) {
        // If the original context does not equal our themed context, then we need to manually
        // inflate it using the name so that android:theme takes effect.
        view = createViewFromTag(context, name, attrs);
    }

    // ...

    return view;
}

Forcing the usage of non-AppCompat views

So, in order to force the creation of a regular ImageView (no AppCompatImageView) while still using AppCompatActivity you need to specify the complete class name, for example:

    <android.widget.ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@mipmap/test"/>

For more information on how layout inflation works you can see the amazing talk "LayoutInflater: Friend or Foe?" by Chris Jenx, author of Calligraphy.

Xavier Rubio Jansana
  • 6,388
  • 1
  • 27
  • 50
1

Should I just extend from an Activity from now on?

No.The difference between a normal component (Activity) or Compat Component (AppCompatActivity) is that Compat components are designed to support the latest UI components in legacy devices . That is it provides backward compatibility , so you will need it if you're supporting a large variety of devices.

While I don't particularly have a problem with this behavior because its AppCompat after all

yes you are right , when using an Image view from inside a AppCompatActivity , a normal image view will be converted as AppCompatImageView.

AppCompatImageView

Follow this link to read more about AppCompatImageView.

Sree Vishnu
  • 385
  • 2
  • 13
-1

AppCompatImageView works the same as ImageView. The support library AppCompat is just for backwards compatibility. So if you want your app to be backwards compatible you should extend the AppCompat class.

Joshua
  • 589
  • 1
  • 5
  • 19
  • See the question I posted. It seems to make a lot of difference using AppCompat vs normal in some cases. – Manish Kumar Sharma Aug 16 '17 at 10:37
  • Have you noticed any difference when using AppCompat or Activity? – Joshua Aug 16 '17 at 10:40
  • I haven't actually used just Activity as Google recommends using AppCompatActivity but with ImageView, my previous App was crashing when working with vectors. The problem is the risk that you would have to be willing to take to find out if there is any difference between the two, I can't afford to take that big risk due to lack of time. – Manish Kumar Sharma Aug 16 '17 at 10:47
  • You could just go with Activity instead!, but I think you were using AppCompat in your layout file and not using it in your java file or vice versa, probably that's why it crashed. – Joshua Aug 16 '17 at 10:53
  • No, I spent like 20 hrs debugging it and I am sure it happened because of `ImageView`. Moreover, many other on SO also testified it and recommended using `AppCompatImageView`. – Manish Kumar Sharma Aug 16 '17 at 10:55
  • But I mostly just use **ImageView** instead of **AppCompatImageView** – Joshua Aug 16 '17 at 11:03
  • Doesn't answer the original question. – Xavier Rubio Jansana Aug 29 '17 at 14:19