当我没有明确声明它们时,为什么Android会选择AppCompat组件?

时间:2021-08-14 14:21:56

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).

我正在调试我的应用程序,并在将鼠标悬停在ImageView引用上时发现,它是一个AppCompatImageView而不是ImageView。 TextView(使用AppCompatTextView)也是如此。

当我没有明确声明它们时,为什么Android会选择AppCompat组件?

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".

虽然我对此行为没有特别的问题,因为它的AppCompat毕竟在检查其他开发人员的代码时,我看到,扩展Activity而不是AppCompatActivity,我几乎把它标记为“不好的做法”。

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并且存在一个问题,因为我没有使用AppCompatImageView并使用它是解决方案:

ImageView not displaying correctly in the device

ImageView无法在设备中正确显示

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

这种不一致的行为让我对我应该遵循的做法感到困惑。我应该从现在开始延伸活动吗?

3 个解决方案

#1


5  

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:

简短回答“我应该从现在开始延伸活动吗?”不,你应该继续扩展AppCompatActivity,因为它为旧设备提供了向后兼容的功能。在AppCompatImageView的情况下:

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

ImageView支持旧版平台上的兼容功能,包括:

  • Allows dynamic tint of its background via the background tint methods in ViewCompat.
  • 允许通过ViewCompat中的背景色调方法动态显示其背景色调。
  • Allows setting of the background tint using backgroundTint and backgroundTintMode.
  • 允许使用backgroundTint和backgroundTintMode设置背景色调。
  • Allows dynamic tint of its image via the image tint methods in ImageViewCompat.
  • 允许通过ImageViewCompat中的图像色调方法动态着色其图像。
  • Allows setting of the image tint using tint and tintMode.
  • 允许使用tint和tintMode设置图像色调。

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

此外,它还增加了与旧版Android版本的矢量绘图兼容性。

Explanation about the inconsistencies

关于不一致的解释

As it is explained in AppCompatImageView:

正如在AppCompatImageView中解释的那样:

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

当您在布局中使用ImageView并且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.

AppCompatActivity安装LayoutInflater.Factory2来拦截某些视图的膨胀。可以在AppCompatViewInflater.java中看到此inflater的代码。

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:

负责创建视图的函数是AppCompatViewInflater #createView(View,String,Context,AttributeSet,boolean,boolean,boolean,boolean),正如你在这里看到的那样,它会检查简单的视图名称(没有为它添加前缀的包),以及而是创建AppCompat *版本:

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

强制使用非AppCompat视图

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:

因此,为了在使用AppCompatActivity时强制创建常规ImageView(无AppCompatImageView),您需要指定完整的类名,例如:

    <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.

有关布局通胀如何工作的更多信息,您可以看到令人惊叹的演讲“布局冲突:朋友还是敌人?”由书法作者Chris Jenx撰写。

#2


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.

不可以。普通组件(Activity)或Compat组件(AppCompatActivity)之间的区别在于Compat组件旨在支持旧设备中的最新UI组件。这就是它提供了向后兼容性,因此如果你支持各种各样的设备,你将需要它。

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

虽然我对这种行为没有特别的问题,因为它毕竟是AppCompat

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

是的,你是对的,当从AppCompatActivity内部使用图像视图时,普通的图像视图将被转换为AppCompatImageView。

AppCompatImageView

AppCompatImageView

Follow this link to read more about AppCompatImageView.

请点击此链接阅读有关AppCompatImageView的更多信息。

#3


-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.

AppCompatImageView与ImageView的工作方式相同。支持库AppCompat仅用于向后兼容。因此,如果您希望您的应用程序向后兼容,则应扩展AppCompat类。

#1


5  

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:

简短回答“我应该从现在开始延伸活动吗?”不,你应该继续扩展AppCompatActivity,因为它为旧设备提供了向后兼容的功能。在AppCompatImageView的情况下:

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

ImageView支持旧版平台上的兼容功能,包括:

  • Allows dynamic tint of its background via the background tint methods in ViewCompat.
  • 允许通过ViewCompat中的背景色调方法动态显示其背景色调。
  • Allows setting of the background tint using backgroundTint and backgroundTintMode.
  • 允许使用backgroundTint和backgroundTintMode设置背景色调。
  • Allows dynamic tint of its image via the image tint methods in ImageViewCompat.
  • 允许通过ImageViewCompat中的图像色调方法动态着色其图像。
  • Allows setting of the image tint using tint and tintMode.
  • 允许使用tint和tintMode设置图像色调。

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

此外,它还增加了与旧版Android版本的矢量绘图兼容性。

Explanation about the inconsistencies

关于不一致的解释

As it is explained in AppCompatImageView:

正如在AppCompatImageView中解释的那样:

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

当您在布局中使用ImageView并且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.

AppCompatActivity安装LayoutInflater.Factory2来拦截某些视图的膨胀。可以在AppCompatViewInflater.java中看到此inflater的代码。

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:

负责创建视图的函数是AppCompatViewInflater #createView(View,String,Context,AttributeSet,boolean,boolean,boolean,boolean),正如你在这里看到的那样,它会检查简单的视图名称(没有为它添加前缀的包),以及而是创建AppCompat *版本:

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

强制使用非AppCompat视图

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:

因此,为了在使用AppCompatActivity时强制创建常规ImageView(无AppCompatImageView),您需要指定完整的类名,例如:

    <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.

有关布局通胀如何工作的更多信息,您可以看到令人惊叹的演讲“布局冲突:朋友还是敌人?”由书法作者Chris Jenx撰写。

#2


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.

不可以。普通组件(Activity)或Compat组件(AppCompatActivity)之间的区别在于Compat组件旨在支持旧设备中的最新UI组件。这就是它提供了向后兼容性,因此如果你支持各种各样的设备,你将需要它。

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

虽然我对这种行为没有特别的问题,因为它毕竟是AppCompat

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

是的,你是对的,当从AppCompatActivity内部使用图像视图时,普通的图像视图将被转换为AppCompatImageView。

AppCompatImageView

AppCompatImageView

Follow this link to read more about AppCompatImageView.

请点击此链接阅读有关AppCompatImageView的更多信息。

#3


-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.

AppCompatImageView与ImageView的工作方式相同。支持库AppCompat仅用于向后兼容。因此,如果您希望您的应用程序向后兼容,则应扩展AppCompat类。