为啥 `android:foreground` 属性不起作用?
Posted
技术标签:
【中文标题】为啥 `android:foreground` 属性不起作用?【英文标题】:Why doesn't `android:foreground` attribute work?为什么 `android:foreground` 属性不起作用? 【发布时间】:2019-02-19 02:32:58 【问题描述】:看看这个小的安卓应用:
MainActivity.java:
package io.github.gsaga.toucheventtest;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
public class MainActivity extends AppCompatActivity
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
activity_main:
<ImageView android:layout_
android:layout_
android:foreground="@drawable/ic_launcher_background"
xmlns:android="http://schemas.android.com/apk/res/android" />
android:foreground
指向的图像不显示,但如果我将foreground
更改为src
或background
中的activity_main.xml
,它就会出现。此代码似乎遵循此处描述的说明:
https://developer.android.com/reference/android/view/View.html#attr_android:foreground
为什么android:foreground
标签在上面的代码中不起作用?
注意:
minSdkVersion
是19
,我在Android 5.1
(API level 22
) 上运行这个应用程序
【问题讨论】:
您在哪个 API 版本中运行应用程序? @HarishJoseminSdkVersion
19 岁,我在 android 5.1 上运行这个应用
@saga 你好,你能说明一下发帖的实际意图吗?您是想为 ImageView 设置叠加层,还是想弄清楚为什么 android:foreground
属性不适用于 ImageViews
出于好奇?
你好,@saga。请在FrameLayout
使用android:foreground
。
【参考方案1】:
简答
这是由于 bug 自 API 级别 23 以来就存在于 Android 中。
有关行为的更多详细信息
这里列出了所有与将前景可绘制对象设置为具有 API 级别的视图相关的 XML 属性和相应方法,它们通过FrameLayout
引入。但是,这些后来在 API 级别 23 中移至 View
。
╔════════════════════════════╦═════════════════════════════════════════════════╦═════════════╗
║ XML attribute ║ Method ║ Added in ║
║ ║ ║ (API level) ║
╠════════════════════════════╬═════════════════════════════════════════════════╬═════════════╣
║ android:foreground ║ setForeground(Drawable) ║ 1 ║
╠════════════════════════════╬═════════════════════════════════════════════════╬═════════════╣
║ android:foregroundGravity ║ setForegroundGravity(int gravity) ║ 1 ║
╠════════════════════════════╬═════════════════════════════════════════════════╬═════════════╣
║ android:foregroundTint ║ setForegroundTintMode(PorterDuff.Mode tintMode) ║ 21 ║
╠════════════════════════════╬═════════════════════════════════════════════════╬═════════════╣
║ android:foregroundTintMode ║ setForegroundTintMode(PorterDuff.Mode tintMode) ║ 21 ║
╚════════════════════════════╩═════════════════════════════════════════════════╩═════════════╝
Android 文档说setForeground(Drawable)
被添加到 API 1 中,setForegroundTintList (ColorStateList tint)
和 setForegroundTintMode (PorterDuff.Mode tintMode)
被添加到 API 级别 21 到 View
。实际上,它们一直存在于 FrameLayout
中,直到它移入 API 23。
在 API 级别 this。
现在看看这些属性在不同版本上的工作方式。
╔═══════════╦══════════════════╦══════════════════╗
║ API level ║ By code ║ Using XML ║
╠═══════════╬══════════════════╬══════════════════╣
║ <23 ║ FrameLayout only ║ FrameLayout only ║
╠═══════════╬══════════════════╬══════════════════╣
║ >=23 ║ FrameLayout only ║ All views ║
╚═══════════╩══════════════════╩══════════════════╝
错误原因
当这些属性在 API 级别 23 中移动到 View
时,他们对其进行了一些奇怪的修改,这可以称为错误。在从 XML 加载属性时,它会检查 View
是否是 FrameLayout
,而我们可以将其用于相同目的的方法中。
视图构造函数,API 级别 23:
case R.styleable.View_foreground:
if (targetSdkVersion >= Build.VERSION_CODES.M || this instanceof FrameLayout)
setForeground(a.getDrawable(attr));
break;
【讨论】:
【参考方案2】:要在Android 5.1
上使用android:foreground
,即API level 22
,您没有正确使用android:foreground
。
因为它的名字清楚地表明你可以在任何内容的顶部/前景设置drawable,比如覆盖,即你可以在FrameLayout
中放置一些视图,你可以使用android:foreground
。在此 FrameLayout 中添加您的 ImageView
。
Documentation:
定义可绘制对象在内容上绘制。这可以用作 叠加层。前景可绘制对象参与填充 重力设置为填充时的内容。
以下是使用示例:
<FrameLayout
android:id="@+id/share"
android:layout_
android:layout_
android:foreground="@drawable/ic_launcher_background>
// your ImageView here
<ImageView
android:layout_
android:layout_/>
</FrameLayout>
注意:
对于API level > 23
,它可以在没有FrameLayout
的情况下工作。
希望对你有帮助。
【讨论】:
文档说明了android:foreground
defines the drawable to draw over the content
,它没有说明内容必须是ViewGroup
。并且标签是为View
类定义的。如果您说的是正确的,那么应该已经为 ViewGroup
类定义了标签。
@saga 我同意文档不够清晰,但我的解决方案适用于Android 5.1
,即API level 22
(您面临的问题)。是的,您是对的,文档不够清晰,无法使用 android:foreground
属性。对于API level > 23
,它可以在没有FrameLayout
的情况下工作。【参考方案3】:
正如 VicJordan 所建议的那样,android:foreground
似乎在某个时候 (API FrameLayout 一起使用。但是,对于 API 23+,android:foreground
似乎适用于任何视图类型。请参阅 View.java 源中的 this 选择:
case R.styleable.View_foreground:
if (targetSdkVersion >= Build.VERSION_CODES.M || this instanceof FrameLayout)
setForeground(a.getDrawable(attr));
这是android:foreground
使用 API 28 的示例,其布局如下:
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_
android:layout_
tools:context=".MainActivity">
<ImageView
android:layout_
android:layout_
android:foreground="@drawable/ic_launcher_foreground"
android:src="@android:drawable/ic_delete"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
然而,在 API 22 上,我们看到了这一点:
没有前景图像。因此,android:foreground
仅在 API 级别为 23+ 时有效。我同意这并没有真正记录在案,但事实就是如此。
更新:API 23 似乎与android:foreground
存在问题,因此假设android:foreground
适用于API 24+ 以获得一般视图。
第二次更新:遇到了一些其他帖子,解决了关于 setForeground()
here 和 here 的相同问题。在第二个问题的公认答案中,CommonsWare 将其标识为“文档错误”。
【讨论】:
【参考方案4】:另一种解决方案是用<ripple>
包装您的图像,将其设置为您的ImageView
的background
,并使用tint
和tintMode
来“隐藏”src
图像以便背景上面有波纹的图像是可见的。
<!-- In your layout file -->
<ImageView
android:adjustViewBounds="true"
android:background="@drawable/image_with_ripple"
android:layout_
android:layout_
android:src="@drawable/image"
android:tint="@android:color/transparent"
android:tintMode="src_in" />
<!-- drawable/image_with_ripple.xml -->
<?xml version="1.0" encoding="utf-8"?>
<ripple
xmlns:android="http://schemas.android.com/apk/res/android"
android:color="?colorControlHighlight">
<item android:drawable="@drawable/image" />
</ripple>
这不仅适用于 API 21+,而且如果您的图像有圆角 - 或者是另一种类型的非矩形形状,如星形或心形图标 - 波纹会保持在其边界内,而不是填充视图的矩形边界,这在某些情况下可以提供更好的外观。
请参阅this Medium article 获取动画 GIF,了解此技术与使用 <FrameLayout>
或 foreground
属性的比较。
【讨论】:
以上是关于为啥 `android:foreground` 属性不起作用?的主要内容,如果未能解决你的问题,请参考以下文章
Android FrameLayout的android foreground属性可以设置单击时的前景色
为什么`android:foreground`属性不起作用?