Android完全透明状态栏?

267

我已经搜索了文档,但只找到这个链接:Link。它用于使栏 半透明?我想要做的是让状态栏完全透明(如下图所示),并且向后兼容APK<19:

enter image description here

我的styles.xml:

<resources xmlns:tools="http://schemas.android.com/tools">

  <style name="AppTheme" parent="Theme.AppCompat.Light">
  <item name="android:actionBarStyle">@style/ThemeActionBar</item>
  <item name="android:windowActionBarOverlay">true</item>
  <!-- Support library compatibility -->
  <item name="actionBarStyle">@style/ThemeActionBar</item>
  <item name="windowActionBarOverlay">true</item>
  </style>

  <style name="ThemeActionBar" parent="Widget.AppCompat.Light.ActionBar.Solid">
  <item name="android:background"> @null </item>
  <!-- Support library compatibility -->
  <item name="background">@null</item>
  <item name="android:displayOptions"> showHome | useLogo</item>
  <item name="displayOptions">showHome|useLogo</item>

  </style>

</resources>

我能做的事情:

输入图像描述


来自官方消息:https://medium.com/androiddevelopers/translucent-systembars-the-right-way-across-api-levels-and-themes-6d7ddda21396 - Zero
39个回答

488

您只需要在您的主题中设置这些属性:

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

如果您希望您的活动/容器布局具有透明状态栏,则需要设置以下属性:

android:fitsSystemWindows="true"

在 Kitkat 及以下版本通常无法确保能够执行此操作,似乎可以通过 一些奇怪的代码 实现。

编辑:我建议使用此库:https://github.com/jgilfelt/SystemBarTint 来控制很多 pre-lollipop 状态栏颜色。

经过深思熟虑之后,我得出了完全禁用Lollipop上状态栏和导航栏透明度或任何颜色的答案,那就是在窗口上设置该标志:

// In Activity's onCreate() for instance
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
    Window w = getWindow();
    w.setFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS, WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
}

不需要其他的主题设置,它会产生类似于这样的效果:

在此输入图片描述


35
我使用了这段代码,然后将一张图片作为背景,并在我的activity_main.xml文件中添加了android:fitsSystemWindows="true"。系统栏是半透明的而不是透明的。 - Muhammad Ali
13
那么,我认为在这种情况下,您只需要将<item name="android:statusBarColor">@android:color/transparent</item>添加到您的主题中即可 :) - Daniel Wilson
15
是的,谢谢您的耐心等待。我已经尽力了几个小时,现在有些疲劳了。上面的代码不起作用,即使我将android:windowTranslucentStatus设置为false,状态栏仍然透明。现在我尝试对导航栏做同样的事情,并添加了<item name="android:navigationBarColor">@android:color/transparent</item>,但两个栏都变成了灰色,真是让人沮丧! - Muhammad Ali
5
为了使“adjustPan”生效,请使用以下代码: w.setFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS, WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN); - Arda Kara
13
使用FLAG_LAYOUT_NO_LIMITS标志还会将布局置于底部的虚拟导航栏下方。一般来说,你不希望这样做...所以就像其他人所说的那样:-(。 - kenyee
显示剩余7条评论

40

只需将此行代码添加到您的主Java文件中:

getWindow().setFlags(
    WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,
    WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
);

72
这也影响了导航栏。 - android developer
2
这会破坏SOFT_INPUT_ADJUST_PAN和SOFT_INPUT_ADJUST_RESIZE,因此无法滚动。 - mikep
这在安卓一加手机上不起作用! - charitha amarasinghe

40

您可以使用以下代码使状态栏透明。 查看带有红色突出显示的图像,以帮助您识别以下代码的用法

1]

适用于您的Android应用程序的Kotlin代码片段

步骤1:在OnCreate方法中编写代码

if (Build.VERSION.SDK_INT >= 19 && Build.VERSION.SDK_INT < 21) {
    setWindowFlag(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(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, false)
    window.statusBarColor = Color.TRANSPARENT
}

第二步:您需要SetWindowFlag方法,该方法在下面的代码中描述。

private fun setWindowFlag(bits: Int, on: Boolean) {
    val win = window
    val winParams = win.attributes
    if (on) {
        winParams.flags = winParams.flags or bits
    } else {
        winParams.flags = winParams.flags and bits.inv()
    }
    win.attributes = winParams
}

您的Android应用程序的Java代码片段:

步骤1:主要活动代码

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) {
    getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
}

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

步骤2:设置SetWindowFlag方法

