如何使用DrawerLayout来显示在ActionBar /工具栏和状态栏下?

我在新的材料deviseSide Nav规范中看到,您可以在操作栏和状态栏后面显示抽屉。 我怎样才能实现呢?

框架和支持库中的新function可以完全实现这一点。 有三个难题:

  1. 使用工具栏,以便您可以将您的操作栏embedded到视图层次结构中。
  2. 使DrawerLayout适合 fitsSystemWindows以便它放在系统栏之后。
  3. 禁用Theme.Material的正常状态栏着色,以便DrawerLayout可以在那里绘制。

我假设你将使用新的appcompat。

首先,你的布局应该是这样的:

 <!-- The important thing to note here is the added fitSystemWindows --> <android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/my_drawer_layout" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true"> <!-- Your normal content view --> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <!-- We use a Toolbar so that our drawer can be displayed in front of the action bar --> <android.support.v7.widget.Toolbar android:id="@+id/my_awesome_toolbar" android:layout_height="wrap_content" android:layout_width="match_parent" android:minHeight="?attr/actionBarSize" android:background="?attr/colorPrimary" /> <!-- The rest of your content view --> </LinearLayout> <!-- Your drawer view. This can be any view, LinearLayout is just an example. As we have set fitSystemWindows=true this will be displayed under the status bar. --> <LinearLayout android:layout_width="304dp" android:layout_height="match_parent" android:layout_gravity="left|start" android:fitsSystemWindows="true"> <!-- Your drawer content --> </LinearLayout> </android.support.v4.widget.DrawerLayout> 

然后在你的Activity / Fragment中:

 public void onCreate(Bundled savedInstanceState) { super.onCreate(savedInstanceState); // Your normal setup. Blah blah ... // As we're using a Toolbar, we should retrieve it and set it // to be our ActionBar Toolbar toolbar = (...) findViewById(R.id.my_awesome_toolbar); setSupportActionBar(toolbar); // Now retrieve the DrawerLayout so that we can set the status bar color. // This only takes effect on Lollipop, or when using translucentStatusBar // on KitKat. DrawerLayout drawerLayout = (...) findViewById(R.id.my_drawer_layout); drawerLayout.setStatusBarBackgroundColor(yourChosenColor); } 

然后,您需要确保DrawerLayout在状态栏后面可见。 你通过改变你的values-v21主题来做到这一点:

值-V21 /的themes.xml

 <style name="Theme.MyApp" parent="Theme.AppCompat.Light.NoActionBar"> <item name="android:windowDrawsSystemBarBackgrounds">true</item> <item name="android:statusBarColor">@android:color/transparent</item> <item name="android:windowTranslucentStatus">true</item> </style> 

注意:如果使用<fragment android:name="fragments.NavigationDrawerFragment">而不是

 <LinearLayout android:layout_width="304dp" android:layout_height="match_parent" android:layout_gravity="left|start" android:fitsSystemWindows="true"> <!-- Your drawer content --> </LinearLayout> 

实际的布局,如果在从onCreateView方法返回的视图上调用fitsSystemWindows(boolean) ,将实现预期的效果。

 @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View mDrawerListView = inflater.inflate( R.layout.fragment_navigation_drawer, container, false); mDrawerListView.setFitsSystemWindows(true); return mDrawerListView; } 

编辑:新的devise支持库支持这和以前的方法不再需要。

现在可以使用新的Androiddevise支持库来实现这一点。

你可以看到Chris Banes的Cheesesquare示例应用程序 ,它演示了所有的新function。


先前的方法:

由于没有完整的解决scheme发布,这是我达到预期的结果的方式。

