第一行代码(十二)
第十二章主要讲了 Material Design 的一些用法
一、Maternal Design 介绍
Android 平台的界面风格长期难以统一,为了解决这个问题,Google 推出了全新的界面设计语言--Material Design.
从 Android5.0开始,就将所有内置的应用都使用 Material Design 风格来进行设计。谷歌在2015年 Google I/O 大会上推出了 Design Support 库。
二、Toolbar
每个活动最顶部的那个标题栏其实就是 ActionBar,不过 ActionBar 由于其设计原因,被限定只能位于活动顶部,从而不能实现一些 Material Design 的效果,因此官方已经不再建议使用 ActionBar 了。
ToolBar 的强大之处在于,它不仅继承了 ActionBar 的所有功能,而且灵活性很高,可以配合其他控件一起来完成一些 Material Design 的效果。
注意:任何一个新建的项目,默认都会显示 ActionBar 的,这个 ActionBar 是根据项目中指定的主题来显示的。打开清单文件,找到 <application>标签中有一个 android:theme 属性,并且指定了一个 AppTheme 主题。
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
</resources>
这个 DarkActionBar 就是一个深色的 ActionBar 主题,项目中自带的 ActionBar 就是因为指定了这个主题才出现的。但是我们现在要使用 ToolBar,所以要指定一个不带 ActionBar 的主题,通常有 Theme.AppCompat.NoActionBar 和 Theme.AppCompat.Light.NoActionBar 这两种主题
,其中 Theme.AppCompat.NoActionBar 表示深色主题,他会将界面的主体颜色设置成深色,陪衬颜色设成淡色。而 Theme.AppCompat.Light.NoActionBar 表示淡色主题,它会将界面的主体颜色设置成淡色,陪衬颜色设成深色。
观察一下 AppTheme 中的属性重写,这里重写了 colorPrimary、colorPrimaryDark 和 colorAccent 这3个属性的颜色。
其他:
- 1.colorPrimary:应用的主要色调,actionBar默认使用该颜色,Toolbar导航栏的底色
- 2.colorPrimaryDark:应用的主要暗色调,statusBarColor默认使用该颜色
- 3.statusBarColor:状态栏颜色,默认使用colorPrimaryDark
- 4.windowBackground:窗口背景颜色
- 5.navigationBarColor:底部栏颜色
- 6.colorForeground:应用的前景色,ListView的分割线,switch滑动区默认使用该颜色
- 7.colorBackground:应用的背景色,popMenu的背景默认使用该颜色
- 8.colorAccent:CheckBox,RadioButton,SwitchCompat等一般控件的选中效果默认采用该颜色
- 9.colorControlNormal:CheckBox,RadioButton,SwitchCompat等默认状态的颜色。
- 10.colorControlHighlight:控件按压时的色调
- 11.colorControlActivated:控件选中时的颜色,默认使用colorAccent
- 12.colorButtonNormal:默认按钮的背景颜色
- 13.editTextColor:默认EditView输入框字体的颜色。
- 14.textColor:Button,textView的文字颜色
- 15.textColorPrimaryDisableOnly:RadioButton checkbox等控件的文字
- 16.textColorPrimary:应用的主要文字颜色,actionBar的标题文字默认使用该颜色
- 17.colorSwitchThumbNormal:switch thumbs 默认状态的颜色
使用了 NoActionBar 主题后,我们已经将 ActionBar 隐藏起来了,接下来就要使用 ToolBar 代替 ActionBar 了。
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light">
</android.support.v7.widget.Toolbar>
</FrameLayout>
这里首先记得添加 xmlns:app 指定命名空间,因为 Material Design 是在 Android5.0系统中才出现的,而很多 Material 属性在5.0之前的系统中并不存在,为了兼容之前的老系统,我们不能使用 android:attribute 这样的写法了,就要使用 app:attribute 的写法。
我们使用了 ToolBar 控件,该控件是 appcompat-v7库提供的,高度指定为 ActionBar 的高度,背景色设置为 colorPrimary。接下来,我们设置了主题,因为我们在 style.xm里面将程序的主题设置成了淡色主题,因此 ToolBar 也是淡色主题,ToolBar 上的元素就会自动使用深色主题,看起来很难看,为了让 ToolBar 单独使用深色主题,我们就使用了 theme 为 ToolBar 单独指定了一个主题:
ThemeOverlay.AppCompat.Dark.ActionBar
,但是这样又会有一个问题,如果 ToolBar 中有菜单按钮,弹出的菜单项会变成深色主题,又变得很难看,于是使用了app:popupTheme
属性单独将弹出的菜单指定成了淡色主题。
Toolbar toolBar = (Toolbar) findViewById(R.id.toolbar);
/*
这句话的作用是:既使用了 ToolBar,又让它的外观和功能都和 ActionBar 一致
*/
setSupportActionBar(toolBar);
<activity android:name=".MaterialDesignActivity"
android:label="Material Design">
<!--这里我们为 Activity 指定了一个 label,作用就是让在 ToolBar 中显示标题-->
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
接下来我们给 ToolBar 添加一个 action 按钮
image.png
image.png
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/backup"
android:icon="@mipmap/ic_launcher"
android:title="Backup"
app:showAsAction="always" />
<item
android:id="@+id/delete"
android:icon="@mipmap/ic_launcher"
android:title="delete"
app:showAsAction="ifRoom" />
<item
android:id="@+id/settings"
android:icon="@mipmap/ic_launcher"
android:title="settings"
app:showAsAction="never" />
</menu>
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<!--使用 showAsAction 来指定按钮的显示位置 ,
这里之所以使用 app 命名空间,是为了能够
兼容低版本-->
<!-- always 表示永远显示在 ToolBar 上,如果屏幕空间不够则不显示 -->
<item
android:id="@+id/backup"
android:icon="@mipmap/ic_launcher"
android:title="Backup"
app:showAsAction="always" />
<!--ifRoom 表示如果屏幕空间足够就显示在 ToolBar 上,
不够的话就显示在菜单当中-->
<item
android:id="@+id/delete"
android:icon="@mipmap/ic_launcher"
android:title="delete"
app:showAsAction="ifRoom" />
<!-- never 表示永远显示在菜单当中-->
<item
android:id="@+id/settings"
android:icon="@mipmap/ic_launcher"
android:title="settings"
app:showAsAction="never" />
</menu>
注意:ToolBar 中的 action 按钮只会显示图标,菜单中的 action 按钮只会显示文字。
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.toolbar,menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()){
case R.id.backup:
Toast.makeText(this, "backUp", Toast.LENGTH_SHORT).show();
break;
case R.id.delete:
Toast.makeText(this, "delete", Toast.LENGTH_SHORT).show();
break;
case R.id.settings:
Toast.makeText(this, "settings", Toast.LENGTH_SHORT).show();
break;
default:
break;
}
return true;
}
ToolBar 使用总结:
- 首先要设置应用主题是 NoActionBar 的
- 然后在布局文件中要使用 app 命名空间
- 给 ToolBar 设置高度为
?attr/actionBarSize
ActionBar 的高度,背景色设置为?attr/colorPirmary
- 给 ToolBar 设置高度为
- 根据程序主体颜色,设置 ToolBar 的 theme 主体颜色(与程序主体颜色相反),
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
,然后设置弹出的菜单主体颜色(与程序主体颜色相同)app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
- 根据程序主体颜色,设置 ToolBar 的 theme 主体颜色(与程序主体颜色相反),
- ToolBar 最左侧有一个按钮,叫做 HomeAsUp,默认是一个向左的箭头,该按钮的 id 永远都是 android.R.id.home
二、DrawerLayout(滑动菜单)
所谓滑动菜单,就是将一些菜单隐藏起来,不放在主屏幕上,然后可以通过滑动的方式将菜单显示出来。我们可以借助 DrawerLayout 来实现这种效果。
首先 DrawerLayout (support.v4库提供)是一个布局,在布局中允许放入两个直接子控件,第一个子控件是主屏幕中显示的内容,第二个控件是滑动菜单中显示的内容(侧边栏)。
<?xml version="1.0" encoding="utf-8"?>
<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:layout_width="match_parent"
android:layout_height="match_parent">
<!-- 主屏幕的内容 -->
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light">
</android.support.v7.widget.Toolbar>
</FrameLayout>
<!-- 侧边栏的内容 -->
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="start"
android:text="我是侧边栏中的 TextView"
android:textSize="30sp"
android:background="@android:color/white"/>
</android.support.v4.widget.DrawerLayout>
image.png
注意:关于第二个子控件,layout_gravity这个属性必须指定,因为我们需要告诉 DrawerLayout 滑动菜单是在屏幕的左边还是右边,这里指定了 start,表示根据系统语言进行判定,如果系统语言是从左往右的,比如英语、汉语,滑动菜单就是在左边,如果系统语言是从右往左的,比如阿拉伯语,滑动菜单就是在右边。
这时候会有点问题,可能用户不知道可以滑动,不知道有侧边栏这个东西,Material Design 建议的做法是在 Toolbar 的左边加入一个导航按钮,点击按钮就将侧边栏展示出来。
drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
/*
通过 getSupportActionBar()方法获取 ActionBar,虽然是 ActionBar,
但是其实具体实现是由 ToolBar 来实现的
*/
ActionBar actionBar = getSupportActionBar();
/*
这里注意,其实 ToolBar 最左侧的导航按钮就叫做 HomeAsUp 按钮
他默认的是一个返回箭头,我们修改了他的样式
*/
if(actionBar != null){
//让导航按钮显示出来
actionBar.setDisplayHomeAsUpEnabled(true);
//设置导航按钮图标
actionBar.setHomeAsUpIndicator(R.mipmap.ic_launcher);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()){
case R.id.backup:
Toast.makeText(this, "backUp", Toast.LENGTH_SHORT).show();
break;
case R.id.delete:
Toast.makeText(this, "delete", Toast.LENGTH_SHORT).show();
break;
case R.id.settings:
Toast.makeText(this, "settings", Toast.LENGTH_SHORT).show();
break;
case android.R.id.home:
/*
注意:这里,HomeAsUp 按钮的 id 永远都是 android.R.id.home
*/
drawerLayout.openDrawer(GravityCompat.START);
break;
default:
break;
}
return true;
}
DrawerLayout 使用总结:
- DrawerLayout 是一个布局,并且允许放入两个直接子控件,第一个控件是主屏幕中显示的内容,第二个控件是滑动菜单中显示的内容。
- 第二个控件必须制定 layout_gravity 属性,用于告诉 DrawerLayout 是从哪个方向滑动出来的。
三、NavigationView
NavigationView 是 Design Support 库中提供的一个控件,所以我们需要引入这个库,而且在使用 NavigationView 之前,我们需要提前准备好两个东西:menu 和 headerLayout,menu 是用来在 NavigationView 中显示具体的菜单项的,headerLayout 则是用来在 NavigationView 中显示头布局的。
//design 库
compile 'com.android.support:design:24.2.1'
//circleimageview
compile 'de.hdodenhof:circleimageview:2.1.0'
CircleImageView 可以用来轻松实现图片圆形化的功能,项目主页地址是:https://github.com/hdodenhof/CircleImageView
在开始使用 NavigationView 之前,我们还需要提前准备好两个东西:menu 和 headerLayout,其中 menu 是用来在 NavigationView 中显示具体的菜单项的, headerLayout 则是用来在显示头部布局的。
接下来在 menu 文件夹中创建一个 xml 文件,叫做nav_menu.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<!-- group表示一个组,checkableBehavior属性指定为single表示组中所有菜单项只能单选 -->
<group android:checkableBehavior="single">
<item android:id="@+id/nav_call"
android:icon="@mipmap/ic_launcher"
android:title="Call" />
<item android:id="@+id/nav_friends"
android:icon="@mipmap/ic_launcher"
android:title="Friends" />
<item android:id="@+id/nav_location"
android:icon="@mipmap/ic_launcher"
android:title="Location" />
<item android:id="@+id/nav_mail"
android:icon="@mipmap/ic_launcher"
android:title="Mail" />
<item android:id="@+id/nav_task"
android:icon="@mipmap/ic_launcher"
android:title="Task" />
</group>
</menu>
然后我们在 layout 文件夹中创建一个 xml 文件,命名为:nav_header.xml
。
<?xml version="1.0" encoding="utf-8"?>
<!-- 这里宽度设为match_parent,高度设为180dp,这是一个 NavigationView 比较合适的高度 -->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="180dp"
android:padding="10dp"
android:background="?attr/colorPrimary"
android:orientation="vertical">
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/icon_img"
android:layout_width="70dp"
android:layout_height="70dp"
android:layout_centerInParent="true"
android:src="@mipmap/ic_launcher" />
<TextView
android:id="@+id/mail"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:text="www.baidu.com@game.com"
android:textColor="#ffffff"
android:textSize="14sp" />
<TextView
android:id="@+id/username"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@id/mail"
android:text="Tony"
android:textColor="#ffffff"
android:textSize="14sp"/>
</RelativeLayout>
然后修改 Activity 的 layout 布局文件
<?xml version="1.0" encoding="utf-8"?>
<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:layout_width="match_parent"
android:layout_height="match_parent">
<!-- 主布局 -->
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light">
</android.support.v7.widget.Toolbar>
</FrameLayout>
<!-- 侧边栏 -->
<android.support.design.widget.NavigationView
android:id="@+id/nav_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="start"
app:headerLayout="@layout/nav_header"
app:menu="@menu/nav_menu">
<!-- 这里通过 app:header 和 app:menu 属性将我们刚才准备好的
menu 和 headerLayout 设置拉进去 -->
</android.support.design.widget.NavigationView>
</android.support.v4.widget.DrawerLayout>
navView = (NavigationView) findViewById(R.id.nav_view);
//设置默认选中的菜单项
navView.setCheckedItem(R.id.nav_call);
//菜单项选中事件监听
navView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
drawerLayout.closeDrawers();
return true;
}
});
NavigationView.png
NavigationView使用总结:
- 1.使用 NavigationView 需要引入design 库。
- 2.还需要准备两个 xml 文件:展示 menu 的 xml 文件,和展示头部 header 的 xml 文件。
- 3.头部的 header 的 xml 布局文件中,最外层根布局高度设置为180dp,这是一个比较适合 NavigationView 的高度。
- 4.然后通过
app:menu 和 app:headerLayout
属性来指定文件。
四、FloatingActionButton (悬浮按钮)
这是 Design Support 库中提供的一个控件,默认使用 colorAccent 来作为按钮的颜色。
<?xml version="1.0" encoding="utf-8"?>
<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:layout_width="match_parent"
android:layout_height="match_parent">
<!-- 主布局 -->
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light">
</android.support.v7.widget.Toolbar>
<!-- 悬浮按钮 -->
<!-- 这里 end 和 start 一样,如果系统语言是从左往右的,那么 end
就在右边,如果系统语言是从右往左的,那么 end 就在左边。
可以使用 app:elevation 属性来给按钮指定一个高度值,高度值
越大,投影范围也越大,但是投影效果越淡,高度值越小,投影范围
也越小,投影效果越浓 -->
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="16dp"
android:src="@mipmap/ic_launcher"
app:elevation="8dp"/>
</FrameLayout>
<!-- 侧边栏 -->
<!-- ...... -->
</android.support.v4.widget.DrawerLayout>
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(ThirdActivity.this, "点击了悬浮按钮", Toast.LENGTH_SHORT).show();
}
});
五、Snackbar
还是由 Design Support 库提供的。但是需要明确一点,Snackbar 并不是 Toast 的替代品,他们两者之间有着不同的应用场景。Toast 的作用是告诉用户现在发生了什么事情,用户只能被动接受。而 Snackbar 则在这方面进行了扩展,它允许在提示当中加入一个可交互按钮,当用户点击按钮的时候可以执行一些额外的操作逻辑。
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// Toast.makeText(ThirdActivity.this, "点击了悬浮按钮", Toast.LENGTH_SHORT).show();
//使用 Snackbar
/*
Snackbar 使用 make 方法来创建
参1:传入一个 View 对象,只要是当前界面任意一个 View 都可以,Snackbar 会使用
这个 View 来自动查找最外层的布局,用于展示Snackbar
参2:Snackbar 中显示的内容
参3:Snackbar 显示的时长
*/
Snackbar.make(v,"是你点击了悬浮按钮?",Snackbar.LENGTH_SHORT)
//通过 setAction 方法设置一个动作
.setAction("Yes", new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(ThirdActivity.this, "是我点击了", Toast.LENGTH_SHORT).show();
}
//调用 show() 方法让 Snackbar 显示出来
}).show();
}
});
Snackbar.png
不管是点击 YES 还是过一段时间等待 Snackbar 自动消失,Snackbar都是自带动画效果的,但是你会发现一个问题,就是 Snackbar 将我们的 FloatingActionBar 给挡住了,这就需要借助 CoordinatorLayout 就行了。
六、CoordinatorLayout
CoordinatorLayout 可以说是一个加强版的 FrameLayout,该布局也是由 Design Support 库提供的,在普通情况下和 FrameLayout 基本一致。
事实上,CoordinatorLayout 可以监听其所有子控件的各种事件,然后自动帮我们做出最为合理的响应,举个例子:刚才弹出的 Snackbar 将悬浮按钮挡住了,如果我们让 CoordinatorLayout 监听到 Snackbar 的弹出事件,那么它会自动将内部的 FloatingActionButton 向上偏移,从而确保不会被 Snackbar 挡住。
<?xml version="1.0" encoding="utf-8"?>
<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:layout_width="match_parent"
android:layout_height="match_parent">
<!-- 主布局 -->
<android.support.design.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light">
</android.support.v7.widget.Toolbar>
<!-- 悬浮按钮 -->
<!-- 这里 end 和 start 一样,如果系统语言是从左往右的,那么 end
就在右边,如果系统语言是从右往左的,那么 end 就在左边。
可以使用 app:elevation 属性来给按钮指定一个高度值,高度值
越大,投影范围也越大,但是投影效果越淡,高度值越小,投影范围
也越小,投影效果越浓 -->
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="16dp"
android:src="@mipmap/ic_launcher"
app:elevation="8dp"/>
</android.support.design.widget.CoordinatorLayout>
<!-- 侧边栏 -->
<android.support.design.widget.NavigationView
android:id="@+id/nav_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="start"
app:headerLayout="@layout/nav_header"
app:menu="@menu/nav_menu">
</android.support.design.widget.NavigationView>
</android.support.v4.widget.DrawerLayout>
CoordinatorLayout.png
问题解决了,但是有点奇怪,FloatingActionButton 是 CoordinatorLayout 的子控件没问题,但 Snackbar 并不是,为什么 CoordinatorLayout 还会监听到呢?
道理很简单,我们在 Snackbar 的 make() 方法中传入的第一个参数,就是用来指定 Snackbar 是基于哪个 View 来触发的,我们传入的是 FloatingActionBar,而 FloatingActionBar 又是 CoordinatorLayout 中的子控件,所以这个事件就能被监听到,如果给 Snackbar 的 make() 方法的第一个参数传入别的 View,比如传入 DrawerLayout,那么 Snackbar 就会再次遮挡悬浮按钮,因为 DrawerLayout 不是 CoordinatorLayout 的子控件,CoordinatorLayout 也就无法坚挺到 Snackbar 的弹出和隐藏事件了。
FloatingActionBar、Snackbar、CoordinatorLayout 使用总结
- 1.这三个控件都是基于 Design Support 库中的
- 2.FloatingActionBar 可以通过 android:src 属性设置图标,还可以通过 app:elevation 设置按钮悬浮高度,设置点击事件和其他 View 的方法一样
- 3.CoordinatorLayout 其实就是高级 FrameLayout,但是它可以监听其所有子控件的各种事件,然后自动做出最为合理的响应。
- 4.Snackbar 通过 make 方法创建,通过setAction 方法设置一个动作,可以和用户进行交互,通过 show 方法将 Snackbar 显示出来。
- 5.Snackbar 的 make 方法中,参1可以是当前布局的任意一个 View,如果有 FloatingActionBar 最好传入 FloatingActionBar,然后和 CoordiantorLayout 一起使用,避免 Snackbar 挡住 FloatingActionBar。
- 6.FloatingActionBar 可以通过 app:layout_anchor="@id/xxx" 属性设置锚点,指定悬浮按钮出现在某个控件区域内,然后使用 app:layout_anchorGravity 属性将悬浮按钮定位在区域内的某个位置。
七、CardView(卡片式布局)
CardView 由 appcompat-v7 提供,实际上 CardView 也是一个 FrameLayout,只是额外提供了圆角和阴影等效果,看上去会有立体的感觉。
我们使用一个简单的例子来展示一下 CardView 控件
<?xml version="1.0" encoding="utf-8"?>
<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:layout_width="match_parent"
android:layout_height="match_parent">
<!-- 主布局 -->
<android.support.design.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light">
</android.support.v7.widget.Toolbar>
<!-- 这里添加一个 RecyclerView -->
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
</android.support.v7.widget.RecyclerView>
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="16dp"
android:src="@mipmap/ic_launcher"
app:elevation="8dp"/>
</android.support.design.widget.CoordinatorLayout>
<!-- 侧边栏 -->
<android.support.design.widget.NavigationView
android:id="@+id/nav_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="start"
app:headerLayout="@layout/nav_header"
app:menu="@menu/nav_menu">
</android.support.design.widget.NavigationView>
</android.support.v4.widget.DrawerLayout>
/**
* 实体类
*/
public class Fruit {
private String name;//水果的名字
private int imageId;//水果对应资源的图片id
public Fruit(){
}
public Fruit(String name, int imageId) {
this.name = name;
this.imageId = imageId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getImageId() {
return imageId;
}
public void setImageId(int imageId) {
this.imageId = imageId;
}
@Override
public String toString() {
return "Fruit{" +
"name='" + name + '\'' +
", imageId=" + imageId +
'}';
}
}
<!-- 这个是RecyclerView的item布局 -->
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:orientation="vertical"
app:cardCornerRadius="4dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView
android:id="@+id/iv_fruit"
android:layout_width="match_parent"
android:layout_height="100dp"
android:scaleType="centerCrop" />
<TextView
android:id="@+id/tv_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_margin="5dp"
android:textSize="16sp" />
</LinearLayout>
</android.support.v7.widget.CardView>
/**
* RecyclerView 的适配器
*/
public class FruitAdapter extends RecyclerView.Adapter<FruitAdapter.ViewHolder>{
private List<Fruit> mFruitList;
private Context mContext;
public FruitAdapter(List<Fruit> list){
this.mFruitList = list;
}
static class ViewHolder extends RecyclerView.ViewHolder{
ImageView ivFruit;
TextView tvName;
public ViewHolder(View itemView) {
super(itemView);
ivFruit = itemView.findViewById(R.id.iv_fruit);
tvName = itemView.findViewById(R.id.tv_name);
}
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if(mContext == null){
mContext = parent.getContext();
}
View view = LayoutInflater.from(mContext).inflate(R.layout.fruit_item,parent,false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
Fruit fruit = mFruitList.get(position);
holder.tvName.setText(fruit.getName());
Glide.with(mContext).load(fruit.getImageId()).into(holder.ivFruit);
}
@Override
public int getItemCount() {
return mFruitList == null ? 0 : mFruitList.size();
}
}
这里使用了 Glide 加载图片,Glide 项目主页地址是:https://github.com/bumptech/glide
这里为什么要用 Glide 而不用传统的方式设置图片呢?因为如果图片像素非常高的话,如果不进行压缩就直接展示,很容易就会引起内存泄露,而 Glide 在内部做了许多复杂的逻辑操作,其中就包括了图片的压缩。
//数据
private Fruit[] fruits = {
new Fruit("Apple", R.mipmap.ic_launcher),
new Fruit("Banana", R.mipmap.ic_launcher),
new Fruit("Orange", R.mipmap.ic_launcher),
new Fruit("Watermelon", R.mipmap.ic_launcher),
new Fruit("Pear", R.mipmap.ic_launcher),
new Fruit("Grape", R.mipmap.ic_launcher),
new Fruit("Pineapple", R.mipmap.ic_launcher),
new Fruit("Strawbeery", R.mipmap.ic_launcher),
new Fruit("Cherry", R.mipmap.ic_launcher),
new Fruit("Mango", R.mipmap.ic_launcher)
};
private List<Fruit> fruitList = new ArrayList<>();
initFruits();
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
GridLayoutManager gridLayoutManager = new GridLayoutManager(this,2);
recyclerView.setLayoutManager(gridLayoutManager);
FruitAdapter adapter = new FruitAdapter(fruitList);
recyclerView.setAdapter(adapter);
private void initFruits() {
fruitList.clear();
for (int i = 0; i < 50; i++) {
Random random = new Random();
int index = random.nextInt(fruits.length);
fruitList.add(fruits[index]);
}
}
CardView.png
CardView 的效果已经出来了,但是你自己观察一下,ToolBar 被 RecyclerView 挡住了,怎么办?这就要借助另一个工具 -- AppBarLayout了。
八、AppBarLayout
分析下上述问题的原因,因为 ToolBar 和 RecyclerView 都放在 CoordinatorLayout 中,而 CoordinatorLayout 是一个加强版的 FrameLayout,那么肯定会遮挡了啊。那么如果我们在 CoordinatorLayout 内,给 ToolBar 和 RecyclerView 的包一层 LinearLayout 或者 RelativeLayout 是不是就可以了呢?是的,是可以了,但是,我们既然引出了 AppBarLayout,就有用 AppBarLayout 的理由。
AppBarLayout 实际上是一个垂直方向上的 LinearLayout,而且它也是 Design Support 库中的空间,它在其内部做了非常多的滚动事件的封装。先看看如何使用:
<!-- 主布局 -->
<android.support.design.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- 在 Toolbar 外层嵌套一个 AppBarLayout -->
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light">
</android.support.v7.widget.Toolbar>
</android.support.design.widget.AppBarLayout>
<!-- 给 ReccylerView 添加一个 app:layout_behavior 属性,值也是固定这么写的 -->
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
</android.support.v7.widget.RecyclerView>
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="16dp"
android:src="@mipmap/ic_launcher"
app:elevation="8dp" />
</android.support.design.widget.CoordinatorLayout>
AppBarLayout1.png
AppBarLayout2.png注意:这里如果将 RecyclerView 也放在 AppBarLayout 中,则会无法滑动。如果不给 RecyclerView 添加 app:layout_behavior 属性,则会出现 RecyclerView 的上面部分被 ToolBar 遮盖了,如下图所示。
注意顶部,这个是 RecyclerView 滑动到顶部的样子,也就是说 RecyclerView 的上面部分被 ToolBar 遮盖了。
当 AppBarLayout 接收到滚动事件的时候,它内部的子控件其实是可以指定如何去响应这些事件的,通过app:layout_scrollFlags
属性就可以
<!-- 主布局 -->
<android.support.design.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- 在 Toolbar 外层嵌套一个 AppBarLayout -->
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<!-- 给 Toolbar 添加了 app:layout_scrollFlags 属性
其中 scroll 表示当 RecyclerView 向上滚动的时候,Toolbar 会跟随一起向上滚动并实现隐藏;
enterAlways 表示当RecyclerView 向下滚动的时候,Toolbar 会跟着一起向下滚动并重新显示;
snap 表示当 Toolbar 还没有完全隐藏或显示的时候,会根据当前滚动的距离,自动选择是隐藏还是显示-->
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
app:layout_scrollFlags="scroll|enterAlways|snap">
</android.support.v7.widget.Toolbar>
</android.support.design.widget.AppBarLayout>
<!-- 给 RecyclerView 添加一个 app:layout_behavior 属性,值也是固定这么写的 -->
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</android.support.v7.widget.RecyclerView>
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="16dp"
android:src="@mipmap/ic_launcher"
app:elevation="8dp" />
</android.support.design.widget.CoordinatorLayout>
CardView、AppBarLayout使用总结:
- 1.CardView 是由 appcompat-v7 库提供,实质上是一个 FrameLayout。AppBarLayout是由 Design Support 库提供,实质上是一个垂直方向上的 LinearLayout.
- 2.CardView 中使用 app:cardCornerRadius 属性指定卡片圆角的弧度,数值越大,圆角的弧度也越大,还可以使用 app:elevation 属性指定卡片的高度,高度值越大,投影范围越大,但是投影效果越淡。高度值越小,投影范围也越小,但是投影效果越浓。
- 3.解决 RecyclerView 覆盖 Toolbar 问题,只需要两步:第一步 --- 将 Toolbar 嵌套在 AppBarLayout 中;第二步 --- 给RecyclerView 指定一个布局行为:
app:layout_behavior="@string/appbar_scrolling_view_behavior"
- 4.让 Toolbar 跟随 RecyclerView 一起滑动的效果:给 Toolbar 设置属性
app:layout_scrollFlags="scroll|enterAlways|snap"
其中,scroll 表示 RecyclerView 上滑时,Toolbar 会隐藏;enterAlways 表示 RecyclerView 下滑时,Toolbar 会显示;snap 表示 Toolbar 没有完全隐藏或显示的时候,会根据当前滑动的距离,自动选择隐藏还是显示。
九、SwipeRefreshLayout(下拉刷新)
SwipeRefreshLayout 是由 support-v4 库提供,一般下拉刷新都配合 RecyclerView 一起使用,我们只需要在 RecyclerView 外层嵌套一个 SwipeRefreshLayout 即可。
<!-- 主布局 -->
<android.support.design.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- 在 Toolbar 外层嵌套一个 AppBarLayout -->
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:layout_scrollFlags="scroll|enterAlways|snap"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light">
</android.support.v7.widget.Toolbar>
</android.support.design.widget.AppBarLayout>
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/swipe_refresh"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
</android.support.v7.widget.RecyclerView>
</android.support.v4.widget.SwipeRefreshLayout>
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="16dp"
android:src="@mipmap/ic_launcher"
app:elevation="8dp" />
</android.support.design.widget.CoordinatorLayout>
注意:这里因为 RecyclerView 外面嵌套了一层 SwipeRefreshLayout 所以,之前设置的 app:layout_behavior 属性也必须要放到 SwipeRefreshLayout 中才行。
swipeRefresh = (SwipeRefreshLayout) findViewById(R.id.swipe_refresh);
//设置下拉刷新进度条的颜色,参数是可变参数
swipeRefresh.setColorSchemeResources(R.color.colorPrimary,R.color.colorAccent);
//设置下拉刷新的监听器,当触发了下拉刷新操作时,就会回调这个监听器的 onRefresh 方法
swipeRefresh.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
refreshFruits();
}
});
private void refreshFruits(){
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
runOnUiThread(new Runnable() {
@Override
public void run() {
initFruits();
adapter.notifyDataSetChanged();
//传入 false,表示刷新事件结束,并隐藏刷新进度条
swipeRefresh.setRefreshing(false);
}
});
}
}).start();
}
swipeRefreshLayout.png
十、可折叠式标题栏
如果我们希望根据自己的喜好随意定制标题栏的样式,比如实现一个可折叠式的标题栏的效果,就需要借助 CollapsingToolbarLayout 这个工具。
CollapsingToolbarLayout 是一个作用于 Toolbar 基础之上的布局,它也是由 Design Support 库提供,CollapsingToolbarLayout 可以让 Toolbar 的效果变得更加丰富。
注意:CollapsingToolbarLayout 不能独立存在,它在设计的时候就被限定只能作为AppBarLayout 的直接子布局来使用,而AppBarLayout 又必须是 CoordinatorLayout 的子布局,所以嵌套的代码如下:
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout 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:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.AppBarLayout
android:id="@+id/appbar"
android:layout_width="match_parent"
android:layout_height="250dp">
<!-- 嵌套在 AppBarLayout 中的 CollapsingToolbarLayout
app:contentScrim 属性用于指定 CollapsingToolbarLayout 在趋于折叠状态
以及折叠之后的背景色,其实 CollapsingToolbarLayout 在折叠后就是一个普
通的Toolbar。
app:layout_scrollFlags 属性中 scroll 表示 CollapsingToolbarLayout 会
随着内容详情的滚动一起滚动,exitUntilCollapsed 表示当 CollapsingToolbarLayout
会随着完成折叠之后就保留在界面上,不再移出屏幕-->
<android.support.design.widget.CollapsingToolbarLayout
android:id="@+id/collapsing_toolbar"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:contentScrim="?attr/colorPrimary"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<!-- app:layout_collapseMode 属性指定当前控件在 CollapsingToolbarLayout
折叠过程中的折叠模式,pin 表示在折叠过程中位置始终保持不变,parallax 表示
会在折叠的过程中产生一定的错位偏移,视觉效果很好 -->
<ImageView
android:id="@+id/iv_fruit"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
app:layout_collapseMode="parallax"/>
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_collapseMode="pin">
</android.support.v7.widget.Toolbar>
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<!-- NestedScrollView 是在 ScrollView 的基础之上增加了嵌套
响应滚动事件的功能,由于 CoordinatorLayout 本身已经可以
响应滚动事件了,因此我们在内部就需要使用 NestedScrollView
或 RecyclerView ,而且还加了 app:layout_behavior 属性,
为了不要遮挡Toolbar -->
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="15dp"
android:layout_marginLeft="15dp"
android:layout_marginRight="15dp"
android:layout_marginTop="35dp"
app:cardCornerRadius="4dp">
<TextView
android:id="@+id/tv_fruit_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp" />
</android.support.v7.widget.CardView>
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
<!-- 使用 app:layout_anchor 属性指定一个锚点
使用 app:layout_anchorGravity 属性将悬浮按钮定位在标题栏区域的右下角 -->
<android.support.design.widget.FloatingActionButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
app:layout_anchor="@id/appbar"
app:layout_anchorGravity="bottom|end"/>
</android.support.design.widget.CoordinatorLayout>
该布局分三部分,最外层是一个 CoordinatorLayout
第一部分:在 CoordinatorLayout 内嵌一个 AppBarLayout,然后在 AppBarLayout 中再内嵌一个 CollapsingToolbarLayout,然后再 CollapsingToolbarLayout 中再加上 ImageView 和 Toolbar 两个控件。
第二部分:加入 NestedScrollView 控件,和 AppBarLayout 是同级的,在 NestedScrollView 内部嵌套一个 LinearLayout,然后在 LinearLayout 中加上 CardView 布局。
第三部分:比较简单,就是加一个FloatingActionButton,和 AppBarLayout 以及 NestedScrollView 都是同级的。
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.setDisplayHomeAsUpEnabled(true);
}
CollapsingToolbarLayout collapsingToolbar = (CollapsingToolbarLayout) findViewById(R.id.collapsing_toolbar);
collapsingToolbar.setTitle("Apple");
ImageView ivFruit = (ImageView) findViewById(R.id.iv_fruit);
TextView tvFruitContent = (TextView) findViewById(R.id.tv_fruit_content);
Glide.with(this).load(R.mipmap.ic_launcher).into(ivFruit);
tvFruitContent.setText(generateFruitContent("Apple"));
/**
* 生成比较长的内容
*/
private String generateFruitContent(String name) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 500; i++) {
sb.append(name);
}
return sb.toString();
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()){
case android.R.id.home:
finish();
return true;
default:
break;
}
return super.onOptionsItemSelected(item);
}
device-2018-04-17-150418.png
CollapsingToolbarLayout使用总结:
- 1.CollapsingToolbarLayout 是不能独立存在的,必须作为 AppBarLayout 的直接子布局来使用,AppBarLayout 又必须是 CoordinatorLayout 的子布局。
- 2.通过设置 app:contentScrim 属性用于指定 CollapsingToolbarLayout 在趋于折叠状态和折叠之后的背景色。还有 app:layout_scrollFlags 属性,scroll 表示 CollapsingToolbarLayout 会随之一起滚动,exitUntilCollapsed 表示当 CollapsingToolbarLayout 随着滚动完成折叠之后就保留在界面上,不再移出屏幕。
- 3.CollapsingToolbarLayout 的子控件,可以添加 app:layout_collapseMode 属性指定当前控件在 CollapsingToolbarLayout 折叠过程中的折叠模式,pin 表示在折叠的过程中位置始终保持不变,parallax 表示会在折叠的过程中产生一定的错位偏移。
十一、系统状态栏
在 Android 5.0 系统之前,我们是无法对状态栏的背景或颜色进行操作的。
想让背景图能够和系统状态栏融合,需要借助 android:fitsSystemWindows 这个属性,在控件中,将该属性指定为 true,表示该控件会出现在系统状态栏里,修改布局文件:
image.png
image.png这里如果只给 ImageView 设置 android:fitsSystemWindows 属性是没有用的,必须将 ImageView 布局结构中的所有父布局都设置上这个属性才可以。
有点变化,但不是我们要的效果。这时就需要将状态栏颜色指定成透明色才行。
设置成透明的方法很简单,在主题中将 android:statusBarColor 属性的值指定成 @android:color/transparent 就可以了,但是问题是,android:statusBarColor 这个属性是从 API 21(Android 5.0)开始才有的,之前的系统无法指定这个属性。
为了解决上述问题,我们做如下操作
image.png
然后在 values-v21 目录下创建一个styles.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="FruitActivityTheme" parent="AppTheme">
<item name="android:statusBarColor">@android:color/transparent</item>
</style>
</resources>
由于 values-v21 目录是只有 Android 5.0 及其以上的系统才会去读取的,所以,这么写是没问题的。但是 Android 5.0 之前的系统却无法识别 FruitActivityTheme 这个主题,因此我们还需要修改 values/styles.xml 文件。
<!-- 因为Android 5.0 之前的系统无法指定状态栏的颜色,所以这里什么都不用做 -->
<style name="FruitActivityTheme" parent="AppTheme">
</style>
最后别忘了在清单文件中给 Activity 添加 Theme 属性
image.png
最后,Material Design 的官方文章:https://material.google.com