Material Design 之 侧边栏与 Status Bar 不得不说的故事
2018-01-09 11:00
204 查看
再过2个小时就是2015苹果春季发布会了,在两个小时的闲暇时间里,顺便鼓捣一下 Material Design 的侧边栏,没想到又是心酸血泪史。
在 Material Design 中,侧边栏是一个可以从屏幕左侧滑出的抽屉式的组件。如下效果图所示,当侧边栏滑出时,它处于 Status Bar 之下,并且覆盖了 Status Bar 原有的颜色(图中 Dark Primary Color 所指组件)。按照 Material Design 中 Status Bar 的设计,Status Bar 的颜色会深于 ActionBar (图中 Primary Color 所指组件),效果图由于压缩的原因,可能不太明显。
效果图
所以,要实现如下效果,有如下3件事需要完成:
为了能使 ActionBar 获得最大的扩展,我们使用 ToolBar 代替 ActionBar,并将其颜色设置为 Material Design 中的 Primary Color
将 Status Bar 的颜色设置为 Material Design 中的 Dark Primary Color
将侧边栏插入于 Status Bar 和 页面内容之间
前两步中,正常的实现方式如下:
values-v21/styles.xml
运行程序,效果正是我们想要的(请不要在意配色怎么这么丑的细节, = =)。
效果图
下面,按照我们正常的方式添加侧边栏组件,为了能使侧边栏正常的显示在 Status Bar 之下,在
并将 Activity 继承自
duang! 问题出现了,侧边栏正常显示,但是之前设置的 Status Bar 的颜色失效了。
Status Bar 的颜色失效
但是,将刚才添加的状态栏透明的属性删除
duang!duang! 问题又出现了,侧边栏根本没有在 SystemBar 之下。
侧边栏没有在 Status Bar 之下
擦擦擦,这两个属性冲突了
为了解决这个问题,我拆解了谷歌官方应用,终于发现了这个秘密(可能是我才疏学浅,后来才发现官方的 Android Demo 里就有, 哈哈)
所以现在是解决问题的时间了。
首先,需要在应用中添加 ScrimInsetsFrameLayout 开源组件。
** ScrimInsetsFrameLayout.java **
然后创建该组件的自定义属性,使组件的
values/attrs.xml
更新你的 Activity 的布局文件,需要注意的是,确保 DrawerLayout 和 ScrimInsetsFrameLayout 都添加了
layout/activity_main.xml
最后,更新主题文件,使侧边栏显示与 Status Bar 之下
values-v21/styles.xml
最最后,就是见证奇迹的时刻(边看直播边码文字,刚好在发布会结束时完成,Macbook 很赞,Apple Watch 除外观之外,还不错的说)
最终效果图
转自:https://www.jianshu.com/p/ab937c80ed6e
在 Material Design 中,侧边栏是一个可以从屏幕左侧滑出的抽屉式的组件。如下效果图所示,当侧边栏滑出时,它处于 Status Bar 之下,并且覆盖了 Status Bar 原有的颜色(图中 Dark Primary Color 所指组件)。按照 Material Design 中 Status Bar 的设计,Status Bar 的颜色会深于 ActionBar (图中 Primary Color 所指组件),效果图由于压缩的原因,可能不太明显。
效果图
所以,要实现如下效果,有如下3件事需要完成:
为了能使 ActionBar 获得最大的扩展,我们使用 ToolBar 代替 ActionBar,并将其颜色设置为 Material Design 中的 Primary Color
将 Status Bar 的颜色设置为 Material Design 中的 Dark Primary Color
将侧边栏插入于 Status Bar 和 页面内容之间
前两步中,正常的实现方式如下:
values-v21/styles.xml
<style name="AppTheme" parent="Theme.AppCompat.Light"> <item name="android:windowDrawsSystemBarBackgrounds">true</item> <item name="windowActionBar">false</item> <item name="android:colorPrimary">@color/theme_primary</item> <item name="android:colorPrimaryDark">@color/theme_dark_primary</item> </style>
运行程序,效果正是我们想要的(请不要在意配色怎么这么丑的细节, = =)。
效果图
下面,按照我们正常的方式添加侧边栏组件,为了能使侧边栏正常的显示在 Status Bar 之下,在
values-v21/styles.xml中添加入
<item name="android:windowTranslucentStatus">true</item>
并将 Activity 继承自
ActionBarActivity, 并使用如下布局:
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/drawer_layout" android:layout_width="match_parent" android:layout_height="match_parent"> <RelativeLayout android:id="@+id/content_relative" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true"> <include android:id="@+id/toolbar" layout="@layout/framelayout_toolbar_with_search"/> <FrameLayout android:id="@+id/content_frame" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_below="@id/toolbar" android:background="@android:color/white"> </FrameLayout> </RelativeLayout> <!-- navigation 中根目录已经添加 android:fitsSystemWindows="false" 属性 --> <include layout="@layout/navigation"/> </android.support.v4.widget.DrawerLayout>
duang! 问题出现了,侧边栏正常显示,但是之前设置的 Status Bar 的颜色失效了。
Status Bar 的颜色失效
但是,将刚才添加的状态栏透明的属性删除
<item name="android:windowTranslucentStatus">true</item>
duang!duang! 问题又出现了,侧边栏根本没有在 SystemBar 之下。
侧边栏没有在 Status Bar 之下
擦擦擦,这两个属性冲突了
<item name="android:windowTranslucentStatus">true</item><item name="android:windowDrawsSystemBarBackgrounds">true</item>
为了解决这个问题,我拆解了谷歌官方应用,终于发现了这个秘密(可能是我才疏学浅,后来才发现官方的 Android Demo 里就有, 哈哈)
所以现在是解决问题的时间了。
首先,需要在应用中添加 ScrimInsetsFrameLayout 开源组件。
** ScrimInsetsFrameLayout.java **
/* * 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. */ package com.google.samples.apps.iosched.ui.widget; 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.google.samples.apps.iosched.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); } }
然后创建该组件的自定义属性,使组件的
insetForeground属性生效
values/attrs.xml
<declare-styleable name="ScrimInsetsView"> <attr name="insetForeground" format="reference|color" /> </declare-styleable>
更新你的 Activity 的布局文件,需要注意的是,确保 DrawerLayout 和 ScrimInsetsFrameLayout 都添加了
android:fitsSystemWindows属性,并设置为
true
layout/activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/drawer_layout" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true"> <RelativeLayout android:id="@+id/content_relative" android:layout_width="match_parent" android:layout_height="match_parent"> <include android:id="@+id/toolbar" layout="@layout/framelayout_toolbar_with_search"/> <FrameLayout android:id="@+id/content_frame" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_below="@id/toolbar" android:background="@android:color/white"> </FrameLayout> </RelativeLayout> <com.google.samples.apps.iosched.ui.widget.ScrimInsetsFrameLayout xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/scrimInsetsFrameLayout" android:layout_width="@dimen/navigation_width" android:layout_height="match_parent" android:layout_gravity="start" android:elevation="10dp" android:fitsSystemWindows="true" app:insetForeground="#4000"> <include layout="@layout/navigation"/> </com.google.samples.apps.iosched.ui.widget.ScrimInsetsFrameLayout> </android.support.v4.widget.DrawerLayout>
最后,更新主题文件,使侧边栏显示与 Status Bar 之下
values-v21/styles.xml
<style name="AppTheme" parent="Theme.AppCompat.Light"> <item name="android:windowDrawsSystemBarBackgrounds">true</item> <item name="windowActionBar">false</item> <item name="android:colorPrimary">@color/theme_primary</item> <item name="android:colorPrimaryDark">@color/theme_dark_primary</item> <item name="android:statusBarColor">@android:color/transparent</item> </style>
最最后,就是见证奇迹的时刻(边看直播边码文字,刚好在发布会结束时完成,Macbook 很赞,Apple Watch 除外观之外,还不错的说)
最终效果图
转自:https://www.jianshu.com/p/ab937c80ed6e
相关文章推荐
- Material Design 之 侧边栏与 SystemBar 不得不说的故事
- 那些年,与Material design不得不说的故事之TextInputLayout
- int,float,double之间不得不说的故事
- (九)模板方法模式详解(包含与类加载器不得不说的故事)
- dp和px,那些不得不吐槽的故事——Android平台图
- 本·拉登、奥巴马和布什不得不说的故事
- 模板方法模式详解(包含与类加载器不得不说的故事)
- 淘宝、百度、腾讯、京东 那不得不说的四角恋故事
- ValueVector和Vector不得不说的故事
- 黑客帝国之PHP与ASP.net不得不说的故事
- 我与免疫台不得不说的故事(二):风湿性疾病_1
- 微软与开放——不得不说的故事(3)(转载)
- 与android不得不说的故事之KJFrameForAndroid
- Hadoop的一些认识--------我与Hadoop不得不说的故事
- 【Hadoop 10周年】我与Hadoop不得不说的故事
- 关于猪头之不得不说的故事……
- dp和px,那些不得不吐槽的故事——Android平台图
- [转]用户故事【任务分解】和软件开发不得不说的故事
- 《我与Android不得不说的故事-1-立贴明志》
- 微软高层的宿命----一个不得不说的故事