Material Design

MaterialDesign是一套全新的界面设计语言,包含视觉,运动,互动效果等特性.

Design Support库这个库将Material Design中最具有代表性的一些控件和效果进行了封装,使得开发者在即使不了解Material Design的情况下也能非常轻松地将自己的应用Material化.

Toolbar

Toolbar不仅继承了ActionBar的所有功能,而且灵活性很高,可以配合其他控件来完成一些MaterialDesign的效果.

将ActionBar取消并替换成Toolbar

ActionBar由于设计原因,被限定只能在活动的顶部,所以不能实现一些Material Design的效果,官方现在已经不建议使用ActionBar了.ActionBar其实是根据项目中指定的主题来显示的.打开AndroidManifest.xml .使用android:theme属性指定了一个AppTheme的主题

  <application

  android:allowBackup="true"

    android:icon="@mipmap/ic_launcher"

    android:label="Fruit"

    android:roundIcon="@mipmap/ic_launcher_round"

    android:supportsRtl="true"

  android:theme="@style/AppTheme">

AppTheme主题是在res/values/styles.xml文件中定义的(as中可以直接ctrl+鼠标左键来查看)

这里定义了一个叫AppTheme的主题然后指定它的parent主题是Theme.AppCompat.Light.DarkActionBar.这个DarkActionBar便是一个深色的ActionBar主题,我们之前所有项目中自带的ActionBar就是因为指定了这个主题才出现的.

如果我们要使用Toolbar来代替ActionBar,因此需要指定一个不带ActionBar的主题,通常有两种一种是Theme.AppCompat.NoActionBar另一种是Theme.AppCompat.Light.NoActionBar.

1
2

总结:将ActionBar设置成Toolbar的方法是将res/values/styles.xml文件中的parent设置一下,由之前的Theme.AppCompat.Light.DarkActionBar设置成Theme.AppCompat.NoActionBar/Theme.AppCompat.Light.NoActionBar.设置完成后会发生一个显著的变化即AppTheme中属性也随之发生了变化,比如colorPrimary和colorPrimaryDark和colorAccent;当然除了这三个属性以外我们还可以在<style>标签内设置一些其他的属性比如:textColorPrimary.windowBackground和navigationBarColor等属性来控制更多位置颜色.

colorAccent这个属性不只是用来指定这样一个按钮的颜色,而是更多表达一种强调的意思,比如一些控件选中状态也会使用colorAccent的颜色

使用Toolbar来代替ActionBar

xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:app="http://schemas.android.com/apk/res-auto"

打开布局文件,首先看一下的这二行代码首先使用xmln:android来指定一个命名空间,因此我们才能一直使用android:id,android:layout_width等写法.这里指定了xmln:app,也就是说可以使用app:attribute这样的写法了,指定xmlns:app的目的在于为了兼顾Android5.0之前的那些老系统,因为Material Design是在Android5.0系统才出现的,而很多Material属性在5.0之前的系统中并不存在,所以为了兼顾之前的老系统,我们不能使用android:attribute的写法了而是应该采用app:attribute.

使用Toolbar控件使用<android.support.v7.widget.Toolbar/>

3

使用"app:属性名"兼顾低版本系统的条件:属性是Android5.0系统之后新增的,之前版本没有这个属性便可以用"app:属性名"兼顾低版本系统了

注意:在代码中使用Toolbar和之前的button有不同之处,都先要找到控件 用findViewbyid Toolbar的不同之处在于还要调用setsupportActionBar()方法将Toolbar实例传入,只有这样我们就做到了及使用了Toolbar又让它的外观和功能都和ActionBar一致了.

setsupportActionBar(toolbar)的作用:将Toolbar的外观和功能与ActionBar一致.

Toolbar的常用功能

修改标题栏上显示的文字内容:在AndroidManifest.xml文件中通过android:label属性指定

在Toolbar上设置action按钮:首先提前准备好要放的图片.新建一个menu的文件夹,然后再这个menu文件夹中新建Menu resource file创建一个toolbar.xml通过item标签来指定各个选项的图标,名称,和showAsAction属性.

