Material Design 基础

作者: 王世军Steven | 来源:发表于2017-01-19 17:22 被阅读211次

    0 简介

    Material Design 是由Google工程师们基于传统的设计原则,集合丰富的创意和科学技术所发明的一套全新的界面设计语言,包含了视觉,运动,互动效果等特性.

    1 ToolBar 控件

    ToolBar 控件是Google推荐使用的,它不仅继承了ActionBar的所有功能,而且使用起来十分灵活,可以和其他控件结合使用,从而打造出Material Design 效果.

    1.1 隐藏系统原有的 ActionBar

    AndroidManifest.xml 文件中找到<application> 标签中的 theme 属性的值.一般是 @style/AppTheme,跳转到 AppTheme的定义(res/values/styles),如下 :

    <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>
    

    其中的 Theme.AppCompat.Light.DarkActionBar 是一个深色的ActionBar主题.我们可以将其替换成Theme.AppCompat.Light.NoActionBar 则可以将ActionBar隐藏. 接着我们就可以使用ToolBar了.对MainActivity的布局文件修改如下

    <!-- 
        1. xmlns:app="http://schemas.android.com/apk/res-auto" 新增的命名控件,
           主要是为了兼容 Android 5.0 以前的设备.
        2. android:theme="@style/ThemeOverlay.AppCompat.Dark" 设置ToolBar主题.
           因为我们App使用的是 Light主题,因此文字是深色,再次让此控件单独使用此
           主题,效果是显示的文字是亮的.
        3. app:popupTheme="@style/ThemeOverlay.AppCompat.Light" 设置菜单主题色.
           popupTheme 属性是 5.0 新增因此需要使用app命名空间做兼容处理.
        4. android:layout_height="?attr/actionBarSize" 设置ToolBar的高度和
           ActionBar一样.
    -->
    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout
        android:id="@+id/activity_main"
        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="match_parent"
        >
        <!-- ToolBar 替代 ActionBar -->
        <android.support.v7.widget.Toolbar
            android:id="@+id/tb_actionbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="@color/colorPrimary"
            android:theme="@style/ThemeOverlay.AppCompat.Dark"
            app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
            />
    </RelativeLayout>
    

    接着在Activity中进行设置

    // 1. 找到Toolbar
    Toolbar toolbar = (Toolbar) findViewById(R.id.tb_actionbar);
    // 2. 设置ActionBar
    setSupportActionBar(toolbar);
    

    这样基本的设计就完成了.效果和ActionBar看上去一样.

    1.2 为 ToolBar 添加action

    • res/ 下创建 menu 文件夹,然后在res/menu/ 下创建 toolbar_menu.xml内容如下 :
    <?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">
        <!--
            1. app:showAsAction="always" 用来设置按钮的显示位置.
                1.1 always : 永远显示在Toolbar 中.
                1.2 ifRoom : 如果空间足够则显示在ToolBar中.
                1.3 never  : 永远显示在菜单中
            2. ToolBar 中的Action只会显示图片,菜单中的action只会显示文字.
        -->
        <item android:id="@+id/tb_backup"
              android:title="BackUp"
              android:icon="@drawable/icon_backup"
              app:showAsAction="always"
            />
        <item android:id="@+id/tb_delete"
              android:title="Delete"
              android:icon="@drawable/icon_delete"
              app:showAsAction="ifRoom"
            />
        <item android:id="@+id/tb_settings"
              android:title="Settings"
              android:icon="@drawable/icon_settings"
              app:showAsAction="never"
            />
    </menu>
    
    • 在Activity总重写和菜单相关的方法
    // 加载菜单布局文件.
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.toolbar_menu,menu);
        return true;
    }
    // 添加菜单选项点击事件. 我们在每一个action使用Toast进行提示.
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()){
            case R.id.tb_backup:
                Toast.makeText(MainActivity.this,"备份",Toast.LENGTH_SHORT).show();
                break;
            case R.id.tb_delete:
                Toast.makeText(MainActivity.this,"删除",Toast.LENGTH_SHORT).show();
                break;
            case R.id.tb_settings:
                Toast.makeText(MainActivity.this,"设置",Toast.LENGTH_SHORT).show();
                break;
        }
        return true;
    }
    

    运行程序效果图如下 :

    ToolBar 效果图

    2 滑动菜单

    滑动菜单就是将一些单选项隐藏起来,而不是放在主屏幕上,然后可以通过滑动将菜单显示出来.这zhong种方式节省了空间又实现了非常好的滑动效果.Material Design 推荐使用这种方式. Google提供了DrawerLayout 控件来实现这种效果.

    2.1 DrawerLayout 使用

    在DrawerLayout 布局中允许添加两个直接子控件, 第一个是屏幕中显示的内容;第二个是菜单中显示的内容.现在我们对Activity布局文件进行如下修改.

    <!-- 将根布局替换成 DrawLayout  -->
    <?xml version="1.0" encoding="utf-8"?>
    <android.support.v4.widget.DrawerLayout
        android:id="@+id/dl_main"
        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="match_parent"
        >
        <!-- 屏幕中的内容 -->
        <FrameLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">
            <android.support.v7.widget.Toolbar
                android:id="@+id/tb_actionbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                android:background="@color/colorPrimary"
                android:theme="@style/ThemeOverlay.AppCompat.Dark"
                app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
                />
        </FrameLayout>
        <!--
            菜单中的内容
            1. android:layout_gravity="start" 该属性必须设置,
               它表示了菜单弹出的方向.
               start : 表示根据系统语言进行判断弹出方向.
               left  : 左侧弹出
               right : 右侧弹出.
         -->
        <TextView
            android:layout_width="match_parent"
            android:layout_gravity="start"
            android:text="菜单"
            android:textSize="32sp"
            android:background="#FF00FF"
            android:layout_height="match_parent"/>
    </android.support.v4.widget.DrawerLayout>
    

    此时运行程序就可以从通过边缘滑动将菜单显示出来 效果图如下.

    Paste_Image.png

    接下来我们将在ToolBar 中增添一个按钮通过点击该按钮来实现打开菜单,类似于QQ上的点击头像,引出菜单.我们修改Activity代码如下:

    • onCreate() 添加如下代码.

      • 我们首先通过 findViewById() 方法获取到DrawerLayout实例.
      • 通过 getSupportActionBar() 来获取到ActionBar实例,此时的ActionBar实际是一个ToolBar.
      • 通过 setDisplayHomeAsUpEnabled(true) 方法让导航按钮显示出来.
      • 通过 setHomeAsUpIndicator() 来设置导航按钮的图标.
    // 实现按钮弹出菜单
    mDrawerLayout = (DrawerLayout) findViewById(R.id.dl_main);
    ActionBar actionBar = getSupportActionBar();
    if (actionBar != null){
        // 设置显示导航按钮.也就是返回按钮
        actionBar.setDisplayHomeAsUpEnabled(true);
        // 设置返回按钮的图片
        actionBar.setHomeAsUpIndicator(R.drawable.ic_menu);
    }
    
    • 在 onOptionsItemSelected 中添加如下代码

      • switch(item.getItemId()) 中增加一个case分支 .用于处理用户点击导航按钮时的响应处理.
      • 通过 DrawerLayout 的 openDrawer() 打开菜单. 该函数需要传入一个Gravity参数.

    注意 :系统提供的导航按钮的id是 android.R.id.home

    // 新增加一个case 判断
    case android.R.id.home:
    // 弹出菜单.
    mDrawerLayout.openDrawer(GravityCompat.START);
    break;
    

    2.2 NavigationView 的使用

    NavigationView 是 design support 库中提供的一个控件 ,它严格按照 Material Design 来设计的 , 它使滑动菜单的实现变的非常简单. 它一般分为上下两部分;下面是菜单选项,上面是其他信息.

    注意 : 由于我们需要使用design support库和图片kuang'jia框架因此需要添加如下依赖

    // 添加design support 库依赖
    compile 'com.android.support:design:24.2.1'
    // 添加圆形图片裁剪图第三方库依赖
    compile 'de.hdodenhof:circleimageview:2.1.0'
    
    • res/menu/下创建菜单布局文件 nav_menu.xml 内容如下:
    <?xml version="1.0" encoding="utf-8"?>
    <menu xmlns:android="http://schemas.android.com/apk/res/android">
        <!--
            1. android:checkableBehavior="single" 设置在该组中的item是单选的.
        -->
        <group android:checkableBehavior="single">
            <item
                android:id="@+id/nav_call"
                android:title="Call"
                android:icon="@drawable/ic_menu_call"
                />
            <item
                android:id="@+id/nav_friend"
                android:title="Friend"
                android:icon="@drawable/ic_menu_friend"
                />
            <item
                android:id="@+id/nav_location"
                android:title="Location"
                android:icon="@drawable/ic_menu_location"
                />
            <item
                android:id="@+id/nav_mail"
                android:title="Mail"
                android:icon="@drawable/ic_menu_mail"
                />
            <item
                android:id="@+id/nav_task"
                android:title="Tasks"
                android:icon="@drawable/ic_menu_task"
                />
        </group>
    </menu>
    
    • 创建上半部分布局文件.
    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                  android:layout_width="match_parent"
                    android:padding="10dp"
                    android:background="?attr/colorPrimary"
                  android:layout_height="180dp">
        <!-- 使用圆形图片框架 -->
        <de.hdodenhof.circleimageview.CircleImageView
            android:layout_width="70dp"
            android:id="@+id/iv_head_pic"
            android:src="@drawable/head_pic"
            android:layout_centerInParent="true"
            android:layout_height="70dp"/>
        <TextView
            android:layout_width="wrap_content"
            android:id="@+id/tv_mail"
            android:text="wsj_china_cpu@163.com"
            android:textSize="14sp"
            android:textColor="#FFF"
            android:layout_alignParentBottom="true"
            android:layout_height="wrap_content"/>
        <TextView
            android:layout_width="wrap_content"
            android:id="@+id/tv_name"
            android:layout_above="@id/tv_mail"
            android:text="WSJ"
            android:textColor="#FFF"
            android:textSize="14sp"
            android:layout_height="wrap_content"/>
    
    </RelativeLayout>
    
    • 将MainActivity的布局文件中的TextView替换成 NavigationView
    <android.support.design.widget.NavigationView
    android:layout_width="match_parent"
    android:id="@+id/nav_view"
    android:layout_gravity="start"
    app:menu="@menu/nav_menu"
    app:headerLayout="@layout/header_layout"
    android:layout_height="match_parent"/>
    
    • 在 MainActivity 的onCreate() 中添加如下代码

      • 首先获取到NavigationView实例.
      • 通过 setCheckedItem() 设置默认选择的item
      • 通过 setNavigationItemSelectedListener 设置item点击事件监听.
      • 使用Toast打印点击的选项.
      • 通过 closeDrawers() 关闭菜单.
     // 导航菜单
     NavigationView navigationView =
             (NavigationView) findViewById(R.id.nav_view);
     // 设置默认选项
     navigationView.setCheckedItem(R.id.nav_call);
     // 设置Item点击
     navigationView.setNavigationItemSelectedListener(
             new NavigationView.OnNavigationItemSelectedListener() {
         @Override
         public boolean onNavigationItemSelected(@NonNull MenuItem item) {
             switch (item.getItemId()){
                 case R.id.nav_call:
                     Toast.makeText(MainActivity.this
                             ,"电话",Toast.LENGTH_SHORT).show();
                     break;
                 case R.id.nav_friend:
                     Toast.makeText(MainActivity.this
                             ,"朋友",Toast.LENGTH_SHORT).show();
                     break;
                 case R.id.nav_mail:
                     Toast.makeText(MainActivity.this
                             ,"邮件",Toast.LENGTH_SHORT).show();
                     break;
                 case R.id.nav_location:
                     Toast.makeText(MainActivity.this
                             ,"地位",Toast.LENGTH_SHORT).show();
                     break;
                 case R.id.nav_task:
                     Toast.makeText(MainActivity.this
                             ,"任务",Toast.LENGTH_SHORT).show();
                     break;
             }
             // 关闭菜单
             mDrawerLayout.closeDrawers();
             return true;
         }
     });
    

    此时的效果图如下


    Paste_Image.png

    3 悬浮按钮和可交互提示

    立面设计师Material Design中的一条非常重要的设计思想. 悬浮按钮就体现了立面设计的思想.这种按钮不属于主界面的一部分,而是位于另外一个纬度,给人一种悬浮的感觉.

    3.1 FloatActionButton 使用

    FloatActionButton 是Google提供的悬浮按钮控件.

    • 在 MainActivity中添加FloatActionButton.
    <!--
        添加悬浮按钮
        1. app:elevation="8dp" 指定高度值(立体) 高度值也大投影就越大,
           投影效果也就越淡.
     -->
    <android.support.design.widget.FloatingActionButton
        android:layout_width="wrap_content"
        android:id="@+id/fab"
        android:layout_gravity="bottom|end"
        android:layout_margin="16dp"
        android:src="@drawable/ic_done"
        app:elevation="8dp"
        android:layout_height="wrap_content"/>
    
    • 在MainActivity中添加点击事件
    // 悬浮按钮的点击事件.
    FloatingActionButton fab =
            (FloatingActionButton) findViewById(R.id.fab);
    fab.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Toast.makeText(MainActivity.this,"悬浮框",
                    Toast.LENGTH_SHORT).show();
        }
    });
    

    3.2 SnackBar 可交互提醒控件.

    该控件和Toast一样是用来提示用户的但是它增加了取消选项.也就是可交互的.但是他和Dialog也不同,他也会自动消失.

    • 修改MainActivity中悬浮按钮点击事件的处理代码
    // 悬浮按钮的点击事件.
    FloatingActionButton fab =
            (FloatingActionButton) findViewById(R.id.fab);
    fab.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            // 使用SnackBar来提示.
            Snackbar.make(v,"Data to delete!!",Snackbar.LENGTH_SHORT)
                    .setAction("Undo", new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            Toast.makeText(MainActivity.this,"悬浮框",
                                    Toast.LENGTH_SHORT).show();
                        }
                    })
                    .show();
        }
    });
    

    效果图如下,可以看到有 "UNDO" 选项.

    Paste_Image.png

    3.3 CoordinatorLayout

    CoordinatorLayout 可以认为是加强版本的的FrameLayout ,在普通情况下他和FrameLayout 用法基本一致. 但是他可以监听子控件的各种shi'jian事件,并自动帮我们做出是适当的行为.比如上面的的SnackBar 弹出的时候会覆盖FloatActionButton,如果使用CoordinatorLayout则会自动将SnackBar上移.

    • 我们修改MainActivity的布局文件
    <!-- 屏幕中的内容
        将FrameLayout 换乘 CoordinatorLayout
    -->
    <android.support.design.widget.CoordinatorLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <android.support.v7.widget.Toolbar
            android:id="@+id/tb_actionbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="@color/colorPrimary"
            android:theme="@style/ThemeOverlay.AppCompat.Dark"
            app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
            />
    
        <!--
            添加悬浮按钮
         -->
        <android.support.design.widget.FloatingActionButton
            android:layout_width="wrap_content"
            android:id="@+id/fab"
            android:layout_gravity="bottom|end"
            android:layout_margin="16dp"
            android:src="@drawable/ic_done"
            app:elevation="8dp"
            android:layout_height="wrap_content"/>
    </android.support.design.widget.CoordinatorLayout>
    

    效果图 ,可以看到在SnackBar弹出时,FloatActionButton上移.

    Paste_Image.png

    4 卡片式布局

    卡片式布局也是Material Design 中提出的一个概念,他可以让页面中的元素看起来像是子啊卡片中一样,并且可以拥有圆角和投影.

    4.1 CardVeiw

    CardView 是Google提供的一种卡片布局控件. 现在我们要实现展示图片.

    • build.gradle 文件中添加依赖.

      • 使用RecycleView实现网格布局.
      • 使用CardView实现每一个item的图片卡展示.
      • 由于我们使用的图片可能会很大因此使用Glide框架加载图片,他内部会进行优化处理,使用起来也很简单.Glide是一个功能强大的图片加载库.Github地址 : https://github.com/bumptech/glide.
    // 添加RecycleView 依赖
    compile 'com.android.support:recyclerview-v7:24.2.1'
    // 添加CardView依赖
    compile 'com.android.support:cardview-v7:24.2.1'
    // 添加Glide依赖
    compile 'com.github.bumptech.glide:glide:3.7.0'
    
    • 修改 activity_main.xml 文件.

      • 在内容区添加 RecyclerView ,用来展示图片.
      • AppBarLayout 来包裹 ToolBar 以及设置RecycleView的 app:layout_behavior="@string/appbar_scrolling_view_behavior" 属性来避免RecycleView将ToolBar覆盖.
      • app:layout_scrollFlags="scroll|enterAlways|snap" 用来设置在RecycleView滚动时ToolBar的响应.
        1. scroll : 表示RecycleView向上滑动时,ToolBar也会向上滚动实现隐藏
        2. enterAlways : RecycleView向下滑动,ToolBar也会向下滑动,重新显示.
        3. snap : 停止时根据当前显示的比例确定是显示还是隐藏.
        
    <?xml version="1.0" encoding="utf-8"?>
    <android.support.v4.widget.DrawerLayout
        android:id="@+id/dl_main"
        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="match_parent"
        >
        <!-- 屏幕中的内容 -->
        <android.support.design.widget.CoordinatorLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">
            <!--
                1. xmlns:app="http://schemas.android.com/apk/res-auto" 新增的命名控件,
                   主要是为了兼容 Android 5.0 以前的设备.
                2. android:theme="@style/ThemeOverlay.AppCompat.Dark" 设置ToolBar主题.
                    因为我们App使用的是 Light主题,因此文字是深色,再次让此控件单独使用此
                    主题,效果是显示的文字是亮的.
                3. app:popupTheme="@style/ThemeOverlay.AppCompat.Light" 设置菜单主题色.
                4. android:layout_height="?attr/actionBarSize" 设置ToolBar的高度和
                    ActionBar一样.
            -->
            <!-- 使用 AppBarLayout 包裹来解决覆盖问题
                1. app:layout_scrollFlags="scroll|enterAlways|snap"
                   子啊子控件中使用这个数据来设置内部控件如何响应滚动事件.
                   scroll : 表示RecycleView向上滑动时,ToolBar也会向上滚动实现隐藏
                   enterAlways : RecycleView向下滑动,ToolBar也会向下滑动,重新显示.
                   snap : 停止时根据当前显示的比例确定是显示还是隐藏.
             -->
            <android.support.design.widget.AppBarLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content">
                <android.support.v7.widget.Toolbar
                    android:id="@+id/tb_actionbar"
                    android:layout_width="match_parent"
                    android:layout_height="?attr/actionBarSize"
                    android:background="@color/colorPrimary"
                    android:theme="@style/ThemeOverlay.AppCompat.Dark"
                    app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
                    app:layout_scrollFlags="scroll|enterAlways|snap"
                    />
            </android.support.design.widget.AppBarLayout>
    
            <!--
                1. 添加RecycleView
             -->
            <android.support.v7.widget.RecyclerView
                android:id="@+id/rv_show"
                android:layout_width="match_parent"
                app:layout_behavior="@string/appbar_scrolling_view_behavior"
                android:layout_height="match_parent"/>
    
    
            <!--
                添加悬浮按钮
                1. app:elevation="8dp" 指定高度值(立体) 高度值也大投影就越大,
                   投影效果也就越淡.
             -->
            <android.support.design.widget.FloatingActionButton
                android:layout_width="wrap_content"
                android:id="@+id/fab"
                android:layout_gravity="bottom|end"
                android:layout_margin="16dp"
                android:src="@drawable/ic_done"
                app:elevation="8dp"
                android:layout_height="wrap_content"/>
        </android.support.design.widget.CoordinatorLayout>
    
        <!--
            菜单中的内容
            1. android:layout_gravity="start" 该属性必须设置,
               它表示了菜单弹出的方向.
               start : 表示根据系统语言进行判断弹出方向.
               left  : 左侧弹出
               right : 右侧弹出.
            2. 设置菜单布局 app:menu="@menu/nav_menu"
            3. 设置上面的布局 app:headerLayout="@layout/header_layout"
         -->
        <android.support.design.widget.NavigationView
            android:layout_width="match_parent"
            android:id="@+id/nav_view"
            android:layout_gravity="start"
            app:menu="@menu/nav_menu"
            app:headerLayout="@layout/header_layout"
            android:layout_height="match_parent"/>
    
    
    </android.support.v4.widget.DrawerLayout>
    
    • 创建 item_panda.xml 作为RecycleView的Item布局

      • 使用CardView作为跟布局实现卡片效果.
      • 使用 app:cardCornerRadius="4dp" 设置卡片圆角半径.
      • 使用 LinearLayout 设置子控件的位置.
    <?xml version="1.0" encoding="utf-8"?>
    <!--
        1. 使用 CardView作为跟布局.
        2. app:cardCornerRadius="4dp" 设置圆角半径.
        3. 使用一个LeanerLayout 来定位子控件位置
    -->
    <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_margin="5dp"
        app:cardCornerRadius="4dp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <LinearLayout
            android:orientation="vertical"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
            <ImageView
                android:id="@+id/iv_pic"
                android:layout_width="match_parent"
                android:scaleType="centerCrop"
                android:layout_height="100dp"/>
            <TextView
                android:id="@+id/tv_pic"
                android:layout_gravity="center_horizontal"
                android:layout_margin="5dp"
                android:textSize="16dp"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"/>
        </LinearLayout>
    </android.support.v7.widget.CardView>
    
    • 创建自定义Adapter
    public class PandaAdapter extends RecyclerView.Adapter<PandaAdapter.ViewHolder> {
    
        private Context mContext;
        private List<Panda> mPandaList;
    
        public PandaAdapter(List<Panda> list){
            mPandaList = list;
        }
    
    
        @Override
        public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            if (mContext == null)
                mContext = parent.getContext();
            // 加载布局文件
            View view = LayoutInflater.from(mContext).inflate(R.layout.item_panda,parent,false);
            return new ViewHolder(view);
        }
    
        @Override
        public void onBindViewHolder(ViewHolder holder, int position) {
            // 设置数据.
            Panda panda = mPandaList.get(position);
            holder.mTextView.setText(panda.getName());
            // 使用 Glide 加载图片,会进行内存优化.
            Glide.with(mContext).load(panda.getImageId()).into(holder.mImageView);
        }
    
        @Override
        public int getItemCount() {
            return mPandaList.size();
        }
    
        static class ViewHolder extends RecyclerView.ViewHolder{
            CardView mCardView;
            ImageView mImageView;
            TextView mTextView;
    
            public ViewHolder(View itemView) {
                super(itemView);
                mCardView = (CardView) itemView;
                mImageView = (ImageView) itemView.findViewById(R.id.iv_pic);
                mTextView = (TextView) itemView.findViewById(R.id.tv_pic);
            }
        }
    
    }
    
    • Panda类
    public class Panda {
        private static final String TAG = "Panda";
        private String name;
        private int imageId;
        public Panda(String name,int id){
            this.name = name;
            this.imageId = id;
        }
    
        public int getImageId() {
            return imageId;
        }
    
        public String getName() {
            return name;
        }
    }
    
    • 在MainActivity 的 onCreate()中添加如下代码

      • 首先获取到RecycleView实例.
      • 创建网格布局管理器 GridLayoutManager .
      • 为RecycleView 设置布局管理器.
      • 设置适配器
    // 加载图片
    initPandas();
    // 首先获取到RecycleView实例
    RecyclerView recyclerView = (RecyclerView) findViewById(R.id.rv_show);
    // 创建网格布局管理器
    GridLayoutManager layoutManager = new GridLayoutManager(this,2);
    // 为RecycleView 设置布局管理器
    recyclerView.setLayoutManager(layoutManager);
    // 设置适配器
    mAdapter = new PandaAdapter(mPandaList);
    recyclerView.setAdapter(mAdapter);
    

    效果图

    Paste_Image.png

    5 下拉刷新

    5.1 SwipeRefreshLayout

    SwipeRefreshLayout 是Google提供的实现下拉刷新的类.他是由 support-v4提供的.我们只需要将要实现下拉刷新的控件放到其中即可.

    • 使用SwipeRefreshLayout包裹RecycleView控件
     <!-- 下拉刷新 -->
     <android.support.v4.widget.SwipeRefreshLayout
         android:id="@+id/srfl"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         app:layout_behavior="@string/appbar_scrolling_view_behavior">
         <!--
              1. 添加RecycleView
          -->
         <android.support.v7.widget.RecyclerView
             android:id="@+id/rv_show"
             android:layout_width="match_parent"
             android:layout_height="match_parent"/>
     </android.support.v4.widget.SwipeRefreshLayout>
    
    • 在 MainActivity 的 onCreate() 方法中添加如下代码
    // 实现下拉刷新
    // 找到实例
    mSwipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.srfl);
    // 设置加载框颜色
    mSwipeRefreshLayout.setColorSchemeResources(R.color.colorPrimary);
    // 设置刷新监听
    mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
        @Override
        public void onRefresh() {
            // 更新数据
            refreshPanda();
        }
    
        private void refreshPanda() {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    // 延时 2 s
                    SystemClock.sleep(2 * 1000);
                    // 更新数据.
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            initPandas();
                            // 通知RecycleView数据变更.
                            mAdapter.notifyDataSetChanged();
                            // 停止刷新
                            mSwipeRefreshLayout.setRefreshing(false);
                        }
                    });
                }
            }).start();
        }
    });
    

    继续优化

    6. 可折叠式标题栏

    Material Design 对标题栏的样式并没有限制在ActionBar原来的样式,因此我们完全可以定义一种新的样式.
    利用CollapsingToolBarLayout我们可以实现可折叠的标题栏.先看效果图.

    Android 上滑前 4.3 Android 上滑前 5.1 Android 上滑后 5.1

    注意 : 由于5.0 之前是没有办法获取设置状态栏的因此,此处需要不同处理

    6.1 CollapsingToolBarLayout

    CollapsingToolBarLayout 是不能独立存在的他在设计的时候就被限定为只能作为AppBarLayout的子布局来使用.而AppBarLayout 又必须是CoordinateLayout的子布局,因此我们需要综合使用上面介绍的知识点.

    • 创建activity_panda.xml 布局文件,布局文件结构比较复杂,但是都有注释.

      • 使用 CoordinatorLayout 作为根跟布局,因为我们需要他的事件监听功能.
      • 标题部分的根布局使用 AppBarLayout 标题的的子布局使用了CollapsingToolbarLayout,它可以帮我们实现折叠功能.
      • 详情模块使用 NestedScrollView ,他和普通的ScrollView功能yi'zhi一致,只是增加了嵌套响应滚动事件的功能.内部使用了一个 CardView实现卡片效果.
      • 最后增加了一个悬浮按钮作为评论按钮.
    <?xml version="1.0" encoding="utf-8"?>
    <!--
        CoordinatorLayout 加强版的FrameLayout.
    -->
    <android.support.design.widget.CoordinatorLayout
        android:id="@+id/activity_panda"
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:fitsSystemWindows="true"
        android:layout_height="match_parent"
        >
        <!--
            1. AppBarLayout 只能作为CoordinatorLayout的子布局
        -->
        <!-- 标题 -->
        <android.support.design.widget.AppBarLayout
            android:id="@+id/abl_panda"
            android:fitsSystemWindows="true"
            android:layout_width="match_parent"
            android:layout_height="250dp">
            <!--
                1. CollapsingToolbarLayout 只能作为AppBarLayout 的子布局
                   因此需要使用AppBarLayout包装一层
                2. contentScrim 用来指定CollapsingToolBarLayout 折叠之后的背景色.
                   CollapsingToolBarLayout 折叠之后就是一个ToolBar.
                3. exitUntilCollapsed 当CollapsingToolBarLayout随着滚动完成折叠
                   之后就保留在界面上.不再移出屏幕.
            -->
            <android.support.design.widget.CollapsingToolbarLayout
                android:id="@+id/ctl_panda"
                android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
                app:contentScrim="?attr/colorPrimary"
                app:layout_scrollFlags="scroll|exitUntilCollapsed"
                android:layout_width="match_parent"
                android:fitsSystemWindows="true"
                android:layout_height="match_parent">
                <!-- 开始定义标题栏 -->
                <!--
                    1. layout_collapseMode : 指定当前控件在CollapsingToolbarLayout
                       折叠过程中的模式.
                       1.1 parallax : 折叠过程总会有一定的错位现象,视觉效果很好.
                       1.2 pin : 折叠过程中位置始终保持不变.
                -->
                <ImageView
                    android:layout_width="match_parent"
                    android:id="@+id/iv_panda_title_pic"
                    android:scaleType="centerCrop"
                    app:layout_collapseMode="parallax"
                    android:fitsSystemWindows="true"
                    android:layout_height="match_parent"/>
                <android.support.v7.widget.Toolbar
                    android:layout_width="match_parent"
                    app:layout_collapseMode="pin"
                    android:id="@+id/tb_panda_title_txt"
                    android:layout_height="?attr/actionBarSize"/>
            </android.support.design.widget.CollapsingToolbarLayout>
        </android.support.design.widget.AppBarLayout>
        <!--
            1. NestedScrollView 在ScrollView的基础上增加了嵌套响应滚动事件的功能.
            2. NestedScrollView 内部只可以有一个直接子布局,因此可以用一LinearLayout包装.
        -->
        <!-- 详情 -->
        <android.support.v4.widget.NestedScrollView
            android:layout_width="match_parent"
            app:layout_behavior="@string/appbar_scrolling_view_behavior"
            android:layout_height="match_parent">
            <LinearLayout
                android:layout_width="match_parent"
                android:orientation="vertical"
                android:layout_height="wrap_content">
                <android.support.v7.widget.CardView
                    android:layout_width="match_parent"
                    android:layout_marginTop="35dp"
                    android:layout_marginLeft="15dp"
                    android:layout_marginRight="15dp"
                    android:layout_marginBottom="15dp"
                    app:cardCornerRadius="4dp"
                    android:layout_height="wrap_content">
                    <TextView
                        android:layout_width="wrap_content"
                        android:id="@+id/tv_panda_text"
                        android:layout_margin="10dp"
                        android:layout_height="wrap_content"/>
                </android.support.v7.widget.CardView>
    
            </LinearLayout>
        </android.support.v4.widget.NestedScrollView>
        <!-- 评论 -->
        <android.support.design.widget.FloatingActionButton
            android:layout_width="wrap_content"
            android:layout_margin="16dp"
            android:id="@+id/fab_comment"
            android:src="@drawable/comment"
            app:layout_anchorGravity="bottom|end"
            android:layout_height="wrap_content"/>
    </android.support.design.widget.CoordinatorLayout>
    
    
    • 创建 PandaActivity

      • 获取到传进来的资料数据.
      • 设置ActionBar 为 ToolBar.
      • 设置导航按钮为返回按钮.
      • 设置相关数据.
    public class PandaActivity extends AppCompatActivity {
    
        // 熊猫名称
        public static final String PANDA_NAME = "panda_name";
        // 熊猫图片ID
        public static final String PANDA_IMAGE_ID = "panda_image_id";
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_panda);
            // 获取数据.
            Intent intent = getIntent();
            String pandaName = intent.getStringExtra(PANDA_NAME);
            int pandaImageId = intent.getIntExtra(PANDA_IMAGE_ID,0);
            // 获取控件
            Toolbar toolbar = (Toolbar) findViewById(R.id.tb_panda_title_txt);
            CollapsingToolbarLayout collapsingToolbar =
                    (CollapsingToolbarLayout) findViewById(R.id.ctl_panda);
            ImageView pandaImageView = (ImageView) findViewById(R.id.iv_panda_title_pic);
            TextView pandaTextView = (TextView) findViewById(R.id.tv_panda_text);
            // 设置 ActionBar
            setSupportActionBar(toolbar);
            // 设置显示返回导航按钮
            ActionBar actionBar = getSupportActionBar();
            if (actionBar != null)
                actionBar.setDisplayHomeAsUpEnabled(true);
            // 设置标题
            collapsingToolbar.setTitle(pandaName);
            // 设置图片
            Glide.with(this).load(pandaImageId).into(pandaImageView);
            // 设置内容
            String content = getratePandaContent(pandaName);
            pandaTextView.setText(content);
        }
        // 随机生成内容
        private String getratePandaContent(String pandaName) {
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < 200; i++) {
                sb.append(pandaName);
            }
            return sb.toString();
        }
    
        // 处理返回按钮点击事件
        @Override
        public boolean onOptionsItemSelected(MenuItem item) {
            switch (item.getItemId()){
                case android.R.id.home:
                    finish();
                    return true;
            }
            return super.onOptionsItemSelected(item);
        }
    }
    
    
    • 修改MainAdapter 为Item增加点击事件,修改之后就可以实现折叠功能了.
    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        if (mContext == null)
            mContext = parent.getContext();
        // 加载布局文件
        View view = LayoutInflater.from(mContext).inflate(R.layout.item_panda,parent,false);
        final ViewHolder holder = new ViewHolder(view);
    
        // 添加点击事事件
        holder.mCardView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                final int pos = holder.getAdapterPosition();
                Panda panda = mPandaList.get(pos);
                Intent intent = new Intent(mContext, PandaActivity.class);
                intent.putExtra(PandaActivity.PANDA_NAME,panda.getName());
                intent.putExtra(PandaActivity.PANDA_IMAGE_ID,panda.getImageId());
                mContext.startActivity(intent);
            }
        });
        return holder;
    }
    

    6.2 充分利用系统状态栏空间

    在Android 5.0 之前我们无法对状态栏的颜色和背景进行操作. 在Android 5.0 之后的系统都是支持这个功能的.因此这里实现一个系统差异型的效果. 在Android 5.0 之后的系统中使用背景图和状态栏融合的模式.在之前的系统中使用普通模式.

    android:fitsSystemWindows 属性可以实现系统状态栏和背景图片融合的效果.我们只需要将需要融合的控件以及他的所有的父控件的该属性设置为 true 就可以实现.

    对于我们来说我们需要 ImageView 实现该效果那么就需要将,它和他的父控件的该属性都设置一下.也就是 ImageView,CollapsingToolbarLayout,AppBarLayout,CoordinateLayout .

    • 设置 fitsSystemWindows 属性
    • 设置状态栏透明.
      • res 下新建 values-v21 文件夹.
      • res/value-v21 下创建styles.xml 文件.
      <?xml version="1.0" encoding="utf-8"?>
      <resources>
          <!-- PandaActivity 主题 -->
          <style name="PandaActivityTheme" parent="AppTheme">
              <!-- 设置透明色,Android 5.0 之后加载这个 -->
              <item name="android:statusBarColor">@android:color/transparent</item>
          </style>
      </resources>
      
      • res/values/styles.xml 文件夹中增加如下代码
      <!-- PandaActivity 主题 -->
      <style name="PandaActivityTheme" parent="AppTheme">
          <!-- Android 5.0 之前的系统会加载这. -->
      </style>
      
      • AndroidManifest.xml 中修改PandaActivity配置
      <!-- 设置我们刚才定义的属性主题 -->
      <activity android:name=".PandaActivity"
          android:theme="@style/PandaActivityTheme">
      </activity>
      

    大功告成

    参考

    • 第一行代码(第2版)

      这本书真的很好.感谢郭霖前辈

    相关文章

      网友评论

        本文标题:Material Design 基础

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