public static void setWindowFlag(Activity activity, final int bits, boolean on) {
    Window win = activity.getWindow();
    WindowManager.LayoutParams winParams = win.getAttributes();
    if (on) {
        winParams.flags |= bits;
    } else {
        winParams.flags &= ~bits;
    }
    win.setAttributes(winParams);
}

我还添加了这个:<item name="android:statusBarColor">@android:color/transparent</item>。 - Paparika
只使用Window#addFlags和/或Window#clearFlags怎么样? - undefined

30

适用于Android KitKat及以上系统版本(对于那些希望透明状态栏而不操纵NavigationBar的人,因为所有这些答案也都将使NavigationBar透明!)

最简单的方法:

将以下3行代码放入styles.xml (v19)中-> 如果您不知道如何使用(v19),只需将它们写入默认的styles.xml中,然后使用alt+enter自动创建:

<item name="android:windowFullscreen">false</item>
<item name="android:windowContentOverlay">@null</item>
<item name="android:fitsSystemWindows">false</item>

现在,前往您的MainActivity类,并将此方法放在类的onCreate之外:

public static void setWindowFlag(Activity activity, final int bits, boolean on) {

    Window win = activity.getWindow();
    WindowManager.LayoutParams winParams = win.getAttributes();
    if (on) {
        winParams.flags |= bits;
    } else {
        winParams.flags &= ~bits;
    }
    win.setAttributes(winParams);
}

然后将此代码放入 Activity 的 onCreate 方法中:

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) {
        getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
    }
    //make fully Android Transparent Status bar
    if (Build.VERSION.SDK_INT >= 21) {
        setWindowFlag(this, WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, false);
        getWindow().setStatusBarColor(Color.TRANSPARENT);
    }

就是这样!


1
根据我在React Native的经验,上述代码与显示键盘(adjustResize)存在冲突。所以,我只做了一些小改动:if (Build.VERSION.SDK_INT >= 19) { getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE); }并在组件中添加:在AppManisfes - MainActivity中添加:android:windowSoftInputMode="adjustResize"其余代码保持不变。附注:"react-native": "0.61.5",谢谢@parsa dadras。 - Rudi Wijaya
3
这个回答正确地回答了楼主的问题。这应该是被采纳的答案。 - oar.garuna
1
如果您将代码放在onCreate()方法中,则无需更改样式,它将正常工作。 - Rohan Patel

18

完全透明的状态栏和导航栏

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    transparentStatusAndNavigation();
}


private void transparentStatusAndNavigation() {
    //make full transparent statusBar
    if (Build.VERSION.SDK_INT >= 19 && Build.VERSION.SDK_INT < 21) {
        setWindowFlag(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS
                | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION, true);
    }
    if (Build.VERSION.SDK_INT >= 19) {
        getWindow().getDecorView().setSystemUiVisibility(
                View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                        | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                        | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
        );
    }
    if (Build.VERSION.SDK_INT >= 21) {
        setWindowFlag(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS
                | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION, false);
        getWindow().setStatusBarColor(Color.TRANSPARENT);
        getWindow().setNavigationBarColor(Color.TRANSPARENT);
    }
}

private void setWindowFlag(final int bits, boolean on) {
    Window win = getWindow();
    WindowManager.LayoutParams winParams = win.getAttributes();
    if (on) {
        winParams.flags |= bits;
    } else {
        winParams.flags &= ~bits;
    }
    win.setAttributes(winParams);
}

14

我刚在这里找到它:https://developer.android.com/training/gestures/edge-to-edge

因为已经过去6年了,而默认的minSDKAPI是21(Lollipop)*CMIIW。以下是我的总结透明状态栏并且不与导航按钮重叠的方法:

fun setStatusBarTransparent(activity: Activity, view: View) {
    activity.apply {
        window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
        window.statusBarColor = ContextCompat.getColor(this, R.color.transparent)
        WindowCompat.setDecorFitsSystemWindows(window, false)
        ViewCompat.setOnApplyWindowInsetsListener(view) { root, windowInset ->
            val inset = windowInset.getInsets(WindowInsetsCompat.Type.systemBars())
            root.updateLayoutParams<ViewGroup.MarginLayoutParams> {
                leftMargin = inset.left
                bottomMargin = inset.bottom
                rightMargin = inset.right
                topMargin = 0
            }
            WindowInsetsCompat.CONSUMED
        }
    }
}
要再次显示状态栏,只需要进行一些更改即可:
fun setStatusBarShown(activity: Activity, view: View) {
    activity.apply {
        window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
        window.statusBarColor = ContextCompat.getColor(this, R.color.transparent)
        WindowCompat.setDecorFitsSystemWindows(window, false)
        ViewCompat.setOnApplyWindowInsetsListener(view) { root, windowInset ->
            val inset = windowInset.getInsets(WindowInsetsCompat.Type.systemBars())
            val inset1 = windowInset.getInsets(WindowInsetsCompat.Type.statusBars())
            root.updateLayoutParams<ViewGroup.MarginLayoutParams> {
                leftMargin = inset.left
                bottomMargin = inset.bottom
                topMargin = inset1.top
                rightMargin = inset.right
            }
            WindowInsetsCompat.CONSUMED
        }
    }
}