注意:Toolbar上的按钮只会显示图标,菜单中的action按钮只会显示文字

4
5

运用之前学的知识创建一个菜单便可以了.

滑动菜单

滑动菜单就是将一些菜单项隐藏起来,而不是放置在主屏幕上,然后可以通过滑动的方式将菜单显示出来.

谷歌提供了一个DrawerLayout控件,借助于这个控件,实现滑动菜单简单又方便.

DrawerLayout用法

它是一个布局,在这个布局中允许放入两个直接子控件,第一个子控件是屏幕中显示的内容,第二个子控件是滑动菜单中显示的内容.(准确把握直接的含义,我们之前在学习scrollview时,scrollview也是允许一个直接子控件)

注意:DrawerLayout中第二个直接子控件中的layyout_gravity这个属性是必须要指定的,因为我们要告诉DrawerLayout滑动菜单是在屏幕左边还是右边,指定left表示滑动菜单在左边,指定right表示滑动菜单在右边.

android:layout_gravity="start"指定为start表示会根据系统语言进行判断,如果系统语言是从左往右的,比如英语,汉语,滑动菜单便在左边,如果系统语言是从右往左的,比如阿拉伯语,滑动菜单就在右边.

提示用户滑动菜单的功能

Material Design建议的做法是在Toolbar的最左边加入一个导航按钮,点击了导航按钮也会将滑动菜单的内容显示出来.这便相当于给用于提供了两种打开滑动菜单的方式.

在代码中实现的过程为:

1.调用findviewbyid()方法获取DrawerLayout的实例

2.调用getSupportActionBar()方法得到了ActionBar的实例,虽然这个ActionBar的具体实现是由Toolbar来实现的

3.调用ActionBar的setDisplayHomeAsUpEnabled()方法让导航按钮显示出来

4.调用setHomeAsUpIndicator()方法来设置一个导航按钮图标

5.接下来在onOptionsItemSelected()方法中对HomeAsUp按钮的点击事件进行处理,HomeAsUp按钮的id永远都是android.R.id.home

6.然后调用DrawerLayout的openDrawer()方法将滑动菜单显示出来(注意:openDrawer()方法要求传入一个Gravity参数,该参数要和XML中定义的行为一致,所以应该传入GravityCompat.START-------表示告诉DrawerLayout滑动菜单在屏幕左边还是右边,这里表示是根据系统语言进行判断)

6

NavigationView

在滑动菜单项中定制任意的布局,从而优化滑动菜单界面---------NavigationView是Design Support库中提供的一个控件,它不仅严格按照Material Design的要求进行设计,而且还将滑动菜单页面的实现变得非常简单.

NavigationView的用法

1.首先将这个库引入build.gradle文件的闭包,在dependencies闭包中添加如下内容:

compile'com.android.support:design:26.1.0'(因为NavigationView是Design Support库中提供的一个控件)

implementation'de.hdodenhof:circleimageview:2.2.0'

其中第一行是Design Support库,第二行是一个开源项目CircleImageView------可以轻松用来实现图片圆形化的功能.项目地址是:https://github.com/hdodenhof/CircleImageView  (在开始使用NavigationView之前我们要准备好两个东西:menu和headerLayout,其中menu是用来在NavigationView中显示具体的菜单项的,headerLayout是用来在NavigationView中显示头部布局的)

2.先准备menu

7

3.准备headerLayout,这是一个可以随意定制的布局

8

4.menu和headerLayout准备好了之后,才可完成NavigationView的定义.打开DrawerLayout的布局文件

9

5.处理菜单项的点击事件,修改代码为

获取到了NavigationView的实例

调用它的setCheckedItem()方法将菜单项中的其中一个菜单项设置为默认选中(方法中需要传入一个菜单项的id)

调用setNavigationItemSelectedListener()方法设置一个菜单项选中事件的监听器,当用户点击了任意菜单项时,就会回调到onNavigationItemSelected()方法中,我们可以在这个方法中填写相应的逻辑,我在这里调用了DrawerLayout的closeDrawers()方法将滑动菜单关闭.