首先在你的项目中包含一个ScrimInsetsFrameLayout 。

 /* * 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. */ /** * A layout that draws something in the insets passed to * {@link #fitSystemWindows(Rect)}, ie 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 (ie when {@link #fitSystemWindows(Rect)} * is called. This is useful for setting padding on UI elements * based on UI chrome insets (eg 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); } } 

然后创build一个样式,以便可以设置insetForeground

值/ attrs.xml

 <declare-styleable name="ScrimInsetsView"> <attr name="insetForeground" format="reference|color" /> </declare-styleable> 

更新您的活动的xml文件,并确保在DrawerLayout以及DrawerLayout android:fitsSystemWindows设置为true。

布局/ activity_main.xml中

 <android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/drawerLayout" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true" tools:context=".MainActivity"> <!-- The main content view --> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <!-- Your main content --> </LinearLayout> <!-- The navigation drawer --> <com.example.app.util.ScrimInsetsFrameLayout xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/scrimInsetsFrameLayout" android:layout_width="320dp" android:layout_height="match_parent" android:layout_gravity="start" android:background="@color/white" android:elevation="10dp" android:fitsSystemWindows="true" app:insetForeground="#4000"> <!-- Your drawer content --> </com.example.app.util.ScrimInsetsFrameLayout> </android.support.v4.widget.DrawerLayout> 

在活动的onCreate方法中,在抽屉布局上设置状态栏背景颜色。

MainActivity.java

 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // ... mDrawerLayout = (DrawerLayout) findViewById(R.id.drawerLayout); mDrawerLayout.setStatusBarBackgroundColor( getResources().getColor(R.color.primary_dark)); } 

最后更新你的应用程序的主题,使DrawerLayout位于状态栏的后面。

值-V21 / styles.xml

 <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar"> <item name="android:windowDrawsSystemBarBackgrounds">true</item> <item name="android:statusBarColor">@android:color/transparent</item> </style> 

结果:

随着最新Android支持库(Rev 22.2.0)的发布,我们已经有了一个devise支持库,并作为这个称为NavigationView的新视图的一部分。 所以我们ScrimInsetsFrameLayout和其他所有东西来完成所有的工作,而只是使用这个视图,一切都为我们完成。

步骤1

Design Support Library添加到您的build.gradle文件

 dependencies { // Other dependencies like appcompat compile 'com.android.support:design:22.2.0' } 

第2步

NavigationView添加到DrawerLayout

 <android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/drawer_layout" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true"> <!-- this is important --> <!-- Your contents --> <android.support.design.widget.NavigationView android:id="@+id/navigation" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_gravity="start" app:menu="@menu/navigation_items" /> <!-- The items to display --> </android.support.v4.widget.DrawerLayout> 

第3步

/res/menu创build一个新的菜单资源,并添加您想要显示的项目和图标:

 <menu xmlns:android="http://schemas.android.com/apk/res/android"> <group android:checkableBehavior="single"> <item android:id="@+id/nav_home" android:icon="@drawable/ic_action_home" android:title="Home" /> <item android:id="@+id/nav_example_item_1" android:icon="@drawable/ic_action_dashboard" android:title="Example Item #1" /> </group> <item android:title="Sub items"> <menu> <item android:id="@+id/nav_example_sub_item_1" android:title="Example Sub Item #1" /> </menu> </item> </menu> 

步骤4

初始化NavigationView并处理点击事件:

 public class MainActivity extends AppCompatActivity { NavigationView mNavigationView; DrawerLayout mDrawerLayout; // Other stuff private void init() { mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); mNavigationView = (NavigationView) findViewById(R.id.navigation_view); mNavigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() { @Override public boolean onNavigationItemSelected(MenuItem menuItem) { mDrawerLayout.closeDrawers(); menuItem.setChecked(true); switch (menuItem.getItemId()) { case R.id.nav_home: // TODO - Do something break; // TODO - Handle other items } return true; } }); } } 

第5步

请务必在values-v21 android:windowDrawsSystemBarBackgrounds设置android:windowDrawsSystemBarBackgroundsandroid:statusBarColor ,否则您的Drawer将不会显示在“StatusBar

 <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar"> <!-- Other attributes like colorPrimary, colorAccent etc. --> <item name="android:windowDrawsSystemBarBackgrounds">true</item> <item name="android:statusBarColor">@android:color/transparent</item> </style> 

可选步骤

将一个标题添加到NavigationView。 为此,只需创build一个新的布局,然后在NavigationView中添加app:headerLayout="@layout/my_header_layout"

结果

显示导航视图的图片

笔记

  • 突出显示的颜色使用通过colorPrimary属性定义的颜色
  • 列表项使用通过textColorPrimary属性定义的颜色
  • 图标使用通过textColorSecondary属性定义的颜色

您还可以通过Chris Banes检查示例应用程序 ,该应用程序突出显示NavigationView以及作为devise支持库的其他新视图(如FloatingActionButtonTextInputLayoutSnackbarTabLayout等)

让它工作,在值-v21风格或主题xml需要使用此属性:

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

这使得魔法!

以上所有的方法是正确的,可能正在工作。 我已经按照上述指南创build了一个工作演示,并在2.x至5.x上进行了testing

你可以从Github克隆

重要的是玩“主要活动”

 toolbar = (Toolbar) findViewById(R.id.toolbar); res = this.getResources(); this.setSupportActionBar(toolbar); ActionBar actionBar = getSupportActionBar(); actionBar.setDisplayHomeAsUpEnabled(true); actionBar.setHomeButtonEnabled(true); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { ScrimInsetsFrameLayout scrimInsetsFrameLayout = (ScrimInsetsFrameLayout) findViewById(R.id.linearLayout); scrimInsetsFrameLayout.setOnInsetsCallback(this); } 

和回电

 @Override public void onInsetsChanged(Rect insets) { Toolbar toolbar = this.toolbar; ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) toolbar.getLayoutParams(); lp.topMargin = insets.top; int top = insets.top; insets.top += toolbar.getHeight(); toolbar.setLayoutParams(lp); insets.top = top; // revert } 

绝对的主题为V21做魔术

 <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar"> <!-- API 21 theme customizations can go here. --> <item name="colorPrimary">@color/colorPrimary</item> <item name="colorPrimaryDark">@color/colorPrimaryDark</item> <item name="colorAccent">@color/accent_material_light</item> <item name="windowActionModeOverlay">true</item> <item name="android:windowDrawsSystemBarBackgrounds">true</item> <item name="android:statusBarColor">@android:color/transparent</item> <item name="android:windowTranslucentStatus">true</item> </style> 

和ScrimInsetsFrameLayout

现在,新的devise支持库变得更容易了

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

来自@Chris Banes的克隆https://github.com/chrisbanes/cheesesquare

这里提到的所有答案都太旧,冗长。最新的Navigationview的最佳和最简短的解决scheme是

 @Override public void onDrawerSlide(View drawerView, float slideOffset) { super.onDrawerSlide(drawerView, slideOffset); try { //int currentapiVersion = android.os.Build.VERSION.SDK_INT; if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP){ // Do something for lollipop and above versions Window window = getWindow(); // clear FLAG_TRANSLUCENT_STATUS flag: window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); // add FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS flag to the window window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); // finally change the color to any color with transparency window.setStatusBarColor(getResources().getColor(R.color.colorPrimaryDarktrans));} } catch (Exception e) { Crashlytics.logException(e); } } 

这将打开抽屉时将状态栏颜色更改为透明

现在,当closures抽屉时,需要将状态栏的颜色再次改为黑暗。所以你可以这样做。

  public void onDrawerClosed(View drawerView) { super.onDrawerClosed(drawerView); try { if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP){ // Do something for lollipop and above versions Window window = getWindow(); // clear FLAG_TRANSLUCENT_STATUS flag: window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); // add FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS flag to the window window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); // finally change the color again to dark window.setStatusBarColor(getResources().getColor(R.color.colorPrimaryDark));} } catch (Exception e) { Crashlytics.logException(e); } } 

然后在主布局中添加一行即

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

你的导航视图看起来像

  <android.support.design.widget.NavigationView android:id="@+id/navigation_view" android:layout_height="match_parent" android:layout_width="wrap_content" android:layout_gravity="start" android:fitsSystemWindows="true" app:headerLayout="@layout/navigation_header" app:menu="@menu/drawer" /> 

我已经testing了它,并且完全正常工作。希望它可以帮助别人。这可能不是最好的方法,但是它工作顺利并且易于实现。 标记它,如果它帮助。快乐的编码:)

我正在使用devise支持库。 而只是通过使用自定义的主题,我打开导航抽屉时,实现了透明的状态栏。

在这里输入图像描述

在这里输入图像描述

 <style name="NavigationStyle" parent="Theme.AppCompat.Light.NoActionBar"> <!-- Customize your theme here. --> <item name="colorPrimary">@color/primaryColor</item> <item name="colorPrimaryDark">@color/primaryColorDark</item> <!-- To Make Navigation Drawer Fill Status Bar and become Transparent Too --> <item name="android:windowDrawsSystemBarBackgrounds">true</item> <item name="android:statusBarColor">@android:color/transparent</item> </style> 

最后在Manifest文件中添加主题

 <activity ........ ........ android:theme="@style/NavigationStyle"> </activity> 

不要忘记在“DrawerLayout”中使用属性android:fitsSystemWindows="true"

这是最简单的,它为我工作:

在值-21:

 <resources> <style name="AppTheme" parent="AppTheme.Base"> ... <item name="android:windowTranslucentStatus">true</item> </style> <dimen name="topMargin">25dp</dimen> </resources> 

在值中:

 <resources> <dimen name="topMargin">0dp</dimen> </resources> 

并设置到您的工具栏

 android:layout_marginTop="@dimen/topMargin" 

而不是使用ScrimInsetsFrameLayout …是不是更容易添加一个固定的高度24dp和一个24dp的背景视图?

我明白,这涉及到在层次结构中添加虚拟视图,但对我来说似乎更清洁。

我已经尝试过了,它运作良好。

 <android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_base_drawer_layout" android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <!-- THIS IS THE VIEW I'M TALKING ABOUT... --> <View android:layout_width="match_parent" android:layout_height="24dp" android:background="?attr/colorPrimary" /> <android.support.v7.widget.Toolbar android:id="@+id/activity_base_toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" android:elevation="2dp" android:theme="@style/ThemeOverlay.AppCompat.Dark" /> <FrameLayout android:id="@+id/activity_base_content_frame_layout" android:layout_width="match_parent" android:layout_height="match_parent" /> </LinearLayout> <fragment android:id="@+id/activity_base_drawer_fragment" android:name="com.myapp.drawer.ui.DrawerFragment" android:layout_width="240dp" android:layout_height="match_parent" android:layout_gravity="start" android:elevation="4dp" tools:layout="@layout/fragment_drawer" /> </android.support.v4.widget.DrawerLayout> 

试试这个:

 <android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/drawer_layout" android:fitsSystemWindows="true"> <FrameLayout android:layout_width="match_parent" android:layout_height="match_parent"> <!--Main layout and ads--> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <FrameLayout android:id="@+id/ll_main_hero" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1"> </FrameLayout> <FrameLayout android:id="@+id/ll_ads" android:layout_width="match_parent" android:layout_height="wrap_content"> <View android:layout_width="320dp" android:layout_height="50dp" android:layout_gravity="center" android:background="#ff00ff" /> </FrameLayout> </LinearLayout> <!--Toolbar--> <android.support.v7.widget.Toolbar android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/toolbar" android:elevation="4dp" /> </FrameLayout> <!--left--> <ListView android:layout_width="240dp" android:layout_height="match_parent" android:layout_gravity="start" android:choiceMode="singleChoice" android:divider="@null" android:background="@mipmap/layer_image" android:id="@+id/left_drawer"></ListView> <!--right--> <FrameLayout android:layout_width="240dp" android:layout_height="match_parent" android:layout_gravity="right" android:background="@mipmap/layer_image"> <ImageView android:layout_width="match_parent" android:layout_height="match_parent" android:src="@mipmap/ken2" android:scaleType="centerCrop" /> </FrameLayout> 

风格:

 <style name="ts_theme_overlay" parent="Theme.AppCompat.Light.NoActionBar"> <item name="colorPrimary">@color/red_A700</item> <item name="colorPrimaryDark">@color/red1</item> <item name="android:windowBackground">@color/blue_A400</item> </style> 

主要活动扩展了ActionBarActivity

 toolBar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolBar); 

现在你可以像使用ToolBar的普通ActionBar一样来创build选项菜单。

这是我的布局

  • 顶部:左抽屉 – 右抽屉
    • MID:ToolBar(ActionBar)
    • 底部:ListFragment

希望你明白!玩得开心!

Interesting Posts