为啥 `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 更改为srcbackground 中的activity_main.xml,它就会出现。此代码似乎遵循此处描述的说明:

https://developer.android.com/reference/android/view/View.html#attr_android:foreground

为什么android:foreground 标签在上面的代码中不起作用?

注意:

minSdkVersion19,我在Android 5.1 (API level 22) 上运行这个应用程序

【问题讨论】:

您在哪个 API 版本中运行应用程序? @HarishJose minSdkVersion 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 &gt; 23,它可以在没有FrameLayout 的情况下工作。

希望对你有帮助。

【讨论】:

文档说明了android:foregrounddefines the drawable to draw over the content,它没有说明内容必须是ViewGroup。并且标签是为View 类定义的。如果您说的是正确的,那么应该已经为 ViewGroup 类定义了标签。 @saga 我同意文档不够清晰,但我的解决方案适用于Android 5.1,即API level 22(您面临的问题)。是的,您是对的,文档不够清晰,无法使用 android:foreground 属性。对于API level &gt; 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】:

另一种解决方案是用&lt;ripple&gt; 包装您的图像,将其设置为您的ImageViewbackground,并使用tinttintMode 来“隐藏”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,了解此技术与使用 &lt;FrameLayout&gt;foreground 属性的比较。

【讨论】:

以上是关于为啥 `android:foreground` 属性不起作用?的主要内容,如果未能解决你的问题,请参考以下文章

Android FrameLayout的android foreground属性可以设置单击时的前景色

为什么`android:foreground`属性不起作用?

linux rsync 同步过来的目录为啥属主属组是mysql

苹果手表applewach5为啥没法截图?

FrameLayout布局

Android 按钮实现按压水波纹效果