悬浮按钮和可交互提示

悬浮按钮不属于主界面平面的一部分,而是位于另一个维度的,所以会给人一种悬浮的感觉

FloatingActionButton

FloatingActionButton是Design Support库中提供的一个控件,这个控件可以帮助我们比较轻松地实现悬浮按钮的效果.悬浮按钮默认使用colorAccent来作为按钮的颜色,可以通过给按钮指定一个图标来表明这个按钮的作用是什么.

FloatingActionButton的用法

首先如果build/gradle中闭包中没有引入相应的库的话要提前引入相应的库  compile'com.android.support:design:26.1.0'

提前为悬浮按钮准备好图片,然后在布局文件中引入悬浮按钮标签<android.support.design.widget.FloatingActionButton>.

android:layout_gravity="bottom|end"属性指定将这个控件放置到屏幕的右下方:其中end的工作原理和之前的start是一样的,即如果系统语言是从左往右的,那么end就表示在右边,反之在左边.

android:src="@drawable/ic_done"通过src属性给FolatingActionButton设置了一个图标

10

FloatingActionButton处理点击事件:和普通的Button设置点击事件没有什么区别都是先声明控件找到控件然后调用setOnClickListener()方法,在方法内写具体的逻辑.

Snackbar

更为先进的提示工具,是由Design Support库提供的(如果想要使用的话需要提前在闭包中引入相应的库,如果闭包中没有引入这个库的话).

Snackbar和Toast的区别

Toast的作用是告诉用户现在发生了什么事情,但同时用户只能被动接收这个事情.因为没有什么办法让用户进行选择.

Snackbar允许在提示当中加入一个可交互按钮,当用户点击按钮的时候可以执行一些额外的逻辑操作.(比如误删数据的提示)

Snackbar的用法

和Toast基本类似,只不过可以额外增加一个按钮的点击事件

11

CoordinatorLayout

加强版的FrameLayout,这个布局也是由Design Support库提供的,可以监听其所有的子控件的各种事件,然后自动帮我们做出最为合理的响应.比如弹出的Snackbar提示将悬浮按钮遮挡住了,而如果我们能让CoordinatorLayout监听到Snackbar的弹出事件,那么它会自动将内部的FloatingActionButton向上偏移,从而确保不会被Snackbar遮挡到.

12
13

卡片式布局

CardView

CardView是用于实现卡片式布局效果的重要控件,由appcompat-v7库提供,CardView实际上也是一个FrameLayout,只是额外提供了圆角和阴影的效果,看上去有立体的感觉.

用CardView之前需要引入相应的库compile'com.android.support:cardview-v7:26.1.0'(因为CardView由appcompat-v7库提供)

常用属性:app:cardCornerRadius="4dp" 指定卡片圆角的弧度,数值越大弧度越大

app:elevation属性指定卡片的高度,高度值越大,投影范围越大

具体例子结合RecycleView和CardView设计了一个水果的卡片式布局,代码都是之前的东西就不写了...

Glide在内部做了很多的非常复杂的逻辑操作,包括图片的压缩,我们只需要安心按照Glide的用法去加载图片即可,完全不用担心内存溢出的问题.

AppBarLayout

14

由Design Support库提供的AppBarLayout,它是一个垂直方向的LinearLayout,在内部做了很多滚动事件的封装,并应用了一些Material Design的设计理念.

主要作用:解决内容覆盖Toolbar的问题(比如图片覆盖住了Toolbar)

主要步骤分为:1.将被覆盖的Toolbar嵌入到AppBarLayout中

2.给RecyclerView指定一个布局行为(因为是RecyclerView覆盖住了Toolbar嘛)

15

在RecylcerView中使用app:layout_behavior="@string/appbar_scrolling_view_behavior"属性指定了一个布局行为(本例中是RecyclerView的滚动行为),当RecyclerView滚动的时候会将滚动事件通知给AppBarLayout.

16

