×

12.3滑动菜单 (DrawerLayout)

96
YMJAndroid
2017.08.22 11:52* 字数 9951

         滑动菜单可以说是Material Desgin中最常见的效果之一了,在许多著名的应用中,都有滑动菜单的功能,虽然这个功能看上去好像挺复杂的,不过借助谷歌提供的各种工具,我们可以很轻松地实现炫酷的滑动效果,那么我们马上开始了。

不过如果我们全靠自己趋势线上述功能的话,难度恐怕就很大了。幸运的是谷歌提供了一个DrawerLayout控件,借助这个控件,实现滑动菜单简单又方便。

先来介绍一下DrawerLayout的用法吧。首先它是一个布局,在布局中允许放入两个直接子控件,第一个控件是主屏幕中显示的内容,第二个子控件是滑动菜单中显示的内容。因此,我们就可以对activity_main.xml中的代码做如下修改: 


可以看到,这里最外层的控件使用了DrawerLayout,这个控件是由support-V4库提供的。DrawerLayout中放置了两个直接子控件,第一个字控件是FrameLayout,用于作为主屏幕中显示的内容,当然里面还有我们刚刚定义的Toolbar。第二个子控件这里使用了一个TextView,用于作为滑动菜单中显示的内容,其实使用什么都可以,DrawerLayout并没有限制只能使用固定的控件。

但是关于第二个控件有一点需要注意,layout_gravity这个属性是必须指定的,因为我们需要告诉DrawerLayout滑动菜单是在屏幕的左边还是右边,指定left表示滑动菜单在左边,指定right表示滑动菜单在右边,比如英语、汉语,滑动菜单就在左边,如果系统语言是从右往左的,比如阿拉伯,滑动菜单就在右边。

没错,只需要改动这么多就可以了,现在重新运行一下程序,然后在屏幕的左侧边缘向右拖动,就可以让滑动菜单显示出来了,如图12.5所示:



然后向左滑动菜单,或者点击一下菜单以外的区域,都可以让滑动菜单关闭掉,从而回到主界面。无论是展示还是隐藏滑动菜单,都是非常流畅的动画度过的。

可以看到,我们只是稍微的改动了一下布局文件,就能实现如此炫酷的效果,是不是觉得挺激动的啦?而很多用户根本就不知道有这个功能,那么该怎么提示他们啦?

Material Desgin建议的做法实在Toolbar的最左边加入一个导航按钮,点击了按钮也会将滑动菜单的内容展示出来。这样就相当于给用户提供了两种打开滑动菜单的的方式,防止一些用户不知道屏幕的左侧边缘是可以拖动的。

下面我们开始来实现这个功能。首先我准备了一张导航按钮图标ic_menu.png,将它放在了drawable-xxhdpi目录下。然后修改MainActivity中的代码,如下所示:



这里我们并没有改动多少代码findViewById()方法得到了DrawerLayout的实例,然后调用getSupportActionBar()方法得到了ActionBar的实例,虽然这个ActionBar的具体实现是由ToolBar来完成的。接着调用ActionBar的setDisplayHomeAsUpEnabled()方法让导航按钮显示出来,有调用了setHomeAsUpIndicator()方法来设置一个导航按钮图标。实际上Toolbar最左侧的这个按钮就叫做HomeAsUp按钮,他默认的图标是一个返回的箭头,含义是返回上一个活动。很明显这里我们将它默认的样式和作用进行了修改。

接下来早onOptionsItemSelected()方法中对HomeAsUp按钮的点击事件进行处理,HomeAsUp按钮的id永远都是android.id.home。然后调用DrawerLayout的openDrawer()方法将滑动菜单展示出来,注意openDrawer()方法要求传入Gravity参数,为了保证这里的行为和XML中定义的一致,我们传入了GravityCompat.start。现在重新运行一下程序,效果如图12.6所示。


可以看到,在Toolbar的最左边出现了一个导航按钮,用户看到这个按钮就知道这坑定是可以点击的。现在点击一下这个按钮,滑动菜单界面就会再次展示出来了。


