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

pul*_*ion 8 android android-appcompat

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

在此输入图像描述

虽然我对这种行为没有特别的问题,因为它的AppCompat毕竟是在检查开发人员的代码时,我看到了,extends Activity而不是,AppCompatActivity我几乎把它标记为"不好的做法".

另一方面,在处理矢量图像时,我使用的ImageView是一个问题,因为我没有使用过AppCompatImageView它并使用它是解决方案:

ImageView无法在设备中正确显示

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

Xav*_*ana 6

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

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

  • 允许通过ViewCompat中的背景色调方法动态显示其背景色调.
  • 允许使用backgroundTint和backgroundTintMode设置背景色调.
  • 允许通过ImageViewCompat中的图像色调方法对其图像进行动态着色.
  • 允许使用tint和tintMode设置图像色调.

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

关于不一致的说明

如下所述AppCompatImageView:

当您在布局中使用ImageView并且appcompat提供顶级活动/对话框时,将自动使用此选项.

所以,这并不意外.

这个怎么运作

AppCompatActivity安装一个LayoutInflater.Factory2拦截某些观点的通货膨胀.可以在AppCompatViewInflater.java中看到此inflater的代码.

负责创建Viewsis 的函数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;
}
Run Code Online (Sandbox Code Playgroud)

强制使用非AppCompat视图

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

    <android.widget.ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@mipmap/test"/>
Run Code Online (Sandbox Code Playgroud)

有关布局通胀如何运作的更多信息,您可以看到书法作者克里斯·詹克斯(Chris Jenx)的精彩演讲" 布局冲突:朋友还是敌人? " .


小智 -1

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