第一行代码(十二)

作者: radish520like | 来源:发表于2018-04-17 15:59 被阅读58次

    第十二章主要讲了 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个属性的颜色。

    image.png
    其他:
    • 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 使用总结:

      1. 首先要设置应用主题是 NoActionBar 的
      1. 然后在布局文件中要使用 app 命名空间
      1. 给 ToolBar 设置高度为?attr/actionBarSizeActionBar 的高度,背景色设置为?attr/colorPirmary
      1. 根据程序主体颜色,设置 ToolBar 的 theme 主体颜色(与程序主体颜色相反),android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar",然后设置弹出的菜单主体颜色(与程序主体颜色相同)app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
      1. 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 使用总结:

      1. DrawerLayout 是一个布局,并且允许放入两个直接子控件,第一个控件是主屏幕中显示的内容,第二个控件是滑动菜单中显示的内容。
      1. 第二个控件必须制定 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

    注意:这里如果将 RecyclerView 也放在 AppBarLayout 中,则会无法滑动。如果不给 RecyclerView 添加 app:layout_behavior 属性,则会出现 RecyclerView 的上面部分被 ToolBar 遮盖了,如下图所示。

    AppBarLayout2.png

      注意顶部,这个是 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

    这里如果只给 ImageView 设置 android:fitsSystemWindows 属性是没有用的,必须将 ImageView 布局结构中的所有父布局都设置上这个属性才可以。

    image.png

      有点变化,但不是我们要的效果。这时就需要将状态栏颜色指定成透明色才行。

    设置成透明的方法很简单,在主题中将 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

    下一篇文章:https://www.jianshu.com/p/501825ee6fff

    相关文章

      网友评论

      本文标题:第一行代码(十二)

      本文链接:https://www.haomeiwen.com/subject/hwawhftx.html