12.3.2 NavigationView

目前我们已经成功实现了滑动菜单功能,其中滑动功能已经做得非常好了,但是菜单却还很丑,毕竟菜单页面仅仅使用了一个TextView,非常单调。有对比才会有落差,我们看一下Google+的滑动菜单页面长什么样,如图12.7所示:


经过对比之后是不是觉得我们的菜单页面更丑了?不过没关系,优化滑动菜单页面,这里就是我们本小节的全部目标。

事实上,你可以在滑动菜单页面定制任意布局,不过谷歌给我们提供了一种更好的方法---------使用NavigationView。NavigationView是Desgin Support库中提供的一个控件,他不仅是严格按照Material Desgin的要求来设计的,而且还可以将滑动菜单页面的视线变得非常简单。接下来我们就学习一下NavigationView的用法。



           这里添加了两行依赖关系,第一行是Desgin Support库,第二行是一个开源项目的CircleImageView,它可以用来轻松实现图片圆形画的功能,我们待会就会用到它。CircleImageView的项目主页地址是:https://github.com/hdodenhof/CircleImageView。

在开始使用NavigationView之前,我们还需要提前准备好两个东西:menu和headerLayout。menu是用来在NavigationView显示具体菜单项的,headerLayout则是用来在NavigationView中显示头布局的。

我们先来准备menu,这里我事先找了几张图片作为按钮图标,并将它们放在了drawable-xxhdpi目录下。日前后在右击menu文件----->NEW---->Menu resource file,创建一个nav_menu.xml文件,并编写如下代码:



           首先我们在<menu>中潜逃了一个<group>标签,然后将group的chaeckableBehavior属性指定为single。group表示一个组,checkableBehavior指定为single表示卒中的所有菜单项只能单选。

          那么下面我们来看一下这些菜单项目。这里一共定义了5个item,分别使用android:id属性指定菜单项的id,android:icon属性指定菜单项的图标,android:title属性指定菜单项显示文字。就是这么简单,现在我们已经把menu主备好了。

接下来应该准备headerLayout了,这是一个可以随意定制的布局,不过我并不想将它做得太复杂。这里简单起见,我们就在headerLayout中放置头像、用户名、邮箱地址这3向内容吧。

说到头像,那么我们还需要在准备一张图片,这里找了一张宠物图片,并把他放在了drawable-xxhdpi目录下。另外这张图片最好是一张正方形图片,因为待会我们会把它圆形话。然后右击layout文件夹---->NEW----->layout resource file ,创建一个nav.header.xml文件。修改其中的代码,如下所示:


           可以看到,布局文件最外层是一个相对布局,我们将他的宽度设为match_parent,高度设为180dp,这是一个NavigationView比较适合的高度,然后指定它的背景色为corlorPrimary。

          在RelativeLayout中我们放置了3个控件,CircleImageView是一个用于将图片圆形化的控件,它的用法非常简单,基本和ImageView是完全一样的,这里给他指定了一张图片作为头像,然后设置为居中显示。另外两个TextView分别用于显示用户名和邮箱地址,他们都用到了一些Relativelayout的定位属性,相信肯定难不倒你啦?

           现在menu和headerLayout都准备好了,我们终于可以使用NavigationView了。修改activity_main.xml中的代码,如下所示:可以看到,我们将之前的TextView换成了                                      NavigationView,这样滑动菜单中显示的内容也就变成NavigationView了。这里又通过app:menu和app:headerLayout设置了进去,这样NavigationView就定义完成了。

NavigationView虽然定义完成了,但是我们还要去处理菜单项的点击事件才行。修改MainActivity中的代码,如下所示:



           代码还是比较简单的,这里首先获取到了NavigationView的实例,然后再调用它的setCheckedItem()方法将Call菜单设置为默认选中。接着调用了setNavigationItemSelectedListener()方法来设置一个菜单项选中事件的监听器,当用户点击了任意菜单项时,就会调用onNavigationItemSelected()方法中。我们可以在这个方法中写相应的逻辑处理,不过这里我们并没有附加任何逻辑,只是调用了DrawerLayout的closeDrawers()方法将滑动菜单关闭,这也是合情合理的做法。

