Lollipop:在 statusBar 后面绘制,其颜色设置为透明

Posted

技术标签:

【中文标题】Lollipop:在 statusBar 后面绘制,其颜色设置为透明【英文标题】:Lollipop : draw behind statusBar with its color set to transparent 【发布时间】:2015-03-07 12:58:03 【问题描述】:

我已将 Lollipop 的 statusBar 颜色设置为透明,仅在我的主题中使用以下行:

<item name="android:statusBarColor">@android:color/transparent</item>

现在我需要在它后面绘制,但我无法在它后面绘制任何视图。我知道如何使用 windowTranslucentStatus 属性,但不想使用此属性,因为它会忽略设置为透明的状态栏的颜色。

【问题讨论】:

你也可以使用android:fitsSystemWindows="true" 【参考方案1】:

方法#1:

要实现完全透明的状态栏,您必须使用statusBarColor,它仅在 API 21 及更高版本上可用。 windowTranslucentStatus 可用于 API 19 及更高版本,但它为状态栏添加了有色背景。但是,设置windowTranslucentStatus 确实实现了将statusBarColor 更改为透明不会实现的一件事:它设置SYSTEM_UI_FLAG_LAYOUT_STABLESYSTEM_UI_FLAG_LAYOUT_FULLSCREEN 标志。获得相同效果的最简单方法是手动设置这些标志,这有效地禁用了 Android 布局系统强加的插入,让您自己照顾。

您在 onCreate 方法中调用此行:

getWindow().getDecorView().setSystemUiVisibility(
    View.SYSTEM_UI_FLAG_LAYOUT_STABLE
    | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);

请务必在 /res/values-v21/styles.xml 中设置透明度:

<item name="android:statusBarColor">@android:color/transparent</item>

或以编程方式设置透明度:

getWindow().setStatusBarColor(Color.TRANSPARENT);

这种方法的好处是,相同的布局和设计也可以在 API 19 上使用,方法是将透明状态栏换成有色半透明状态栏。

<item name="android:windowTranslucentStatus">true</item>

方法#2:

如果您只需要在状态栏下方绘制背景图像,而不是在其后面放置视图,只需将活动主题的背景设置为所需图像并设置状态栏透明度,如图所示在方法#1中。这是我几个月前用来为Android Police article 创建屏幕截图的方法。

方法#3:

如果您必须忽略某些布局的标准系统插图,同时保持它们在其他布局中的工作,那么唯一可行的方法是使用经常链接的 ScrimInsetsFrameLayout 类。当然,在该类中完成的某些事情并不是所有场景都需要的。例如,如果您不打算使用合成状态栏覆盖,只需注释掉 init() 方法中的所有内容,并且不要费心向 attrs.xml 文件添加任何内容。我已经看到这种方法行之有效,但我认为您会发现它带来了一些可能需要大量工作才能解决的其他含义。

我还看到您反对包装多个布局。在将一个布局包装在另一个布局中的情况下,两者的高度和宽度都有match_parent,性能影响太微不足道了,无需担心。无论如何,您可以通过将其从 FrameLayout 扩展的类更改为您喜欢的任何其他类型的 Layout 类来完全避免这种情况。它会工作得很好。

【讨论】:

非常感谢,设置 SYSTEM_UI_FLAG_LAYOUT_STABLESYSTEM_UI_FLAG_LAYOUT_FULLSCREEN 标志是我正在寻找的解决方案。 我搜索了您的第一个解决方案一个月。谢谢! 嗨。您的代码运行良好。但是当我退出片段时,我应该如何回滚更改? 这不适用于CollapsingToolbarLayoutToolbar:状态栏保持不完全半透明,android:statusBarColor 无效 谢谢。如果您想恢复到非全屏活动,请阅读developer.sonymobile.com/knowledge-base/tutorials/…。【参考方案2】:

这适用于我的情况


// Create/Set toolbar as actionbar
toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);

// Check if the version of Android is Lollipop or higher
if (Build.VERSION.SDK_INT >= 21) 

    // Set the status bar to dark-semi-transparentish
    getWindow().setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS,
            WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);

    // Set paddingTop of toolbar to height of status bar.
    // Fixes statusbar covers toolbar issue
    toolbar.setPadding(0, getStatusBarHeight(), 0, 0);


