我在调试我的应用程序时发现,当鼠标悬停在 ImageView
引用上时,它是一个 AppCompatImageView
而不是 ImageView
。 TextView
(使用 AppCompatTextView
)也是如此。
虽然我对这种行为没有特别的问题,因为它毕竟是 AppCompat,但在检查其他开发人员的代码时,我看到,extends Activity
而不是 AppCompatActivity
我几乎将其标记为“不良做法”。
另一方面,在处理矢量图像时,我使用了 ImageView
并且出现了问题,因为我没有使用 AppCompatImageView
而使用它是解决方案:
ImageView not displaying correctly in the device
这种不一致的行为确实让我对我应该遵循的做法感到困惑。我是否应该从现在开始扩展 Activity?
最佳答案
对“我应该从现在开始扩展 Activity 吗?”的简短回答不,您应该继续扩展 AppCompatActivity
,因为它向旧设备提供向后兼容的功能。在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.
此外,它还增加了与适用于旧版 Android 的矢量绘图的兼容性。
关于不一致的解释
如 AppCompatImageView
中所述:
This will automatically be used when you use ImageView in your layouts and the top-level activity / dialog is provided by appcompat.
所以,这并不意外。
工作原理
AppCompatActivity
安装一个 LayoutInflater.Factory2
来拦截某些 View 的膨胀。这个充气机的代码可以在AppCompatViewInflater.java中看到.
负责创建 View
的函数是AppCompatViewInflater#createView(View, String, Context, AttributeSet, boolean, boolean, boolean, boolean)
,你可以看到这里它检查简单的 View 名称(没有包前缀),并创建 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;
}
强制使用非 AppCompat View
因此,为了在仍然使用 AppCompatActivity
的同时强制创建常规 ImageView
(没有 AppCompatImageView
),您需要指定完整的类名,例如:
<android.widget.ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/test"/>
有关布局膨胀如何工作的更多信息,请参阅 LayoutInflater: Friend or Foe? 的精彩演讲“Chris Jenx” ,Calligraphy 的作者.
关于android - 为什么我没有明确声明 Android 会选择 AppCompat 组件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45710962/