现在可以重新运行一下程序,点击Toolabar左侧的导航按钮,效果如图12.8所示。



怎么样?这样的滑动菜单页面,你无论如何也不能说它丑了把?Material Design的魅力就在这里,他真的是一种非常美观的设计理念,只要你按照他的各种规范来设计界面,最终出来的程序就是特别好看的。

相信你对现在做出来的效果也一定十分满意吧?不过不要满足于现状,后面我们会实现更加炫酷的效果。紧跟脚步,继续学习。

12.4 悬浮按钮和可交互提示

           立面设计师Material Design中一条非常重要的设计思想,也就是说,按照Material Desgin的理念,应用程序的界面仅仅只是一个平面,而应该是有立体效果。在官方给出的示例中,最简单且最具代表性的立面设计就是悬浮按钮了,这种按钮不属于界面平面的一部分,而是位于另外一个维度的,因此就会给人一种悬浮的感觉。

           本节中我们会对这个悬浮按钮的效果进行学习,另外还会学习一种可交互式的提示工具。关于提示工具,我们之前一直使用Toast,但是Toast只能用于告知用户某某事情已经发生了,用户却不能对此做出任何的响应,那么今天我们就将这一方面进行扩展。

12.4.14 FolatingActionButton

FolatingActionButtonDesgin Support库中提供的一个控件,这个空间可以帮助我们比较轻轻松松地实现悬浮按钮的效果。其实在之前的图12.2中,我们就已经预览过悬浮按钮长什么样子的了。它默认会使用colorAccent来作为按钮的颜色,我们呢还可以通过各按钮指定一个图标来表明这个按钮的作用是什么。

下面开始来具体实现。首先仍然需要提前准备好一个图标,这里我防止了一张ic_done.pngdrawable-xxhdpi目录下。 然后修改activity_mian.xml中的代码,如下所示:


可以看到,这里我们在主屏幕布局中加入了FolatingActionButton。这个控件的用法并没有什么特别的地方,layout_with和layout_height属性都指定城wrap_content,layout_gravity属性指定将这个控件放置于屏幕的右下角,其中end的工作原理和之前的start是一样的,即如果系统语言是从左往右的,那么end就表示在右边,如果系统语言是从右边往左的,那么end就表示在左边。然后通过layout_margin属性给控件的四周留点边距。经贴着屏幕肯定是不好看的,最后通过SRC属性给FolatingActionButton设置了一个图标。

没错,就是这么简单,现在我们就可以运行一下了,效果如图12.9所示:



一个漂亮的悬浮按钮就在屏幕的右下方出现了。

如果你仔细观察的话,会发现悬浮按钮的下面还有一地那阴影,其实这很好理解,因为FolatingActionButton是悬浮在当前界面上的,既然是悬浮,那么理所应当会有投影,Design  Support库连这种细节都帮我们考虑到了。

说道悬浮,其实我们还可以指定FolatingActionButton的悬浮高度,如下所示:



这里使用了app:elevation属性来给FolatingActionButton指定一个高度,高度值越大,投影范围也越大,但是投影效果越淡,高度值越小,投影范围也越小,但是投影效果越浓,当然这些效果的叉一都不怎么明显,我个人感觉使用默认的FolatingActionButton效果就已经足够了。

接下来我么看一下FolatingActionButton是如何处理点击事件的,毕竟,一个按钮首先要能点击才有意义。修改MainActivity中的代码,如下所示:



            如果你期待FolatingActionButton会有什么特殊用法的话,那可能就要让你失望了,它和普通的Button其实没什么两样,都是调用srtOnClickListener()方法来注册监听器,当点击按钮时,就会执行监听器中的onClick()方法,这里我们在onClick()方法中弹出了一个Toast。

           现在重新运行一下程序,并点击FolatingActionButton,下过如图12.10所示。



