本文翻译自:How do I use DrawerLayout to display over the ActionBar/Toolbar and under the status bar?
I've seen in the new material design Side Nav spec that you can display the drawer over the action bar and behind the status bar. 我在新的材质设计Side Nav规范中看到,可以在操作栏上方和状态栏后面显示抽屉。 How can I implement this? 我该如何实施?
参考:https://stackoom.com/question/1mwTn/如何使用DrawerLayout在操作栏-工具栏上方和状态栏下方显示
New functionality in the framework and support libs allow exactly this. 框架和支持库中的新功能完全可以做到这一点。 There are three 'pieces of the puzzle': 一共有三个“谜题”:
fitsSystemWindows
so that it is layed out behind the system bars. 使DrawerLayout适合 fitsSystemWindows
以便将其fitsSystemWindows
在系统栏后面。 Theme.Material
's normal status bar coloring so that DrawerLayout can draw there instead. 禁用Theme.Material
的正常状态栏着色,以便DrawerLayout可以在此处绘制。 I'll assume that you will use the new appcompat. 我假设您将使用新的appcompat。
First, your layout should look like this: 首先,您的布局应如下所示:
Then in your 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);
}
Then you need to make sure that the DrawerLayout is visible behind the status bar. 然后,您需要确保DrawerLayout在状态栏后面可见。 You do that by changing your values-v21 theme: 您可以通过更改values-v21主题来做到这一点:
values-v21/themes.xml values-v21 / themes.xml
Note: If a
is used instead of 注意:如果使用
而不是
the actual layout, the desired effect will be achieved if you call fitsSystemWindows(boolean)
on a view that you return from onCreateView
method. 实际的布局,如果在从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;
}
Make it work, in values-v21 styles or theme xml needs to use this attribute: 使它工作,在values-v21样式或主题xml中需要使用此属性:
That make the magic! 那使魔术!
This is the most simple, and it worked for me: 这是最简单的,对我有用:
In the values-21: 在值21中:
25dp
In the values: 在值中:
0dp
And set to your toolbar 并设置到您的工具栏
android:layout_marginTop="@dimen/topMargin"
This can now be achieved using the new Android Design Support Library . 现在,可以使用新的Android设计支持库来实现。
You can see the Cheesesquare sample app by Chris Banes which demos all the new features. 您可以看到Chris Banes提供的Cheesesquare示例应用程序 ,其中演示了所有新功能。
Since there is no complete solution posted, here is the way I achieved the desired result. 由于没有发布完整的解决方案,因此这是我获得期望结果的方法。
First include a ScrimInsetsFrameLayout in your project. 首先在您的项目中包含一个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)}, 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);
}
}
Then create a styleable so that the insetForeground
can be set. 然后创建一个可样式化的样式,以便可以设置insetForeground
。
values/attrs.xml values / attrs.xml
Update your activity's xml file and make sure android:fitsSystemWindows
is set to true on both the DrawerLayout
as well as the ScrimInsetsFrameLayout
. 更新您的活动的XML文件,并确保android:fitsSystemWindows
设置为true同时在DrawerLayout
还有ScrimInsetsFrameLayout
。
layout/activity_main.xml layout / activity_main.xml
Inside the onCreate method of your activity set the status bar background color on the drawer layout. 在活动的onCreate方法内,在抽屉布局中设置状态栏背景色。
MainActivity.java 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));
}
Finally update your app's theme so that the DrawerLayout
is behind the status bar. 最后,更新您应用的主题,以使DrawerLayout
位于状态栏的后面。
values-v21/styles.xml values-v21 / styles.xml
Result: 结果:
The above all approaches are correct and may be working . 以上所有方法都是正确的,可能正在起作用。 I have created a working demo following the above guide and tested on 2.x to 5.x 我已经按照上述指南创建了一个工作演示,并在2.x至5.x上进行了测试
You can clone from Github 您可以从Github克隆
The important thing to play around is in Main Activity 重要的事情是在主要活动中
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);
}
and the call back 和回电
@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
}
Absolutely the Theme for V21 does the magic V21的主题绝对具有魔力
and the ScrimInsetsFrameLayout 和ScrimInsetsFrameLayout
Now this come more easy with new Design Support library 现在,有了新的设计支持库,这将变得更加容易
compile 'com.android.support:design:22.2.0'
clone from @Chris Banes https://github.com/chrisbanes/cheesesquare 从@Chris Banes克隆https://github.com/chrisbanes/cheesesquare