// A method to find height of the status bar
public int getStatusBarHeight() 
    int result = 0;
    int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
    if (resourceId > 0) 
        result = getResources().getDimensionPixelSize(resourceId);
    
    return result;


有关使用状态栏的更多信息:youtube.com/watch?v=_mGDMVRO3iE


【讨论】:

@TheOnlyAnil 定义一个固定的工具栏高度 :) “56dp” 左右应该看起来不错 像这样获取状态栏高度是完全不受支持的,未记录在案的,并且肯定会在未来的版本中中断。请改用 fitSystemWindows="true"。 好吧,我这样做也是因为有时你必须这样做。 这是一个非常糟糕的主意,根据 Chris Banes(Google 工程师)在本次演讲中的说法:youtube.com/watch?v=_mGDMVRO3iE @Michael FWIW,我过去做过这个,WindowInsets api 一个(方法)“应该很简单,不需要我们考虑就可以完成”,但事实并非如此,这是一个蹩脚的无用 API,增加了很多不必要的开销。谷歌总是完成 80%,剩下的 20% 留给开发人员……他们一路破解,直到他们实现。 ¯_(ツ)_/¯【参考方案3】:

试试这个主题

<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    <!-- Customize your theme here. -->
    <item name="windowActionBar">false</item>
    <item name="windowNoTitle">true</item>
    <item name="colorPrimaryDark">@android:color/transparent</item>
    <item name="colorPrimary">@color/md_blue_200</item>
    <item name="android:windowDrawsSystemBarBackgrounds">true</item>
    <item name="android:statusBarColor">@android:color/transparent</item>
    <item name="android:windowTranslucentStatus">true</item>
</style>

请确保,您的布局设置

android:fitsSystemWindows="false"

【讨论】:

设置android:fitsSystemWindows="false" 将工具栏切成两半。【参考方案4】:

代替

<item name="android:statusBarColor">@android:color/transparent</item>

使用以下内容:

<item name="android:windowTranslucentStatus">true</item>

并确保删除“MainActivity”布局上的顶部填充(默认添加)。

请注意,这不会使状态栏完全透明,并且在您的状态栏上仍然会有一个“褪色的黑色”覆盖。

【讨论】:

当使用&lt;item name="android:windowTranslucentStatus"&gt;true&lt;/item&gt; 时,它会使android 在状态栏下放置一个褪色的黑色背景,这不是我想要的。有什么办法不让这个黑色褪色的背景? 我尝试了很多方法,但我找不到去除褪色黑色背景的方法。因此,我在结束我的研究时说它在 Android 上是不可能的(即使是 ScrimLayout 也不可能)。这是有道理的,因为它始终确保您的状态栏具有适当的可读性。我会在上面更新我的答案。 我觉得如果能在状态栏上放一个透明的颜色,后面却不能画出来会很奇怪。我理解你为什么说你不能删除这个黑色褪色背景是有道理的,但要小心以保持状态栏图标可见绝对不是那么难。这是一篇android Police的文章,展示了透明状态栏androidpolice.com/2014/07/04/…的使用。我会尝试联系作者。 希望您能与他取得联系。我也对这样做很感兴趣:) 太棒了!也更新了我的答案。【参考方案5】:

Cody Toombs 的解决方案几乎为我解决了问题。我不确定这是否与 Xamarin 相关,但我现在有一个可以接受的解决方案:

这是我的设置:

我有一个 Android 项目,其中引用了 Android.Support v4 和 v7 包。我定义了两种样式:

values/styles.xml:

<?xml version="1.0" encoding="UTF-8" ?>
<resources>
    <style name="MyStyle" parent="@style/Theme.AppCompat.Light.NoActionBar">
        <item name="android:windowTranslucentStatus">true</item>
    </style>
</resources>

values-v21/styles.xml:

<?xml version="1.0" encoding="UTF-8" ?>
<resources>
    <style name="MyStyle" parent="@style/Theme.AppCompat.Light.NoActionBar">
        <item name="android:statusBarColor">@android:color/transparent</item>
    </style>
