简体   繁体   English

将导航抽屉放在状态栏下

[英]Put Navigation Drawer Under Status Bar

I'm trying to make my navigation drawer go under the status bar. 我正在尝试让我的导航抽屉进入状态栏。 I've read extensively about the ScrimInsetsFrameLayout view and I tried implementing it, but for some reason it won't go under. 我已经阅读了很多关于ScrimInsetsFrameLayout视图的内容,并尝试实现它,但由于某些原因它不会被删除。

Here's the code that I used/wrote. 这是我使用/编写的代码。

XML DrawerLayout: XML DrawerLayout:

<android.support.v4.widget.DrawerLayout
    android:id="@+id/drawer_layout"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <FrameLayout
            android:id="@+id/content_frame"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

        <include layout="@layout/toolbar" />

    </FrameLayout>

    <com.andrewq.planets.util.ScrimInsetsFrameLayout
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/linearLayout"
        android:layout_width="304dp"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:fitsSystemWindows="true"
        app:insetForeground="#4000">

        <ListView
            android:id="@+id/left_drawer"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:choiceMode="singleChoice" />
    </com.andrewq.planets.util.ScrimInsetsFrameLayout>


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

ScrimInsetsFrameLayout.java: ScrimInsetsFrameLayout.java:

package com.andrewq.planets.util;

/*
* Copyright 2014 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.support.v4.view.ViewCompat;
import android.util.AttributeSet;
import android.widget.FrameLayout;

import com.andrewq.planets.R;

/**
 * A layout that draws something in the insets passed to {@link #fitSystemWindows(Rect)}, i.e. the area above UI chrome
 * (status and navigation bars, overlay action bars).
 */
public class ScrimInsetsFrameLayout extends FrameLayout {
    private Drawable mInsetForeground;

    private Rect mInsets;
    private Rect mTempRect = new Rect();
    private OnInsetsCallback mOnInsetsCallback;

    public ScrimInsetsFrameLayout(Context context) {
        super(context);
        init(context, null, 0);
    }

    public ScrimInsetsFrameLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs, 0);
    }

    public ScrimInsetsFrameLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(context, attrs, defStyle);
    }

    private void init(Context context, AttributeSet attrs, int defStyle) {
        final TypedArray a = context.obtainStyledAttributes(attrs,
                R.styleable.ScrimInsetsView, defStyle, 0);
        if (a == null) {
            return;
        }
        mInsetForeground = a.getDrawable(R.styleable.ScrimInsetsView_insetForeground);
        a.recycle();

        setWillNotDraw(true);
    }

    @Override
    protected boolean fitSystemWindows(Rect insets) {
        mInsets = new Rect(insets);
        setWillNotDraw(mInsetForeground == null);
        ViewCompat.postInvalidateOnAnimation(this);
        if (mOnInsetsCallback != null) {
            mOnInsetsCallback.onInsetsChanged(insets);
        }
        return true; // consume insets
    }

    @Override
    public void draw(Canvas canvas) {
        super.draw(canvas);

        int width = getWidth();
        int height = getHeight();
        if (mInsets != null && mInsetForeground != null) {
            int sc = canvas.save();
            canvas.translate(getScrollX(), getScrollY());

            // Top
            mTempRect.set(0, 0, width, mInsets.top);
            mInsetForeground.setBounds(mTempRect);
            mInsetForeground.draw(canvas);

            // Bottom
            mTempRect.set(0, height - mInsets.bottom, width, height);
            mInsetForeground.setBounds(mTempRect);
            mInsetForeground.draw(canvas);

            // Left
            mTempRect.set(0, mInsets.top, mInsets.left, height - mInsets.bottom);
            mInsetForeground.setBounds(mTempRect);
            mInsetForeground.draw(canvas);

            // Right
            mTempRect.set(width - mInsets.right, mInsets.top, width, height - mInsets.bottom);
            mInsetForeground.setBounds(mTempRect);
            mInsetForeground.draw(canvas);

            canvas.restoreToCount(sc);
        }
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        if (mInsetForeground != null) {
            mInsetForeground.setCallback(this);
        }
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        if (mInsetForeground != null) {
            mInsetForeground.setCallback(null);
        }
    }

    /**
     * Allows the calling container to specify a callback for custom processing when insets change (i.e. when
     * {@link #fitSystemWindows(Rect)} is called. This is useful for setting padding on UI elements based on
     * UI chrome insets (e.g. a Google Map or a ListView). When using with ListView or GridView, remember to set
     * clipToPadding to false.
     */
    public void setOnInsetsCallback(OnInsetsCallback onInsetsCallback) {
        mOnInsetsCallback = onInsetsCallback;
    }

    public static interface OnInsetsCallback {
        public void onInsetsChanged(Rect insets);
    }
}