当AppBarLayout接收到滚动事件的时候,它内部的子控件其实是可以指定如何去影响这些事件的,通过app:layout_behavior属性就能实现.然后在Toolbar中设置app:layout_scrollFlags="scroll|enterAlways|snap"其中scroll表示当RecyclerView向上滚动的时候,Toolbar会跟着一起向上滚动并实现隐藏;enterAlways表示当RecyclerView向下滚动的时候,Toolbar会跟着一起向下滚动并重新显示.snap表示当Toolbar还没有完全隐藏显示的时候,会根据当前滚动的距离,自动选择隐藏还是显示

下拉刷新

SwipeRefreshLayout是实现下拉刷新功能的核心类。它是由support-v4库提供的。我们把想要实现下拉刷新功能的控件放置到SwipeRefreshLayout中,就可以迅速让这个控件支持下拉刷新。

另外需要注意的是:由于RecyclerView现在变成了SwipeRefreshLayout的子控件,因此之前使用app:layout-behavior声明的布局行为现在也要移动到SwipeRefreshLayout中才行。

虽然RecyclerView已经支持下拉刷新功能,但是还是要在代码中处理具体逻辑才行。

1.声明控件 然后findViewbyId()找到控件,然后调用setColorSchemeResource()方法来设置下拉刷新进度条的颜色

2.调用setOnRefreshFruits()方法来设置一个下拉刷新的监听器,当触发了下拉刷新操作的时候就会回调这个监听器的onRefresh()方法,然后我们在这里处理具体的逻辑就可以了

通常情况下,onfresh()方法应该去网上请求最新的数据,然后再将这些数据展示出来

可折叠式标题栏

CollapsingToolbarLayout(实现可折叠式标题栏的效果)

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

CollapsingToolbarLayout是不能独立存在的,它在设计的时候被限定只能作为AppBarLayout的直接子布局来使用。而AppBarLayout又必须是CoordinatorLayout的子布局

CollapsingToolbarLayout属性:app:contentScrim用于指定CollapsingToolbarLayout在趋于折叠状态以及折叠之后的背景色

我们准备编的布局主要分为两个部分:一个是水果标题栏,一个是水果内容详情

标题栏部分:最外层布局我们设置为CoordinatorLayout,注意始终记得要定义一个xmln:app的命名空间,在Material Design的开发中会经常用到它.

然后在CoordinatorLayout内嵌套一个AppBarLayout,其中AppBarLayout布局很简单我们不做过多的解释;接下来再在AppBarLayout中嵌套一个CollapsingToolbarLayout.

17

接下来我们开始定义标题栏的具体内容,我们在CollapsingToolbarLayout中定义了一个ImageView和一个Toolbar,意味着我们的这个高级版的标题栏将是由普通的标题栏加上图片组合而成的.

18

我们比较陌生的属性app:layout_collapseMode:它用于指定当前控件在CollapsingToolbarLayout折叠过程中的折叠模式,其中Toolbar中指定为pin表示在折叠过程中位置始终不变,ImageView指定成parallax,表示会在折叠过程中产生一定的错位偏移,这种模式的视觉效果比较好.

水果内容详情部分:

水果内容详情的最外层部分我们使用了一个NestedScrollView.我们之前学过ScrollView(它允许使用滚动的方式来查看屏幕以外的数据),而NestedScrollView不仅允许使用滚动的方式来查看屏幕以外的数据还增加了嵌套响应滚动事件的功能.由于CoordinatorLayout本身就已经可以响应滚动事件了,因此我们在它的内部就需要使用NestedScrollView或RecyclerView这样的布局.

不管是ScrollView还是NestedScrollView在他们内部只能有一个直接子布局,如果我们想在里面放入更多的东西的话我们通常先嵌套一个LinearLayout,然后再LinearLayout中再放入具体的内容即可.

19

使用一个TextView来显示水果内容详情,并将TextView放在一个卡片式布局里面.我们还可以在界面上加一个悬浮按钮当然这个不是必需的.根据具体需求再添加即可.

20