12.4.2          Snackbar

          现在我们已经掌握了FolatingActionButton的基本用法,不过在一小节处理点击事件的时候,仍然是使用Toast来作为提示工具,本小节中我们就来学习一下Design Support库提供的更加先进的提示工具---------Snackbar。

          首先要明确,Snackbar并不是Toast的替代品,它们两者之间有着不同的应用场景。Toast的作用是告诉用户现在发生了什么事情,但同时用户只能被动接受这个事情,因为没有什么办法能让用户进行选择。而Snackbar则在这方面进行了扩展,他允许在提示当中加入一个可交互按钮,当用户点击按钮的时候可以执行一些额外的逻辑。打个比方,比如,我们在执行删除操作的时候只弹出一个Toast提示,那么用户要是误删了某个重要数据的话肯定会十分抓狂吧。但是如果我们增加一个Undo按钮,就相当于给用户提供了一种弥补措施,从而大大降低了事故发生的概率,提升用户体验。

          Snackbar的用法也非常简单,它和Toast是基本相似的,只不过可以额外增加一个按钮的点击事件。修改MainActivity中的代码,如下所示:

          可以看到,这里调用了Snackbar的make方法来创建一个Snackbar对象,make()方法的

         第一个参数要传入一个View,只要是当前界面布局的任意一个View,都可以,Snackbar会使用这个View来自动查找最外层的布局,用于展示Snackbar,第二个参数是Snackbar中显示的内容,第三个参数是Snackbar显示的时长。这些和Toast都是类似的。

         接着这里又调用了一个setAction()方法来设置一个动作,从而让Snackbar不仅仅是一个提示,而是可以和用户进行交互的。简单起见,我们在动作按钮的点击事件里面弹出了一个Toast提示。最后调用show()方法让Snackbar显示出来。

         现在重新运行一下程序,并点击悬浮按钮,效果如图12.11所示。


        可以看到,Snackbar从屏幕底部出现了,上面有我们所设置的提示文字,还有一个Undo按钮,按钮是可以点击的。过段时间后Snackbar会自动从底部消失。

        不管是出现还是消失,Snackbar都是带有动画小幅哦的,因此视觉体验也会比较好。

        不过你有没有发现一个bug,这个Snackbar竟然将我们的悬浮按钮给遮挡住了。虽说也不是什么重大的问题,因为Snackbar过一会就会自动消失,但这种用户体验总归是不有好的。有没有什么办法能解决一下啦?当然有,只需要借助CoordinatorLayout(协调员布局)就可以轻松解决。

12.4.3     CoordinatorLayout(协调员布局)

            CoordinatorLayout(协调员布局)可以说是一个加强版的FrameLayout,这个布局也是由Design Support库提供的。他的普通情况下的作用和FrameLayout基本一致,不过既然是Design Support库提供的布局,那么就必然有一些Material Design的魔力了。

            事实上,CoordinatorLayout可以监听其所有子空间的各种事件,然后自动帮助我们做出最为合理的响应。举个简单的例子,刚才弹出的Snackbar提示将悬浮按钮遮挡住了,而如果我们能让CoordinatorLayout(协调员布局)监听到Snackbar的弹出事件,那么他会自动将内部的

           FolatingActionButton向上偏移,从而确保不会被Snackbar遮挡住。

          至于CoordinatorLayout的使用也非常简单,我们只需要将原来的的FrameLayout替换一下就可以了。修改activity_main.xml中的代码,如下所示:

           由于CoordinatorLayout本省就是一个加强版的FrameLayout,因此这种替换不会又仍和的副作用。现在重新运行一下程序,并点击悬浮按钮,效果如图12.12所示:

           可以看到,悬浮按钮自动向上偏移了Snackbar的同等高度,从而确保不会被遮挡住,当Snackbar消失的时候,悬浮按钮会自动向下偏移回原来的位置。

           另外悬浮按钮的向上和向下偏移也是伴随着动画效果的,且和Snackbar完全同步,整体效果看上去特别赏心悦目。

           不过我们回过头来再思考一下,刚才说的是CoordinatorLayout可一件挺其他所有子空间的各种事件,但是Snackbar好像并不是CoordinatorLayout的子空间吧!为什么他却可以被监听到啦?

           其实道理很简单,还记得我们在Snackbar的make()方法中传入的第一个参数吗?整个参数就是用来指定Snackbar是基于哪个View来触发的,刚才我们传入的是FloatingActionButton本身,而FloatingActionButton是CoordinatorLayout中的子空间,因此这个事件就理所应当能被监听到了。你可以自己再做个实验,如果给Snackbar的make()方法传入一个DrawerLayout,那么Snackbar就会再次遮挡主悬浮按钮,因为DrawerLayout不是CoordinatorLayout的子控件,CoordinatorLayout也就无法监听到Snackbar的演出和隐藏事件了。

           本节的内容就到这里,接下来我们继续丰富MaterialTest项目,加入卡片式布局效果。