我把那个函数放在Object类中,命名为UiUtils,这样当我在我的活动中调用该函数时(我也使用了View Binding),它会像这样:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    ...
    UiUtils.setStatusBarTransparent(this, bind.root)
    ...
}

希望我的回答能够帮到你们 :)


调用此函数后,如何将活动恢复到先前的状态? - iknow
也许在进行更改之前保存状态是正确的答案。 - Liong
1
嗨,请再检查一下我的答案,我已经更新了它以再次显示状态栏。 - Liong
1
是的,如果您能再次将状态栏设置为透明,就可以重新从系统栏中设置插入。 - Liong
inset 和 inset1 有什么区别?看起来一样。 - Nursultan Almakhanov
显示剩余2条评论

11

在状态栏下绘制布局:

values/styles.xml

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

values-v21/styles.xml

<item name="android:windowDrawsSystemBarBackgrounds">true</item>
<item name="android:statusBarColor">@color/colorPrimaryDark</item>

使用CoordinatorLayout/DrawerLayout,它们已经处理了fitsSystemWindows参数,或者创建自己的布局像这样:

public class FitsSystemWindowConstraintLayout extends ConstraintLayout {

    private Drawable mStatusBarBackground;
    private boolean mDrawStatusBarBackground;

    private WindowInsetsCompat mLastInsets;

    private Map<View, int[]> childsMargins = new HashMap<>();

    public FitsSystemWindowConstraintLayout(Context context) {
        this(context, null);
    }

    public FitsSystemWindowConstraintLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public FitsSystemWindowConstraintLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        if (ViewCompat.getFitsSystemWindows(this)) {
            ViewCompat.setOnApplyWindowInsetsListener(this, new android.support.v4.view.OnApplyWindowInsetsListener() {
                @Override
                public WindowInsetsCompat onApplyWindowInsets(View view, WindowInsetsCompat insets) {
                    FitsSystemWindowConstraintLayout layout = (FitsSystemWindowConstraintLayout) view;
                    layout.setChildInsets(insets, insets.getSystemWindowInsetTop() > 0);
                    return insets.consumeSystemWindowInsets();
                }
            });
            setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                    | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
            TypedArray typedArray = context.obtainStyledAttributes(new int[]{android.R.attr.colorPrimaryDark});
            try {
                mStatusBarBackground = typedArray.getDrawable(0);
            } finally {
                typedArray.recycle();
            }
        } else {
            mStatusBarBackground = null;
        }
    }

    public void setChildInsets(WindowInsetsCompat insets, boolean draw) {
        mLastInsets = insets;
        mDrawStatusBarBackground = draw;
        setWillNotDraw(!draw && getBackground() == null);

        for (int i = 0; i < getChildCount(); i++) {
            View child = getChildAt(i);
            if (child.getVisibility() != GONE) {
                if (ViewCompat.getFitsSystemWindows(this)) {
                    ConstraintLayout.LayoutParams layoutParams = (ConstraintLayout.LayoutParams) child.getLayoutParams();

                    if (ViewCompat.getFitsSystemWindows(child)) {
                        ViewCompat.dispatchApplyWindowInsets(child, insets);
                    } else {
                        int[] childMargins = childsMargins.get(child);
                        if (childMargins == null) {
                            childMargins = new int[]{layoutParams.leftMargin, layoutParams.topMargin, layoutParams.rightMargin, layoutParams.bottomMargin};
                            childsMargins.put(child, childMargins);
                        }
                        if (layoutParams.leftToLeft == LayoutParams.PARENT_ID) {
                            layoutParams.leftMargin = childMargins[0] + insets.getSystemWindowInsetLeft();
                        }
                        if (layoutParams.topToTop == LayoutParams.PARENT_ID) {
                            layoutParams.topMargin = childMargins[1] + insets.getSystemWindowInsetTop();
                        }
                        if (layoutParams.rightToRight == LayoutParams.PARENT_ID) {
                            layoutParams.rightMargin = childMargins[2] + insets.getSystemWindowInsetRight();
                        }
                        if (layoutParams.bottomToBottom == LayoutParams.PARENT_ID) {
                            layoutParams.bottomMargin = childMargins[3] + insets.getSystemWindowInsetBottom();
                        }
                    }
                }
            }
        }

        requestLayout();
    }

    public void setStatusBarBackground(Drawable bg) {
        mStatusBarBackground = bg;
        invalidate();
    }

    public Drawable getStatusBarBackgroundDrawable() {
        return mStatusBarBackground;
    }

    public void setStatusBarBackground(int resId) {
        mStatusBarBackground = resId != 0 ? ContextCompat.getDrawable(getContext(), resId) : null;
        invalidate();
    }

    public void setStatusBarBackgroundColor(@ColorInt int color) {
        mStatusBarBackground = new ColorDrawable(color);
        invalidate();
    }

    @Override
    public void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (mDrawStatusBarBackground && mStatusBarBackground != null) {
            int inset = mLastInsets != null ? mLastInsets.getSystemWindowInsetTop() : 0;
            if (inset > 0) {
                mStatusBarBackground.setBounds(0, 0, getWidth(), inset);
                mStatusBarBackground.draw(canvas);
            }
        }
    }
}