And finally, here's my styles.xml for values-v21: 最后,这是我的styles.xml for values-v21:

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <style name="AppThemeNavDrawer" parent="Theme.AppCompat.NoActionBar">
        <item name="colorAccent">#F8F8F8</item>
        <item name="android:windowTranslucentStatus">true</item>

        <item name="windowActionBar">false</item>
        <item name="windowActionModeOverlay">true</item>
        <item name="android:windowDrawsSystemBarBackgrounds">true</item>
        <item name="android:statusBarColor">@android:color/transparent</item>
    </style>
</resources>

I've looked at the 2014 I/O app source code as well as this question, and I don't know what is so different. 我查看了2014 I / O应用程序源代码以及这个问题,我不知道有什么不同。

Here's a screenshot of what I have so far minus the drawer under the status bar: 这是我到目前为止的截图,减去状态栏下的抽屉: 截图

I've got everything else working perfectly and this is the last thing I need to do. 我已经完成了其他一切工作,这是我需要做的最后一件事。 Help is greatly appreciated! 非常感谢帮助!

Edit: 编辑:

To clarify, I want to have the image be tinted under the status bar just like in most of the Google Apps and Google Now. 为了澄清,我想让图像在状态栏下着色,就像大多数Google Apps和Google Now一样。

android:fitsSystemWindows="false"到DrawerLayout将解决问题

There are different approaches to get to the desired result. 有不同的方法来达到预期的结果。 You can enable translucent via style or via code. 您可以通过样式或代码启用半透明。

I've created a MaterialDrawer (which follows the Android Material Design Guidelines) which implements all of this and handles everything for you. 我创建了一个MaterialDrawer(遵循Android Material Design Guidelines),它实现了所有这些并为您处理所有事情。 Read more here: https://github.com/mikepenz/MaterialDrawer/ 在这里阅读更多内容: https//github.com/mikepenz/MaterialDrawer/

If you want to create it on your own you always have to decide which is the lowest api you want to support and/or if you have to split up your styles. 如果你想自己创建它,你总是要决定哪个是你想要支持的最低api和/或你是否需要拆分你的样式。

So to enable translucentStatusbar you have to be at least on API v19 or you create a separat style for v19+ values-v19 This will look somehow like this 因此,要启用translucentStatusbar,您必须至少在API v19上,或者为v19 + values-v19创建一个分离样式。这看起来有点像这样

<style name="YourTheme.TranslucentStatus" parent="Theme.AppCompat.Light.NoActionBar">
    <item name="android:windowTranslucentStatus">true</item>
</style>

So now this will move your complete layout below the statusbar. 所以现在这将把你的完整布局移到状态栏下面。 In almost all cases you will now want to add the padding on the top of the drawer content and your normal view content. 几乎在所有情况下,您现在都想在抽屉内容和普通视图内容的顶部添加填充。

You can do this by adding 24dp padding. 您可以通过添加24dp填充来完成此24dp

This is not a really nice implementation. 这不是一个非常好的实现。 So there's a different approach by using the ScrimInsetsLayout which is used in the Google IO 2014 app. 因此,使用Google IO 2014应用中使用的ScrimInsetsLayout有不同的方法。 https://github.com/google/iosched/blob/master/android/src/main/java/com/google/samples/apps/iosched/ui/widget/ScrimInsetsFrameLayout.java https://github.com/google/iosched/blob/master/android/src/main/java/com/google/samples/apps/iosched/ui/widget/ScrimInsetsFrameLayout.java

This will be your contents layout and you can set the color for the statusbar on it. 这将是您的内容布局,您可以在其上设置状态栏的颜色。 You can find a detailed instruction, on how you can use it, here: https://stackoverflow.com/a/26932228 您可以在此处找到有关如何使用它的详细说明: https//stackoverflow.com/a/26932228

It requires some time to get used to the styles and / or the ScrimInsetsLayout . 它需要一些时间来习惯样式和/或ScrimInsetsLayout