12.5 卡片式布局

虽然现在MaterialTest已经用了非常多的Material Design效果,不过你会发现没界面上最主要的一块区域还是处于空白状态。这一块区域通常都是用来放置应用程序的主体内容,握住杯使用一些精美的说过图片填充这部分区域。

那么为了要让水果图片也能Material化,本节中我们将会学习如何实现卡片式布局的效果。卡片式布局也是Material Design中提出的一个型概念,也可以让页面中的元素看起来就像在看片中一样,并且还能拥有圆角和投影,下面我们就开始具体的学习一下。

12.5.1   CardView

CardView是用于实现卡片布局效果的重要空间,有appcompat-V7库提供。实际上CardView也是一个FrameLayout,只是额外提供了圆角和阴影等效果,看上去会有立体的感觉。

我们先来看一下CardView的基本用法吧,其实非常简单,如下所示:



这里定义了一个CardView布局,我们可以通过app:cardCornerRadius属性指定卡片圆角的弧度,数值越大,圆角的弧度也越大。另外还可以通过app:elevation属性指定卡片的高度,高度值越大,投影范围也越大,但是投影效果越淡,高度值越小,投影范围也越小,但是投影效果越浓,这一点和FloatingActionButton是一致的。

然后我们在CardView布局中防止了一个TextView,那么这个TextView就会显示在一张卡片当中了,CardView的用法就这么简单。

但是我们显然不可能在如此宽阔的一块空白区域内只放置一张卡片,为了能够从分利用屏幕的控件,这里我准备综合运用下第三章中学到的知识,使用RecycleView来填充MaterialTest项目的主界面部分。还记得之前实现过的水果列表效果吗?这次我们将升级一下,实现一个高配版的水果列表效果。

既然是要实现水果列表,那么首先肯定需要准备许多张水果图片,这里必须在app/build.gradle文件中声明这些库的以来才行:(需要用到的控件就将包添加进去这里主要是recycleview和CradView)

注意上诉声明的最后一行,这里添加了一个Glide库的依赖。Glide是一个超级强大的图片加载库,它不仅可以用于加载本地图片,还可以加载网络图片、GIF图片、甚至是本地视频。最重要的是,Glide得用法非常简单,只需要一行代码就能轻松实现复杂的图片加载功能,因此这里我们准备用它来加载水果图片。

接下来开始具体的代码实现,修改activity_main.xml中的代码,如下所示:

这里我们在CoordinatorLayout中添加了一个Recycleview,给它制定一个id,然后将宽度和高度都设置为match_parent,这样Recycleview也就占满了布局空间。

接着定义一个实体类Fruit,代码如下所示:

Fruit类中只有两个字段,name表示水果的名字,imageId表示水果对应图片的资源id。