</resources>

AndroidManifest 以“MyStyle”为目标:

AndroidManifest.xml:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="com.agn.test.test">
    <uses-sdk android:minSdkVersion="10" />
    <application android:allowBackup="true" android:icon="@mipmap/icon" android:label="@string/app_name" android:theme="@style/MyStyle">
    </application>
</manifest>

最后是 Main Activity 中的代码:

[Activity (Label = "Test", MainLauncher = true, Icon = "@mipmap/icon")]
public class MainActivity : Activity

    protected override void OnCreate (Bundle savedInstanceState)
    
        base.OnCreate (savedInstanceState);

        SetContentView (Resource.Layout.Main);
        //Resource.Layout.Main is just a regular layout, no additional flags. Make sure there is something in there like an imageView, so that you can see the overlay.

        var uiOptions = (int)Window.DecorView.SystemUiVisibility;
        uiOptions ^= (int)SystemUiFlags.LayoutStable;
        uiOptions ^= (int)SystemUiFlags.LayoutFullscreen;
        Window.DecorView.SystemUiVisibility = (StatusBarVisibility)uiOptions;

        Window.AddFlags (WindowManagerFlags.DrawsSystemBarBackgrounds);
    

请注意,我设置了 DrawsSystemBarBackgrounds 标志,这一切都不同

Window.AddFlags (WindowManagerFlags.DrawsSystemBarBackgrounds); 

我花了很多时间把它弄好,实际上时间太多了。希望这个答案可以帮助任何尝试实现相同目标的人。

【讨论】:

android:windowTranslucentStatus 需要 API 级别 19 或更高,您怎么可能在 values/styles.xml 上使用它,而 android:minSdkVersion 为 10? 【参考方案6】:

@Cody Toombs 的回答导致了将布局置于导航栏后面的问题。所以我发现使用@Kriti 给出的this 解决方案

这是相同的 Kotlin 代码 sn-p:

if (Build.VERSION.SDK_INT >= 19 && Build.VERSION.SDK_INT < 21) 
    setWindowFlag(this, WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, true)


if (Build.VERSION.SDK_INT >= 19) 
    window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN


if (Build.VERSION.SDK_INT >= 21) 
    setWindowFlag(this, WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, false)
    getWindow().setStatusBarColor(Color.TRANSPARENT)


private fun setWindowFlag(activity: Activity, bits: Int, on: Boolean) 

    val win: Window = activity.getWindow()

    val winParams: WindowManager.LayoutParams = win.getAttributes()
    if (on) 
        winParams.flags = winParams.flags or bits
     else 
        winParams.flags = winParams.flags and bits.inv()
    
    win.setAttributes(winParams)

你还需要添加

android:fitsSystemWindows="false"

布局的根视图。

【讨论】:

如何在 Android R 中处理这个问题? @Kishan Solanki【参考方案7】:

我遇到了同样的问题,所以我创建了在状态栏 API 19+ 后面绘制的 ImageView

在状态栏gist.github.com后面设置自定义图片

public static void setTransparent(Activity activity, int imageRes) 
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) 
        return;
    
    // set flags
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) 
        activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
        activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
        activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
        activity.getWindow().setStatusBarColor(Color.TRANSPARENT);
     else 
        activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
    

    // get root content of system window
    //ViewGroup rootView = (ViewGroup) ((ViewGroup) activity.findViewById(android.R.id.content)).getChildAt(0);
    // rootView.setFitsSystemWindows(true);
    // rootView.setClipToPadding(true);

    ViewGroup contentView = (ViewGroup) activity.findViewById(android.R.id.content);
    if (contentView.getChildCount() > 1) 
        contentView.removeViewAt(1);
    

    // get status bar height
    int res = activity.getResources().getIdentifier("status_bar_height", "dimen", "android");
    int height = 0;
    if (res != 0)
        height = activity.getResources().getDimensionPixelSize(res);

    // create new imageview and set resource id
    ImageView image = new ImageView(activity);
    LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, height);
    image.setLayoutParams(params);
    image.setImageResource(imageRes);
    image.setScaleType(ScaleType.MATRIX);

    // add image view to content view
    contentView.addView(image);
    // rootView.setFitsSystemWindows(true);