EDIT: 编辑:

A more complex sample on how you can handle this programmatically: 有关如何以编程方式处理此问题的更复杂示例:

if (Build.VERSION.SDK_INT >= 19 && Build.VERSION.SDK_INT < 21) {
    //enable translucent statusbar via flags
    setTranslucentStatusFlag(true);
}
if (Build.VERSION.SDK_INT >= 19) {
    mActivity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
}
if (Build.VERSION.SDK_INT >= 21) {
    //we don't need the translucent flag this is handled by the theme
    setTranslucentStatusFlag(false);
    //set the statusbarcolor transparent to remove the black shadow
    mActivity.getWindow().setStatusBarColor(Color.TRANSPARENT);
}

//add a padding to the content of the drawer (25dp on devices starting with api v19)
mDrawerContentRoot.setPadding(0, mActivity.getResources().getDimensionPixelSize(R.dimen.tool_bar_top_padding), 0, 0);

// define the statusBarColor
mDrawerContentRoot.setInsetForeground(mStatusBarColor);


private void setTranslucentStatusFlag(boolean on) {
    if (Build.VERSION.SDK_INT >= 19) {
        Window win = mActivity.getWindow();
        WindowManager.LayoutParams winParams = win.getAttributes();
        final int bits = WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
        if (on) {
            winParams.flags |= bits;
        } else {
            winParams.flags &= ~bits;
        }
        win.setAttributes(winParams);
    }
}

EDIT2: EDIT2:

The complete solution to fix this issue was to clean up all the layouts which were in the project. 解决此问题的完整解决方案是清理项目中的所有布局。 some combination of the layouts and styles were causing the troubles. 布局和样式的某种组合导致了麻烦。

The complete changes can be found in this pull request: https://github.com/Andrew-Quebe/Planets-Gradle/commit/83e28c09253af6e807b6f4e94baca8fbca3fc7c8 完整的更改可以在此拉取请求中找到: https//github.com/Andrew-Quebe/Planets-Gradle/commit/83e28c09253af6e807b6f4e94baca8fbca3fc7c8

You need to make a new styles.xml and put that file in style-v19 folder because the status bar translucent method is not available for pre kitkat devices. 您需要创建一个新的styles.xml并将该文件放在style-v19文件夹中,因为状态栏半透明方法不适用于pre kitkat设备。

<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar" >
    <item name="android:windowTranslucentStatus">true</item>
</style>

After that you will see the your Application is under the Status bar But you need to give padding to toolBar for the Exact implementation.Create a dimen-v19 and add 之后,您将看到您的应用程序位于状态栏下但您需要为工具栏提供填充以进行精确实现。创建一个维度-v19并添加

<dimen name="ToolBarPaddingTop">24dp</dimen>

Use it in ToolBar 在ToolBar中使用它

<android.support.v7.widget.Toolbar xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@color/accent_material_light"
    android:theme="@style/ThemeOverlay.AppCompat.Dark"
    android:fitsSystemWindows="true"
    android:paddingTop="@dimen/ToolBarPaddingTop">

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

This is what worked for me: 这对我有用:

  • Set android:windowTranslucentStatus to true in your default style in the v21 folder. 在v21文件夹的默认样式中将android:windowTranslucentStatus设置为true This will make your statusbar almost transparent from Lollipop. 这将使你的状态栏几乎从Lollipop透明。
  • Define the color of your Toolbar with the android:colorPrimary parameter in your style. 使用样式中的android:colorPrimary参数定义Toolbar的颜色。
  • Do NOT set android:fitsSystemWindows="true" in your layout. 不要在布局中设置android:fitsSystemWindows="true"
  • Add a 24dp padding to the top of your toolbar AND to the top of your drawer as well, but only from Lollyipop. 添加一个24dp填充到工具栏顶部和抽屉顶部,但只能从Lollyipop。 The best way to do this is to add a new dimension to your dimens files. 最好的方法是在尺寸文件中添加新尺寸。 In the values folder set it to 0, in the values-v21 set it to 24 dp, and use it on your Toolbar and Drawer . 在values文件夹中将其设置为0,在值-v21中将其设置为24 dp,并在ToolbarDrawer上使用它。

我相信你需要将android:fitsSystemWindows="true"android.support.v4.widget.DrawerLayout标签中。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM