半透明/透明状态栏+CoordinatorLayout+工具栏+片段。

27
我有以下设置:
  • 我使用AppCompat
  • MainActivity持有一个fragment并具有工具栏,当向下滚动时会隐藏
  • Fragment中包含RecyclerView
  • 所有应适合屏幕的视图都在xml布局中具有相应的android:fitsSystemWindows="true"

问题是,在这种情况下我无法使状态栏透明。我做的是以下操作:

  1. 创建活动并调用setContent
  2. 然后我尝试通过编程方式调整活动以获得半透明工具栏,如下所示:

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public void themeNavAndStatusBar(Activity activity)
    {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
            return;
    
        Window w = activity.getWindow();
        w.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
        w.setFlags(
                WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION,
                WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
        w.setFlags(
                WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS,
                WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
        w.setNavigationBarColor(activity.getResources().getColor(android.R.color.transparent));
    
        w.setStatusBarColor(activity.getResources().getColor(android.R.color.transparent));
    }
    
  3. 将活动中的占位符(@+id/frame_container)替换为片段

在这种情况下,状态栏是实色的,并且视图不会被绘制在其下面...为什么?

我想要的

我想要一个工具栏,它可以滚动到屏幕上并完全隐藏,而此工具栏下面的内容应该适合屏幕并在透明的导航栏后面绘制。

布局

这是我的主要活动:

<android.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/clMain"
    android:fitsSystemWindows="true"
    android:background="?attr/main_background_color"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.design.widget.AppBarLayout
        android:id="@+id/appBarLayout"
        android:fitsSystemWindows="true"
        android:background="@null"
        app:elevation="0dp"
        app:contentInsetLeft="0dp"
        app:contentInsetStart="0dp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            android:elevation="4dp"
            android:theme="?actionBarThemeStyle"
            app:popupTheme="?actionBarPopupThemeStyle"
            app:layout_scrollFlags="scroll|enterAlways">

            <LinearLayout
                android:orientation="vertical"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content">

                <LinearLayout
                    android:orientation="horizontal"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content">

                    <ImageView
                        android:id="@+id/ivToolbarDataSource"
                        android:layout_gravity="center_vertical"
                        android:layout_marginRight="2dp"
                        android:layout_width="24dp"
                        android:layout_height="24dp" />

                    <TextView
                        android:id="@+id/tvToolbarTitle"
                        style="@style/TextAppearance.AppCompat.Widget.ActionBar.Title"
                        android:theme="?actionBarThemeStyle"
                        android:layout_gravity="center_vertical"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content" />

                </LinearLayout>

                <TextView
                    android:id="@+id/tvToolbarSubTitle"
                    style="@style/TextAppearance.AppCompat.Widget.ActionBar.Subtitle"
                    android:theme="?actionBarThemeStyle"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content" />

            </LinearLayout>



        </android.support.v7.widget.Toolbar>

        <!-- BUG: https://dev59.com/6l0a5IYBdhLWcg3wRGrp -->
        <View
            android:layout_width="fill_parent"
            android:layout_height="1dp"/>

    </android.support.design.widget.AppBarLayout>

    <FrameLayout
        android:id="@+id/frame_container"
        android:fitsSystemWindows="true"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior" />

    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|right"
        android:layout_margin="32dp"
        android:src="@drawable/ic_local_offer_white_24dp"
        app:backgroundTint="?attr/colorPrimary"
        app:borderWidth="0dp"
        app:fabSize="normal"
        app:rippleColor="?attr/colorPrimaryDark"
        app:layout_anchorGravity="bottom|right|end"
        app:layout_behavior="com.test.classes.ScrollAwareFABBehavior"/>

</android.support.design.widget.CoordinatorLayout>

这是我的片段,它将放置在主活动中:

<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:fitsSystemWindows="true"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.v4.widget.SwipeRefreshLayout
        android:id="@+id/srlImages"
        android:fitsSystemWindows="true"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <android.support.v7.widget.RecyclerView
            android:id="@+id/rvImages"
            android:fitsSystemWindows="true"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

    </android.support.v4.widget.SwipeRefreshLayout>

    <TextView
        android:id="@+id/tvEmpty"
        android:gravity="center"
        android:layout_centerInParent="true"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</RelativeLayout>

编辑 - 屏幕截图

我使用浅色/深色主题,并手动主题化所有内容(因为用户可以选择任何颜色作为主色/强调色),所以请不要在意工具栏是白色的(这是默认的主题背景颜色和主色调)。 我还添加了黑色边框,以便您看到活动结束的位置...

  • 第一张截图:显示工具栏,没有滚动
  • 第二张截图:我刚开始滚动 => 工具栏现在应该滚动消失
  • 第三张截图:现在主要内容应该在导航栏下面滚动...

最终,我当然会使工具栏和导航栏半透明以获得更好的视觉效果...

输入图片说明 输入图片说明 输入图片说明


你能发布一些图片吗?你应该将fitsSystemWindows设置为false以适应CoordinatorLayout(至少)。 - natario
我添加了截图以展示问题... - prom85
将fitsSystemWindows属性设置为false,分别应用于CoordinatorLayout和frame_container。 - natario
10个回答

70

tl;dr 在根CoordinatorLayout和内部片段容器@frame_container中至少设置android:fitsSystemWindows="false"

这可能不是最终的解决方案(即可能还有其他要更改的fitsSystemWindows),如果您遇到任何问题,请告诉我。

为什么

当涉及状态栏时,我认为fitsSystemWindows的作用如下:

  • fitsSystemWindows="false":以正常方式绘制视图,由于您设置的窗口标志,它会在状态栏下面绘制。
  • fitsSystemWindows="true":以正常方式绘制视图,由于您设置的窗口标志,它会在状态栏下面绘制,但添加顶部填充以使内容在状态栏下面绘制,而不是重叠。

实际上,我认为您看到的白色并不是状态栏颜色,而是您的CoordinatorLayout背景。这是因为协调器上的fitsSystemWindows="true":它将背景绘制到整个窗口,并添加了顶部填充以使内部视图不被状态栏覆盖。

这不是您想要的。您的内部内容必须被状态栏覆盖,因此您必须将fitsSystemWindows="false"设置为协调器(这样它就不会应用顶部填充),可能也要将其设置为内容本身。

一旦你理解了它的工作原理,调试和实现你想要的效果就变得容易实际上并非如此。数年过去了,但我仍花费数小时来尝试找出正确的fitsSystemWindows组合方式,因为大多数视图(至少在支持库中)以不太直观的方式覆盖了我上述提到的默认行为。请参见这篇文章,了解如何使用它的简要指南。

5
android:fitsSystemWindows="false"的问题在于工具栏也会隐藏在状态栏后面。 - Hammad Nasir
事实上,在我看来,你看到的白色并不是状态栏颜色,而是你的CoordinatorLayout背景。非常感谢! - lpfx
@HammadNasir 我认为更好的方法是设置 android:fitsSystemWindows="true",并在 coordinateLayout 中设置 app:statusBarBackground="@android:color/transparent" - MHSaffari
请查看此答案:https://dev59.com/yuk6XIcBkEYKwwoYDPoQ#47906087 - MHSaffari

13

编辑 styles.xml(v21)文件,添加以下样式:

 <style name="AppTheme.Home" parent="AppTheme.Base">
    <!-- Customize your theme here. -->
    <item name="android:windowTranslucentStatus">true</item>
    <item name="android:windowDrawsSystemBarBackgrounds">true</item>
    <item name="android:windowTranslucentNavigation">true</item>

</style>

您可以根据自己的喜好更改父主题,但现在要为特定活动在AndroidManifest.xml文件中声明此主题,如下所示:

 <activity
        android:theme="@style/AppTheme.Home"
        android:name=".HomeActivity"
        android:launchMode="singleTop"
        android:screenOrientation="portrait" />

这将使您的内容在透明的操作栏下可见。

现在使用以下方法在状态栏下正确地对齐您的工具栏,在oncreate中调用此方法:

 toolbar.setPadding(0, getStatusBarHeight(), 0, 0);

使用以下方法来获取状态栏高度:

public int getStatusBarHeight() {
    int result = 0;
    int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
    if (resourceId > 0) {
        result = getResources().getDimensionPixelSize(resourceId);
    }
    return result;
}

从您的 coordinator layout 标签中移除以下内容:

android:fitsSystemWindows="true"

现在,如果您想折叠或隐藏工具栏,您可以参考这个教程

请确保您正在使用以下版本的设计支持库,因为它是无bug的:

compile 'com.android.support:design:23.1.0'


2
谢谢,你的解决方案有效。我重写了将ToolBar对齐到StatusBar下面的代码,不仅添加了padding,还给ToolBar增加了高度。我的意思是toolbar.getLayoutParams().height += getStatusBarHeight(); - CoolMind
3
另外,AppBarLayout.LayoutParams lp = (AppBarLayout.LayoutParams) toolbar.getLayoutParams(); lp.topMargin += getStatusBarHeight(); - CoolMind

5

在阅读您关于问题的描述后,我认为 Google 相册 的样式符合您的要求。

好的,以下是一些有用的提示。在我的测试中,它可行。

  • 如果您想要显示状态栏后面的内容,则需要在 Android 版本大于 19(即奇巧巧克力)时,在您的样式文件中添加 <item name="android:windowTranslucentStatus">true</item>
  • 如果您想要显示导航栏后面的内容,则需要在 Android 版本大于 19(即奇巧巧克力)时,在您的样式文件中添加 <item name="android:windowTranslucentNavigation">true</item>
  • 如果您希望在向上滚动内容时平稳地隐藏 Toolbar,并在向下滚动内容时平稳地显示 Toolbar,则需要根据您当前的代码将 app:layout_collapseMode="parallax" 添加到您的 Toolbar 属性中。当然,您需要将 ToolbarCollapsingToolbarLayoutCoordinatorLayoutAppBarLayout 协调起来。

3
你把状态栏和导航栏在描述文本中搞混了吧? - TWiStErRob
我搞混了什么? - SilentKnight
1
@SilentKnight,“如果您想要在状态栏后面显示内容”,以及“android:windowTranslucentNavigation”。也许您已经混淆了描述。但是非常感谢,这很有帮助。 - CoolMind

3

正如一些用户所说,通过设置android:fitsSystemWindows="false",布局会在statusbar下方重叠

我通过设置android:fitsSystemWindows="true"并在CoordinatorLayout标签中设置app:statusBarBackground="@android:color/transparent"来解决这个问题。


哦,谢谢啊!我花了半天时间才找出来为什么我的片段会增加状态栏的高度。 - Fragment
惊人的发现 ;) - Johnathan Awma

1

对我来说,问题不在于它本身无法工作,而是因为我使用了Mike Penz的材料抽屉库,该库使用全屏+偏移量+自定义背景在工具栏后面,所以我必须解决这个问题,尊重这个特殊的设置...

尽管如此,我会将奖励点数授予我认为最具信息价值的答案...


2
v5.0.0 将停止执行偏移等操作。它将使用平台默认值并依赖于 fitsSystemWindows 标志。还有折叠工具栏示例 https://github.com/mikepenz/MaterialDrawer/blob/develop/app/src/main/java/com/mikepenz/materialdrawer/app/CollapsingToolbarActivity.java - mikepenz

0

我曾经遇到过同样的问题,我的解决方案是在DrawerLayout中添加android:fitsSystemWindows="true"

<android.support.v4.widget.DrawerLayout 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:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"> 

....

</android.support.v4.widget.DrawerLayout>

抱歉,它没有帮助到我。请参考上面的答案,它们可能会有所帮助。 - CoolMind

0
你应该将以下代码添加到你的主题中,这段代码将使状态栏透明:
<item name="android:windowTranslucentStatus" tools:targetApi="kitkat">true</item>

此外,将滚动行为添加到您的顶部应用栏。以下示例显示顶部应用栏在向上滚动时消失,在向下滚动时出现:
<androidx.coordinatorlayout.widget.CoordinatorLayout
...>

<com.google.android.material.appbar.AppBarLayout
    ...>

    <com.google.android.material.appbar.MaterialToolbar
        ...
        app:layout_scrollFlags="scroll|enterAlways|snap"
        />

</com.google.android.material.appbar.AppBarLayout>
 ...
</androidx.coordinatorlayout.widget.CoordinatorLayout>

0
首先将此代码添加到AppBarLayout:
 app:liftOnScroll="true"

然后在您的工具栏中更新您的“app:layout_scrollFlags”:

  app:layout_scrollFlags="scroll|enterAlways|snap"

这个应该可以,我非常确定。


0

这是我所做的,使工具栏与状态栏具有相同的颜色,并使状态栏透明:

build.gradle

...
    implementation 'androidx.core:core-ktx:1.2.0'
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'com.google.android.material:material:1.1.0'


**ScrollingActivity.kt**
```kt
class ScrollingActivity : AppCompatActivity(R.layout.activity_scrolling) {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setSupportActionBar(toolbar)
    }
}

activity_scrolling.xml

<androidx.coordinatorlayout.widget.CoordinatorLayout 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"
    tools:context=".ScrollingActivity">

    <com.google.android.material.appbar.AppBarLayout
        android:id="@+id/app_bar" android:layout_width="match_parent" android:layout_height="wrap_content"
        android:background="#f00" android:fitsSystemWindows="true" android:theme="@style/AppTheme.AppBarOverlay">

        <com.google.android.material.appbar.MaterialToolbar
            android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize"
            app:popupTheme="@style/AppTheme.PopupOverlay" />
    </com.google.android.material.appbar.AppBarLayout>

    <androidx.core.widget.NestedScrollView
        android:layout_width="match_parent" android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

        <TextView
            android:layout_width="wrap_content" android:layout_height="wrap_content"
            android:layout_margin="@dimen/text_margin" android:text="@string/large_text" />

    </androidx.core.widget.NestedScrollView>

</androidx.coordinatorlayout.widget.CoordinatorLayout>

styles.xml

<resources>
    <style name="AppTheme" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>

    <style name="AppTheme.NoActionBar">
        <item name="windowActionBar">false</item>
        <item name="windowNoTitle">true</item>
        <item name="android:statusBarColor">@android:color/transparent</item>
    </style>

    <style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />

    <style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />

</resources>

清单

<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.lb.myapplication">

    <application
        android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme">
        <activity
            android:name=".ScrollingActivity" android:label="@string/app_name"
            android:theme="@style/AppTheme.NoActionBar">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

0

我遇到了与android:fitsSystemWindows设置相关的问题。 当设置为false时: 小吃被绘制在导航栏下面 当设置为true时: 状态栏没有透明背景

解决方案非常简单... 只需像这样向CoordinatorLayout添加android:layout_marginBottom="48dp":

just to add <android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:map="http://schemas.android.com/apk/res-auto"
tools:context=".MapsActivity"
android:id="@+id/coordinatorLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"

android:fitsSystemWindows="false"
android:layout_marginBottom="48dp">

理论上,导航栏应该有固定的大小“48dp”,但在未来的版本中可能会发生变化(就像状态栏在Marshmallow中减少了1dp),因此我不会依赖于固定的大小。最好在运行时获取并应用它。
如果您像我一样使用Google地图,您可能想要在运行时知道ActionBar/Toolbar的大小和导航栏:
在onCreate中使用以下代码:
            final TypedArray styledAttributes = MapsActivity.this.getTheme().obtainStyledAttributes(
                new int[]{android.R.attr.actionBarSize});
        mToolbarHeight = (int) styledAttributes.getDimension(0, 0);
        styledAttributes.recycle();
        // translucent bars code. Will not fire below Lollipop
        // Ask NavigationBar Height
        ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.coordinatorLayout),
                new OnApplyWindowInsetsListener() { // setContentView() must be fired already
                    @Override
                    public WindowInsetsCompat onApplyWindowInsets(View v, WindowInsetsCompat insets) {
                        statusBar = insets.getSystemWindowInsetTop(); // You may also need this value
                        mNavBarHeight = insets.getSystemWindowInsetBottom();
                        if (mMap != null)
                            mMap.setPadding(0, mToolbarHeight, 0, mNavBarHeight);
                        // else will be set in onMapReady()
                        SharedPreferences.Editor editor = mSharedPref.edit();
                        editor
                                .putInt(NAVBAR_HEIGHT_KEY, mNavBarHeight)
                                .commit(); // Save the results in flash memory and run the code just once on app first run instead of doing it every run
                        return insets;
                    }
                }
        );

还有一点很重要。如果您有一些额外的层,比如抽屉等,请将它们封装在CoordinatorLayout内部,而不是外部,否则它会使内部其他视图的marginBottom变短。


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