【讨论】:

【参考方案8】:

你可以使用 ScrimInsetFrameLayout

https://github.com/google/iosched/blob/master/android/src/main/java/com/google/samples/apps/iosched/ui/widget/ScrimInsetsFrameLayout.java

android:fitsSystemWindows="true" 应该设置在 scrim 布局上!

【讨论】:

我看到了那个课程,但是如果我想使用 RelativeLayout 而不是 FrameLayout 怎么办? 在框架布局中使用相对布局。 在布局中嵌套布局绝对不是我的性能解决方案 应用程序性能取决于许多因素,这些因素可能会有所不同。如果你没有出路,试试吧。祝你好运 我明白你的意思,但必须有另一种解决方案,如果没有,可能有一种方法可以在任何布局上实现此自定义框架布局中已实现的内容。但无论如何谢谢【参考方案9】:

在 Android Studio 1.4 中,带有样板代码的模板项目在您的 AppbarLayout 和/或工具栏上设置 Overlay 主题。 fitSystemWindowattribute = true 也将它们设置为在状态栏后面呈现。这将导致仅工具栏直接呈现在状态栏下方,其他所有内容都将呈现在工具栏下方。所以上面提供的解决方案不能单独工作。您必须进行以下更改。

删除 Overlay 主题或将其更改为工具栏的非覆盖主题。

将以下代码放入您的 styles-21.xml 文件中。

@android:颜色/透明

将此主题分配给包含导航抽屉的活动 AndroidManifest.xml 文件。

这将使导航抽屉呈现在透明状态栏后面。

【讨论】:

【参考方案10】:

与发布的一些解决方案类似,但在我的情况下,我将状态栏设置为透明,并将操作栏的位置固定为负边距

if (Build.VERSION.SDK_INT >= 21) 
    getWindow().setStatusBarColor(Color.TRANSPARENT);
    FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) toolbar.getLayoutParams();
    lp.setMargins(0, -getStatusBarHeight(), 0, 0);

我在工具栏和根视图中使用过

android:fitsSystemWindows="true"

【讨论】:

getStatusBarHeight() 不标准 此方法在@mike-milla 的解决方案上实现... public int getStatusBarHeight() int result = 0; int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android"); if (resourceId > 0) result = getResources().getDimensionPixelSize(resourceId); 返回结果; 谢谢。我这样做了: AppBarLayout.LayoutParams lp = (AppBarLayout.LayoutParams) toolbar.getLayoutParams(); lp.topMargin += getStatusBarHeight();【参考方案11】:

@laobie 中的良好库 StatusBarUtil 有助于在状态栏中轻松绘制图像。

只需添加您的build.gradle

compile 'com.jaeger.statusbarutil:library:1.4.0'

然后在Activity集合中

StatusBarUtil.setTranslucentForImageView(Activity activity, int statusBarAlpha, View viewNeedOffset)

在布局中

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_
    android:layout_
    android:background="@color/white"
    android:orientation="vertical">

    <ImageView
        android:layout_alignParentTop="true"
        android:layout_
        android:adjustViewBounds="true"
        android:layout_
        android:src="@drawable/toolbar_bg"/>

    <android.support.design.widget.CoordinatorLayout
        android:id="@+id/view_need_offset"
        android:layout_
        android:layout_>

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_
            android:layout_
            android:background="@android:color/transparent"
            app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
            app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"/>

        <!-- Your layout code -->
    </android.support.design.widget.CoordinatorLayout>
</RelativeLayout>

欲了解更多信息,请下载demo 或从github page 克隆并使用所有功能。

注意:支持 KitKat 及以上版本。

希望对其他人有所帮助!

【讨论】:

【参考方案12】:

您需要做的就是在您的主题中设置这些属性

<item name="android:windowTranslucentStatus">true</item>
<item name="android:windowTranslucentNavigation">true</item>

【讨论】:

我很确定这将使您的布局位于 UI 导航按钮(具有屏幕导航的设备)下方。这使得我们的应用程序无法访问底部导航视图,因此我们在 Nexus 上进行测试后放弃了这些标志【参考方案13】:

