将导航抽屉放置在状态栏下方。
将导航抽屉放置在状态栏下方。
我正在尝试让我的导航抽屉在状态栏下面显示。我已经广泛阅读了关于ScrimInsetsFrameLayout视图的资料,并尝试实现它,但出于某种原因它不会在状态栏下面显示。
以下是我使用/编写的代码。
XML DrawerLayout:
ScrimInsetsFrameLayout.java:
package com.andrewq.planets.util; 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; 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); } } public void setOnInsetsCallback(OnInsetsCallback onInsetsCallback) { mOnInsetsCallback = onInsetsCallback; } public interface OnInsetsCallback { void onInsetsChanged(Rect insets); } }
最后,这是我用于values-v21的styles.xml:
我查看了2014年I/O应用程序的源代码以及这个问题:这里,但我不知道有什么不同。
这是我目前的截图,去掉了状态栏下的抽屉:
除了状态栏下的抽屉,其他都运行得很完美。非常感谢您的帮助!
编辑:
澄清一下,我想要让图像在状态栏下面的地方变成半透明,就像大多数谷歌应用和Google Now一样。
问题的出现原因:
想要将导航抽屉放在状态栏下方,需要启用透明状态栏。可以通过样式或代码来实现。另外,还需要在布局中添加顶部的填充。
问题的解决方法:
1. 启用透明状态栏需要至少在API v19上操作,或者为v19+创建一个单独的样式文件。
2. 在样式文件中添加以下代码:
3. 在布局中为抽屉内容和正常视图内容添加24dp的顶部填充。
4. 可以使用ScrimInsetsLayout来实现更好的效果。详细的使用方法可以参考链接:https://stackoverflow.com/a/26932228
5. 如果需要在代码中处理,可以按照以下代码示例进行操作:
if (Build.VERSION.SDK_INT >= 19 && Build.VERSION.SDK_INT < 21) { // 启用透明状态栏 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) { // 无需透明标志,主题已处理 setTranslucentStatusFlag(false); // 将状态栏颜色设置为透明以去除黑色阴影 mActivity.getWindow().setStatusBarColor(Color.TRANSPARENT); } // 在抽屉内容上添加填充(api v19+设备上的25dp) mDrawerContentRoot.setPadding(0, mActivity.getResources().getDimensionPixelSize(R.dimen.tool_bar_top_padding), 0, 0); // 定义statusBar的颜色 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); } }
文章中的其他内容为问题的完整解决方案和相关讨论,不属于问题的原因和解决方法。
问题的原因是在状态栏下方放置导航抽屉时出现的布局问题。解决方法是向DrawerLayout添加android:fitsSystemWindows="false"
属性。添加android:fitsSystemWindows="false"
属性到NavigationView也可以解决该问题。这个方法对我来说很有效。感谢!这个方法很好用。谢谢!这个方法非常简单 🙂 但是为什么这样做会起作用呢?NavigationView的实现中,fitsSystemWindows的默认值是false,所以如果不声明它,它应该默认为false。但为什么必须显式地将其设置为false才能起作用呢?在API19设备上不需要这样做,但对于我来说,在我的27 + 28上起作用。