FloatingActionButton中使用app:layout_anchor属性指定了一个锚点,我们将锚点设置为AppBarLayout,这样悬浮按钮就会出现在水果标题栏的区域内.接着又使用app:layout_anchorGravity属性将悬浮按钮定位在标题栏区域的右下角.

界面完成之后我们编写功能逻辑,

public class FruitActivity extends AppCompatActivity {

public static final String FRUIT_NAME="fruit_name";

public static final String FRUIT_IMAGE_ID="fruit_image_id";

@Override protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_fruit);

//通过Intent获取到传入的水果名和水果图片的ID Intent intent=getIntent();

//通过Intent获取到传入的水果名 String fruitName=intent.getStringExtra(FRUIT_NAME);

//通过Intent获取到传入水果图片的ID int fruitImageId=intent.getIntExtra(FRUIT_IMAGE_ID,0);

//这里的toolbar必须是V7包下的 Toolbar toolbar=findViewById(R.id.toolbar);

CollapsingToolbarLayout collapsingToolbarLayout=findViewById(R.id.collapsing_toolbar);

ImageView fruitImageView=findViewById(R.id.fruit_image_view);

TextView fruitContentText=findViewById(R.id.fruit_content_text);

//不然这里会报错 setSupportActionBar(toolbar); ActionBar actionBar=getSupportActionBar();

if (actionBar!=null){ actionBar.setDisplayHomeAsUpEnabled(true); }

collapsingToolbarLayout.setTitle(fruitName);

Glide.with(FruitActivity.this).load(fruitImageId).into(fruitImageView);

String fruitContent=generateFruitContent(fruitName);

fruitContentText.setText(fruitContent); }

private String generateFruitContent(String fruitName) {

StringBuilder fruitContent=new StringBuilder();

for (int i=0;i<500;i++){ fruitContent.append(fruitName); }

return fruitContent.toString(); }

@Override public boolean onOptionsItemSelected(MenuItem item) {

switch (item.getItemId()){

case android.R.id.home:

finish();

return true; }

return super.onOptionsItemSelected(item); }}

然后记得处理RecyclerView的点击事件:

final ViewHolder holder=new ViewHolder(view);

holder.cardView.setOnClickListener(new View.OnClickListener() {

@Override

    public void onClick(View v) {

int position=holder.getAdapterPosition();

        Fruit fruit=mFruitList.get(position);

        Intent intent=new Intent(mContext,FruitActivity.class);

        intent.putExtra(FruitActivity.FRUIT_NAME,fruit.getName());

        intent.putExtra(FruitActivity.FRUIT_IMAGE_ID,fruit.getImageId());

        mContext.startActivity(intent);}});

return holder;

给CardView注册一个点击事件监听器,然后再点击事件中获取当前点击项的水果名和水果资源ID,把他们传入到Intent中,最后调用startActivity()方法启动FruitActivity.

充分利用系统状态空间

Android5.0系统之前我们无法对状态栏的背景或颜色进行操作的,Android5.0之后才有了Material Design的概念,所以我们实现一个系统差异型的效果,在Android5.0之后的系统,使用背景图和状态栏融合的模式,在之前的系统中使用普通模式.

想要背景图和系统状态栏融合,需要借助android:fitsSystemWindows这个属性:

21

然后还要将程序的主题中将状态栏颜色指定为透明色才行,在主题中将android:statusBarColor这个属性指定为@android:color/transparent即可,问题在于android:statusBarColor这个属性是从API21,也就是Android5.0系统开始才有的,之前的系统无法指定这个属性.系统差异型的功能实现就要从这里开始了.

22
23

本章中用到的库总结:

24

知识体系总结:


25
26
27
28
29
30
31
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 156,907评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,546评论 1 289
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,705评论 0 238
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,624评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,940评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,371评论 1 210
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,672评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,396评论 0 195
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,069评论 1 238
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,350评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,876评论 1 256
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,243评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,847评论 3 231
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,004评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,755评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,378评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,266评论 2 259

推荐阅读更多精彩内容