主活动的xml文件

<FitsSystemWindowConstraintLayout 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_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true">

    <ImageView
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:fitsSystemWindows="true"
        android:scaleType="centerCrop"
        android:src="@drawable/toolbar_background"
        app:layout_constraintBottom_toBottomOf="@id/toolbar"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="0dp"
        android:layout_height="?attr/actionBarSize"
        android:background="@android:color/transparent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <LinearLayout
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:gravity="center"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@id/toolbar">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:text="Content"
            android:textSize="48sp" />
    </LinearLayout>
</FitsSystemWindowConstraintLayout>

结果:

截图:
截图


4
那不是一个透明的状态栏,那是一个半透明的状态栏。 - Marty Miller

11

简单清晰,适用于几乎所有用例(适用于 API 级别 16 及以上):

  1. 在应用主题中使用以下标签可使状态栏透明:

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

  2. 然后在 Activity 的 onCreate 方法中使用以下代码。

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

这就是你需要做的全部内容 ;)

你可以从开发者文档中学到更多知识。我还推荐阅读这篇博客文章。

KOTLIN 代码:

    val decorView = window.decorView
    decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE
            or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN)

请查看我的另一个回答这里


这会破坏SOFT_INPUT_ADJUST_RESIZE和SOFT_INPUT_ADJUST_PAN。 - mikep
1
SYSTEM_UI_FLAG_LAYOUT_STABLE和SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN已经被弃用。 - The MJ
@TheMJ 是的,它们已经被弃用了,必须进行一些研究以找到替代方案。 - Shreemaan Abhishek
systemUiVisibility已被弃用 - famfamfam

10

如果您的API版本>23并且支持白天/黑夜模式,您可以使用下面的扩展。重要的是要理解android:fitsSystemWindows="true"使用填充来在内嵌物(例如工具栏)中移动。因此,在根布局中放置它没有意义(除了DrawerLayout,CoordinatorLayout等这些使用自己的实现)。

enter image description here

<style name="Theme.YourApp.DayNight" parent="Theme.MaterialComponents.DayNight.NoActionBar">
    ...
    <item name="android:windowLightStatusBar">@bool/isDayMode</item>
    <item name="android:statusBarColor">@android:color/transparent</item>
</style>

<androidx.constraintlayout.widget.ConstraintLayout
    ...>

    <com.google.android.material.appbar.MaterialToolbar
        ...
        android:fitsSystemWindows="true">

</androidx.constraintlayout.widget.ConstraintLayout

fun Activity.transparentStatusBar() {
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
        window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN)
    } else {
        window.setDecorFitsSystemWindows(false)
    }
}

然后这样调用:

class YourActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        ...
        transparentStatusBar()
    }
}

查看Chris Banes的这些幻灯片:成为窗户安装专家

编辑:如果您在内容浮动时遇到问题,请使用:

// using Insetter
binding.yourViewHere.applySystemWindowInsetsToPadding(bottom = true)

我认为你也可以使用支持库WindowCompat.setDecorFitsSystemWindows(window, false)。 - Andre Thiele
哇,感谢在搜索互联网多个月后终于找到了。但是要知道,只有 Kotlin 代码可以正常工作。这意味着 fitsSystemWindows 根本不起作用。 - chisom emmanuel

10

这是一个 Kotlin 扩展,可以实现这个功能:

fun Activity.setTransparentStatusBar() {
    window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        window.statusBarColor = Color.TRANSPARENT
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接