然后需要为Recycleview的指向指定一个我们自定义的布局,在Layout目录下新建fruilt_item.xml,代码如下:

            这里使用了CradView来作为子项最外层布局,从而使得RecycleView中的每个元素都是在卡片当中。CradView由于是一个FrameLayout,因此他没有什么方便的地位方式,这里我们只好在CradView外层嵌套了一个LinearLayout,然后在LinearLayout中放置具体的内容。

           内容倒也没什么特殊的地方,就是定义一个ImageView用于显示水果的图片,又定义了一个TextView用于显示水果的名称,并让TextView在水平方向上居中显示。注意哦ImageView使用了scaleType属性,这个属性可以指定图片的缩放模式。由于个张水果图片的长宽比例可能都不一致,为了让所有的图片都能充满整个ImageView,这里使用了centerCrop模式,它可以让图片保持原有的比例填充满ImageView,并将超出屏幕的部分裁减掉。

          接下来需要为RecycleView准备一下适配器,新建FruitAdapter类,让这个适配器继承自RecycleView.Adapter,并将泛型指定为FruitAdapter.ViewHolder,代码如下所示:


          上诉代码代码相信你一定很熟悉,和我们在第三章中编写的FruitAdapter几乎一摸一样。唯一需要注意的是onBindViewHolder()方法中使用了Glide来加载水果图片。

          那么这里就顺便来看心意啊Gilder.with()方法并传入一个Context、Activity或是Fragment参数,然后调用load()方法去加载图片,可以是一个URL地址,也可以是一个本地路径,或者是一个资源Id,最后调用into()方法将图片设置到具体某一个ImageView中就可以了。

                 那么我们为什么要使用Glide而不是传统的设置图片方式啦?因为这次我从网上找到的这些水果图片像素都非常高,如果不进行压缩就直接展示的话,很容易引起内存溢出。而使用Glide就完全不需要担心着回事,因为Glide在内部做了许多非常复杂的逻辑操作,其中就包括了图片压缩,我们只需要安心按照Glide的标准用法去加载图片就可以了。

                 这样我们就将RecycleView的适配器准备好了,最后修改MainActivity中的代码,如下所示:

             数据:

控件绑定----->(6什么6)基本操作

         在MianActivity中我们首先定义了一个数组,数组里面存放了很多个Fruit的实例,每个实例都代表这一种水果。然后在initView()方法中,显示清空了一下fruitList中的数据,接着使用一个随机函数,从刚才定义的Fruit数组中随机挑选一个水果方法到fruitList当中,这样每次打开程序看到的水果数据都会是不同的。另外,为了让界面上的数据多一些,这里使用了一个循环,随机挑选50个水果。

         之后的用法就是RecycleView的标准用法了,不过这里使用了GridLayoutManager这种布局方式。在第三章中我们已经学过了LinearLayoutManager的用法也没什么特别之处,他的构造函数接收两个参数,第一个Context,第二个参数是列数,这里我们希望每一行中有两列数据。

         现在重新运行一下程序,效果如图12.13所示:








         可以看到,精美的水果图片功能展示出来了。每个水果都是在一张单独的卡片当中,并且还拥有圆角和投影,是不是非常美观?另外,由于我们是使用随机的方式获取水果数据的,因此界面上回有一些重复的水果出现。这属于正常现象。

        当你陶醉于当前精美的界面的时候,你是不是忽略了一个细节?哎呀,我们呢的Toolbar怎么不见了!仔细观察一下原来是被RecycleView给挡住了。这个问题又该怎么解决啦?这就需要借助到另外一个工具了--------AppBarLayout。