接受的答案使用 CollapsingToolbarLayout 对我有用。不过需要注意的是,setSytstemUiVisibility() 会覆盖之前对该函数的任何调用。因此,如果您在其他地方为同一视图使用该函数,则需要包含 View.SYSTEM_UI_FLAG_LAYOUT_STABLEView.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN 标志,否则它们将被新调用覆盖。

对我来说就是这种情况,一旦我将这两个标志添加到另一个地方,我正在拨打setSystemUiVisibility(),接受的答案非常有效。

【讨论】:

【参考方案14】:

我将在这里添加更多信息。最新的 Android 开发使得在状态栏中处理很多情况变得非常容易。以下是我对styles.xml的观察

    背景颜色:对于 SDK 21+,正如很多答案提到的,&lt;item name="android:windowTranslucentStatus"&gt;true&lt;/item&gt; 将使状态栏透明并显示在 UI 前面。您的 Activity 将占据顶部的整个空间。

    背景颜色:同样,对于 SDK 21+,&lt;item name="android:statusBarColor"&gt;@color/your_color&lt;/item&gt; 只会为您的状态栏提供颜色,而不会影响其他任何内容。

    但是,在后来的设备 (Android M/+) 中,图标 开始以不同的颜色出现。如果您覆盖 values-23 文件夹中的 styles.xml 文件并添加 &lt;item name="android:windowLightStatusBar"&gt;true&lt;/item&gt;,操作系统可以为 SDK 23/+ 的图标提供更深的灰色阴影。 这样,如果您的状态栏颜色较浅,您将为用户提供更明显的状态栏(想想很多谷歌应用程序有浅色背景,但那里的图标以灰色可见)。 如果您通过第 2 点为状态栏添加颜色,我建议您使用它

    在最新的设备中,SDK 29/+ 带有系统范围的明暗主题,可由用户控制。作为开发人员,我们还应该在一个新的values-night 文件夹中覆盖我们的样式文件,为用户提供两种不同的体验。 在这里,我再次发现第 2 点在提供“状态栏的背景颜色”方面是有效的。但是系统没有改变我的应用程序状态栏图标的颜色。由于我的日版风格由较轻的主题组成,这意味着用户将遭受低能见度(较浅背景上的白色图标) 这个问题可以通过使用第 3 点方法或通过覆盖 values-29 文件夹中的样式文件并使用更新的 api &lt;item name="android:enforceStatusBarContrast"&gt;true&lt;/item&gt; 来解决。如果您的背景颜色太浅,这将自动对图标强制执行浅灰色调。

【讨论】:

【参考方案15】:

这是我用来完成此任务的主题:

<style name="AppTheme" parent="@android:style/Theme.NoTitleBar">

    <!-- Default Background Screen -->
    <item name="android:background">@color/default_blue</item>
    <item name="android:windowTranslucentStatus">true</item>

</style>

【讨论】:

有些奇怪的反对者。即使解决方案对您和其他人来说是正确的,他们也会否决而不是传递给另一个答案。 我所要求的只是一个解释。我认为人们不应该在没有解释的情况下投反对票。 我也这么认为。 我将解释背景选项在此捆绑包中无法正常工作。请更正你的答案。 嗯...您在哪个版本的 Android 中执行此操作?我在 Android M 中使用这个配置。【参考方案16】:

正确的解决方案是将 Activity 标签下的 XML 属性更改为以下样式。它只是工作

  android:theme="@style/Theme.AppCompat.Light.NoActionBar"

【讨论】:

以上是关于Lollipop:在 statusBar 后面绘制,其颜色设置为透明的主要内容,如果未能解决你的问题,请参考以下文章

状态栏后面的布局 - Android Lollipop

在 Lollipop 崩溃前使用 android vector Drawables

Material Design 中呈现的 Android Lollipop 上的半透明渐变状态栏

Lollipop 导航栏与 Gravity.Bottom 重叠对话框

Lollipop导航栏重叠Dialog与Gravity.Bottom

在Pre-Lollipop设备中使用.xml向量时,应用程序崩溃Xamarin.Android