【问题标题】: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_width="match_parent"
    android:layout_height="match_parent"
    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

标签: android xml android-layout


【解决方案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_width="wrap_content"
        android:layout_height="wrap_content"
        android:foreground="@drawable/ic_launcher_background>
    
        // your ImageView here
        <ImageView
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
    
    </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_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
    
        <ImageView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            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() herehere 的相同问题。在第二个问题的公认答案中,CommonsWare 将其标识为“文档错误”。

    【讨论】:

      【解决方案4】:

      另一种解决方案是用&lt;ripple&gt; 包装您的图像,将其设置为您的ImageViewbackground,并使用tinttintMode 来“隐藏”src 图像以便背景上面有波纹的图像是可见的。

      <!-- In your layout file -->
      
      <ImageView
          android:adjustViewBounds="true"
          android:background="@drawable/image_with_ripple"
          android:layout_height="wrap_content"
          android:layout_width="wrap_content"
          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 属性的比较。

      【讨论】:

        猜你喜欢
        • 2017-02-16
        • 1970-01-01
        • 2021-04-27
        • 2019-04-04
        • 1970-01-01
        • 1970-01-01
        • 2016-07-05
        • 2011-03-24
        • 1970-01-01
        相关资源
        最近更新 更多