12.5.2         AppBarLayout

              首先我们先来分析一下为什么RecycleView会把ToolBar给遮挡住吧。其实并不难理解,由于RecycleView和Toolbar都是放置在CoordinatorLayout中的,而前面已经说过,CoordinatorLayout就是一个加强版的FrameLayout,那么FrameLayout中的所有控件在不进行明确定位的情况下,默认都会摆放在布局的左上角,从而也就产生了遮挡的现象。其实这已经不是你第一次遇到这种情况了,我们在3.3.3小节学习FrameLayout的时候就早早见识过了控件与控件之间遮挡的效果。

              既然已经知道了问题的原因,那么该如何解决啦?传统情况下,使用偏移是唯一的解决办法,既让RecycleView向下偏移一个Toolbar的高度,从而保证不会遮挡到Toolbar。不过我们使用的并不是普通的FrameLayout,而是CoordinatorLayout,因此自然会有一些更加巧妙的解决办法。

              这里我准备使用Design Support库中提供了另外一个工具---------AppBarLayout。AppBarLayout实际上是一个垂直方向的LinearLayout,他在内部做了很多滚动时间的封装,并应用了一些Material Design的设计理念。

             那么我们怎样使用AppBarLayout才能解决前面覆盖的问题啦?其实只需要两步就可以了,第一步将Toolbar嵌套到AppBarLayout,第二步给RecycleView制定一个布局行为。修改activity_main.xml中的代码,如下所示:

          可以看到,布局文件并没有什么太大的变化。我们首先定义了一个AppBarLayout,并将Toolbar放置在了AppBarLayout里面,然后在RecycleView中使用app:layout_behavior属性指定了一个布局行为。其中appbar_scrolling_view_behavior这个字符串也是由Design Support库提供的。

          现在重新运行一下程序,你就会发现一切正常了,如图12.14所示。


          虽说使用AppBarLayout已经成功解决了RecycleView遮挡ToolBar的问题,但是刚才有提到过,说AppBarLayout中应用了一些Material Design的设计理念,好像从上面的例子完全体现不出来呀。事实上,当RecycleView滚动的时候就已经滚动事件都通知给了AppBarLayout了,只是我们还没处理而已。那么下面就让我们来进一步优化,看看AppBarLayout到底能实现什么样的Material Design效果。

AppBarLayout接收到滚动事件的时候,它内部的子控件其实是可以指定如何去影响这些事件的,通过app:layout_scrollFlags属性就能实现。修改activity_main.xml中的代码,如下所示:

            这里在Toolbar中添加了一个app:layout_scrollFlags属性,并将这个属性的值指定了scroll|enterAlways|snap。其中scroll表示当RecycleView向上滚动的时候,Toolbar会跟着一起向上滚动并实现隐藏;enterAlways表示当RecycleView向下滚动的时候,Toolbar会跟着向下滚动并显示。snap表示当Toolbar还没有完全隐藏或显示的时候,会根据当前滚动的距离,自动选择是隐藏还是显示。

            我们要改动的就只有一行代码而已,现在重新运行一下程序,并向上滚动RecycleView,效果如图12.15所示


            可以看到,随着我们向上滚动RecycleView,Toolbar竟然消失了,而向下滚动RecycleView,Toolbar又会重新出现。这其实也是Material Design中的一项重要思想,因为当用户在向上滚动RecycleView的时候,其注意力肯定是在RecycleView的内容上面的内容上面的,这个时候如果Toolbar还占据这屏幕控件,就会在一定程度上影响用户的阅读体验,而将Toolbar隐藏则可以让阅读体验达到最佳状态。当用户保证了用户的最佳阅读效果,又不影响任何功能上的操作,Material Design考虑的就是这么细致入微。

             当然了,像这种功能,如果是使用ActionBar的话,那就完全不可能实现了,Toolabar的出爱心为我们提供了更多的可能。

