第一行代码(十二)

第十二章主要讲了 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 是根据项目中指定的主题来显示的。打开清单文件,找到 标签中有一个 android:theme 属性,并且指定了一个 AppTheme 主题。



    
    


  这个 DarkActionBar 就是一个深色的 ActionBar 主题,项目中自带的 ActionBar 就是因为指定了这个主题才出现的。但是我们现在要使用 ToolBar,所以要指定一个不带 ActionBar 的主题,通常有 Theme.AppCompat.NoActionBar 和 Theme.AppCompat.Light.NoActionBar 这两种主题,其中 Theme.AppCompat.NoActionBar 表示深色主题,他会将界面的主体颜色设置成深色,陪衬颜色设成淡色。而 Theme.AppCompat.Light.NoActionBar 表示淡色主题,它会将界面的主体颜色设置成淡色,陪衬颜色设成深色。
  观察一下 AppTheme 中的属性重写,这里重写了 colorPrimary、colorPrimaryDark 和 colorAccent 这3个属性的颜色。

第一行代码(十二)_第1张图片
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 了。




    
        
    


这里首先记得添加 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);
        
            
            
                

                
            
        

  接下来我们给 ToolBar 添加一个 action 按钮


第一行代码(十二)_第2张图片
image.png

第一行代码(十二)_第3张图片
image.png


    

    

    




    

    
    

    

    

    
    

注意: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库提供)是一个布局,在布局中允许放入两个直接子控件,第一个子控件是主屏幕中显示的内容,第二个控件是滑动菜单中显示的内容(侧边栏)。




    
    

        

        

    

    
    


第一行代码(十二)_第4张图片
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



    
    
        

        

        

        

        
    

  然后我们在 layout 文件夹中创建一个 xml 文件,命名为:nav_header.xml





    

    

    


  然后修改 Activity 的 layout 布局文件




    
    

        

        

    

    
    
        

    


        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;
            }
        });
第一行代码(十二)_第5张图片
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 来作为按钮的颜色。




    
    

        

        

        
        
        

    

    
    


        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();
            }
        });
第一行代码(十二)_第6张图片
Snackbar.png

  不管是点击 YES 还是过一段时间等待 Snackbar 自动消失,Snackbar都是自带动画效果的,但是你会发现一个问题,就是 Snackbar 将我们的 FloatingActionBar 给挡住了,这就需要借助 CoordinatorLayout 就行了。

六、CoordinatorLayout

  CoordinatorLayout 可以说是一个加强版的 FrameLayout,该布局也是由 Design Support 库提供的,在普通情况下和 FrameLayout 基本一致。
  事实上,CoordinatorLayout 可以监听其所有子控件的各种事件,然后自动帮我们做出最为合理的响应,举个例子:刚才弹出的 Snackbar 将悬浮按钮挡住了,如果我们让 CoordinatorLayout 监听到 Snackbar 的弹出事件,那么它会自动将内部的 FloatingActionButton 向上偏移,从而确保不会被 Snackbar 挡住。




    
    

        

        

        
        
        

    

    
    

    



第一行代码(十二)_第7张图片
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 控件




    
    

        

        
        
        

        

        

    

    
    

    


/**
 * 实体类
 */

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 的适配器
 */
public class FruitAdapter extends RecyclerView.Adapter{

    private List mFruitList;
    private Context mContext;

    public FruitAdapter(List 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 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]);
        }
    }
第一行代码(十二)_第8张图片
CardView.png

  CardView 的效果已经出来了,但是你自己观察一下,ToolBar 被 RecyclerView 挡住了,怎么办?这就要借助另一个工具 -- AppBarLayout了。

八、AppBarLayout

  分析下上述问题的原因,因为 ToolBar 和 RecyclerView 都放在 CoordinatorLayout 中,而 CoordinatorLayout 是一个加强版的 FrameLayout,那么肯定会遮挡了啊。那么如果我们在 CoordinatorLayout 内,给 ToolBar 和 RecyclerView 的包一层 LinearLayout 或者 RelativeLayout 是不是就可以了呢?是的,是可以了,但是,我们既然引出了 AppBarLayout,就有用 AppBarLayout 的理由。
  AppBarLayout 实际上是一个垂直方向上的 LinearLayout,而且它也是 Design Support 库中的空间,它在其内部做了非常多的滚动事件的封装。先看看如何使用:

    
    

        
        

            

            

        

        
        

        

        

    
第一行代码(十二)_第9张图片
AppBarLayout1.png

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

第一行代码(十二)_第10张图片
AppBarLayout2.png

  注意顶部,这个是 RecyclerView 滑动到顶部的样子,也就是说 RecyclerView 的上面部分被 ToolBar 遮盖了。
  当 AppBarLayout 接收到滚动事件的时候,它内部的子控件其实是可以指定如何去响应这些事件的,通过app:layout_scrollFlags属性就可以

    
    

        
        

            
            

            

        

        
        

        

        

    

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 即可。

    
    

        
        

            

            

        

        

            

            

        

        

    

注意:这里因为 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();
    }
第一行代码(十二)_第11张图片
swipeRefreshLayout.png

十、可折叠式标题栏

  如果我们希望根据自己的喜好随意定制标题栏的样式,比如实现一个可折叠式的标题栏的效果,就需要借助 CollapsingToolbarLayout 这个工具。
  CollapsingToolbarLayout 是一个作用于 Toolbar 基础之上的布局,它也是由 Design Support 库提供,CollapsingToolbarLayout 可以让 Toolbar 的效果变得更加丰富。

注意:CollapsingToolbarLayout 不能独立存在,它在设计的时候就被限定只能作为AppBarLayout 的直接子布局来使用,而AppBarLayout 又必须是 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);
    }
第一行代码(十二)_第12张图片
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,表示该控件会出现在系统状态栏里,修改布局文件:


第一行代码(十二)_第13张图片
image.png

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

第一行代码(十二)_第14张图片
image.png

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

设置成透明的方法很简单,在主题中将 android:statusBarColor 属性的值指定成 @android:color/transparent 就可以了,但是问题是,android:statusBarColor 这个属性是从 API 21(Android 5.0)开始才有的,之前的系统无法指定这个属性。

  为了解决上述问题,我们做如下操作


第一行代码(十二)_第15张图片
image.png

  然后在 values-v21 目录下创建一个styles.xml



    

由于 values-v21 目录是只有 Android 5.0 及其以上的系统才会去读取的,所以,这么写是没问题的。但是 Android 5.0 之前的系统却无法识别 FruitActivityTheme 这个主题,因此我们还需要修改 values/styles.xml 文件。

    
    

  最后别忘了在清单文件中给 Activity 添加 Theme 属性


第一行代码(十二)_第16张图片
image.png

最后,Material Design 的官方文章:https://material.google.com

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

你可能感兴趣的:(第一行代码(十二))