12.6    下拉刷新

          下拉刷新这种功能早就不是什么新鲜的东西了,几乎所有的应用里都有这个功能。不过市面上现有的下拉刷新功能在风格上都各有不相同,并且和Material Desgin还有些格格不入的感觉。因此,谷歌为了让Android的下拉刷新风格能有一个统一的标准,于是在Matreial Design中制定了一个官方的设计规范。当然,我们并不需要去深入了解这个规范到底是什么样的,因为谷歌早就提供好了县城空间,我们只需要在项目中直接使用就可以了。
  SwipeRefreshLayout就是用于实现下拉刷新功能的核心类,它是由support-v4库提供的。我们把想要实现下拉刷新功能的控件放置到SwipeRefreshLayout中,就可以迅速的让这个控件支持下拉刷新,那么在MaterialTest项目中,应该支持下拉刷新功能的控件自然就是RecycleView了。

           由于SwipeRefreshLayout的用法也比较简单,下面我们就直接开始使用了,修改activity_main.xml中的代码,如下所示:

            可以看到,这里我们在RecycleView的外面又嵌套了一层SwipeRefreshLayout,这样RecycleView就自动拥有下拉刷新功能了。另外需要注意,由于RecycleView现在变成了SwipeRefreshLayout的子控件,因此之前使用app:layout_behavior声明的布局行为现在也要移动到SwipeRefreshLayout才行。

           不过这还没有结束,虽然RecycleView已经支持了下拉刷新功能了,但是我们还要在代码重处理具体的刷新逻辑才行。修改MainActivity中的代码,如下所示:

            这段代码应该还是比较好理解的,首先通过findViewById()方法拿到SwipeRefreshLayout的实例,然后调用setColorSchemeResources()方法来设置下拉刷新进度条的颜色,这里我们就使用主题中的colorPrimary作为进度条的颜色了。接着调用setOnRefreshLishtener()方法来设置一个刷新的监听器,当触发了下拉刷新操作就会回调这个间天气的onRefresh()方法,然后我们在这里去处理具体的数显逻辑就可以了。

            通常情况下,onResfresh()方法中应该是去网络上请求最新的数据,然后再将这些数据展示出来。这里简单起见,我们就不和网络进行交互了,而在调用一个refreshFruilts()方法进行本地刷新操作。refreshFruilts()方法中线是掀起了一个线程,然后将线程沉睡两秒钟,之所以这样做,是因为本地刷新的过程速度非常快 ,如果不将线程沉睡的话,刷新立刻就会结束了,从而看不到刷新的过程。沉睡结束之后,这里使用了runOnUiThread()方法将线程切换回主线程,然后调用 ininView()方法重新生成重新生成数据,接着再调用fruiltAdapter的notifyDataSetChanged()方法通知数据发生了变化,最后调用SwipeRefreshLayout的setRefreshing()方法并传入flase,用于表示刷新事件结束,用于表示刷新事件结束,并隐藏刷新进度条。

           现在可以重新运行一下程序了,在屏幕的主界面向下拖动,会有一个下拉刷新的进度条出现。松手后就会自动进行刷新了,效果如图:12.16所示:


           下拉刷新的进度条只会停留两秒,之后就会自动消失了并且这个就是Material Design中滚定的最标准的效果,Design Support苦衷的常用控件也学了大半了。不过本章的学习之旅还没有结束,在最后的尾声部分,我明年再来实现一个非常震撼的Material Design效果-------可折叠式的标题栏。

12.7       可折叠式的标题栏

            虽说我们现在的标题栏时使用Toolbar来编写的,不过他看上去和传统的ActionBar其实没有什么两样,只不过可以响应RecycleView的滚动事件来进行隐藏和显示。而Material Design中并没有限定标准栏必须长什么样子的,事实上,我们可以根据自己的喜爱随意定制标题栏的样式。那么本节中我们就来实现一个可折叠式标题栏的效果,需要借助到CollapsingToolbarLayout这个工具。

12.7.1  CollapsingToolbarLayout

              顾名思义CollapsingToolbarLayout是一个作用于Toolabar基础上的布局,他也是有Design Support库提供的。CollapsingToolbarLayout可以让Toolbar的效果变得更加丰富,不仅仅是展示一个标题栏,而是能够实现非常华丽的效果。

不过,CollapsingToolbarLayout是不能独立存在的,它在设计的时候就被限定只能作为AppBarLayout的直接子布局来使用。而AppBarLayout又必须是使用CoordinatorLayout的子布局,因此本节我们要实现的功能其实需要综合运行前面所学的知识。那么话不多说,这就开始吧!

          首先需要一个格外的活动作为水果详情页的展示界面,右击com.exmple.materialtest包------------>New------->Activity-------->Empty Activity,创建一个FruiltActivity,并将布局名指定成activity_fruit.xml,然后我们开始编写水果详情页展示界面的布局。

         由于整个布局文件比较复杂,这里我准备采用分段编写的方式。activity_fruit.xml中的内容主要分为两部分,一个是水果标题栏,一个是水果内容详情,我们来一步步实现。

首先实现标题栏部分,这里使用CoordinatorLayout来作为最外层布局,如下所示:

Web note ad 1