Material Design

关于Material Design 的各种详细的介绍,极客学院的wiki写的非常清楚了:
http://wiki.jikexueyuan.com/project/material-design/
那么我们现在就开始撸代码

Material Design_第1张图片
本文目录

1.控制项目全局样式

1.1 无论是使用AS还是ADT,都要引入appcompat-v7,AS使用下面这一句代码引入,解决Android碎片化开发的问题,碎片化开发,就是在不同版本上的UI,看起来不会有太大的区别。

implementation 'com.android.support:appcompat-v7:26.1.0'
implementation 'com.android.support:design:26.1.0'

1.2 写自己的全局样式:

 



  
    

AppTheme相当于AppBaseTheme的子类,在AppTheme中设置各种颜色,样式的配置。其中:
colorPrimary:主色,
colorPrimaryDark:主色--深色,一般可以用于状态栏颜色、底部导航栏
colorAccent:代表各个控件的基调颜色--CheckBox、RadioButton、ProgressBar等
android:textColor:当前所有的文本颜色

2. v7包中有很多控件,都是为兼容而生的,在各个版本中运行的外观并没有太大的区别。

2.1 AlertDialog:
android.app包中的AlertDialog:在不同的版本上显示样式是不一样的,在4.1下显示是这样的:


Material Design_第2张图片
传统对话框

8.0下显示是这样的:


Material Design_第3张图片
传统对话框

而android.support.v7.app包下的AlertDialog,在4.1和8.0下显示基本相同。


Material Design_第4张图片
Android 8.0下V7对话框

Material Design_第5张图片
Android 4.1下 V7对话框

可以看到,基本上是一样的。

2.2 下拉刷新 SwipeRefreshLayout

 srl.setColorSchemeColors(Color.YELLOW,Color.RED,Color.GREEN);//设置进度条的颜色
        srl.setProgressBackgroundColorSchemeColor(Color.WHITE);//设置背景颜色
        srl.setDistanceToTriggerSync(100);//下拉多少距离开始刷新
        srl.setSize(SwipeRefreshLayout.DEFAULT);//设置进度条的大小
        srl.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
            @Override
            public void onRefresh() {
                //下拉完毕
                srl.setRefreshing(false);//消失
            }
        });

2.3 PopWindow,ListPopupWindow,PopupMenu
ListPopupWindow:

 final ListPopupWindow listPopupWindow = new ListPopupWindow(this);
        listPopupWindow.setAdapter(adapter);
        listPopupWindow.setHeight(LinearLayout.LayoutParams.WRAP_CONTENT);
        listPopupWindow.setWidth(LinearLayout.LayoutParams.MATCH_PARENT);
        listPopupWindow.setAnchorView(view);//设置锚点,弹出的位置相对于View
        listPopupWindow.show();

        listPopupWindow.getListView().setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView parent, View view, int position, long id) {
                Toast.makeText(getApplicationContext(),"点击了"+position,Toast.LENGTH_SHORT).show();

                listPopupWindow.dismiss();
            }
        });

PopupMenu:

android.support.v7.widget.PopupMenu popupMenu
                = new android.support.v7.widget.PopupMenu(this,view);
        popupMenu.getMenuInflater().inflate(R.menu.menu,popupMenu.getMenu());
        popupMenu.show();

2.4 LinearLayoutCompat :在该控件中添加属性:
app:divider="@drawable/abc_list_divider_mtrl_alpha"
app:showDividers="beginning|middle"
可以给LinearLayout中的控件都加上divider







其他的控件,如Button,TextView,EditText等,V7包中的都是这样的包名:
android.support.v7.widget.AppCompatButton android.support.v7.widget.AppCompatTextView等。

查看源码:分析LinearLayoutCompat是怎么给其中的每个控件添加分割线的?

1.在xml文件中app:设置分割线,那么就从自定义属性入手,发现源码中有这样一句:

setDividerDrawable(a.getDrawable(R.styleable.LinearLayoutCompat_divider));

其中

a.getDrawable(R.styleable.LinearLayoutCompat_divider)

这句代码的意思是获取用户输入的drawable,那么setDividerDrawable()就是设置分割线了。

3. RecyclerView

RecyclerView内容很多,现在暂时没有时间去整理,以后再整理
RecyclerView也是v7包中的控件,因为使用的很多,所以专门拿出来说,RecyclerView最低可兼容到3.0。引入方法:

   implementation 'com.android.support:recyclerview-v7:26.1.0

用法:Adapter 继承自 RecyclerView.Adapter
设置adapter和LayoutManager给RecyclerView。

简单封装
绘制间隔线:
1在getItemOffset()中绘制矩形 在该矩形中绘制间隔线
2 拿到系统的属性的方法

4.侧滑

在Material Design出来之前,一般都是使用SlidingMenu进行侧滑,MD出来之后,Google收录了很多民间项目,并改编,放入了support包中,供开发者使用,DrawerLayout就是其中之一。

4.1 DrawerLayout :

1.来自support-v4包中的weidget包
2.继承自ViewGroup,可以看成是一个帧布局
3.里面包含两块内容,一个是内容布局,一个是侧滑布局,侧滑布局是在内容布局的上面显示,因此是写在内容布局的下面,并且需要设置android:layout_gravity="start" 如果设置成end 则是右侧滑动菜单

DrawerLayout和ToolBar结合使用:

1.在布局中添加ToolBar
2.在代码中用ToolBar替换ActionBar:setSupportActionBar(toolbar);
3.使用ActionBarDrawerToggle把DrawerLayout和ToolBar联系起来

        toolbar = findViewById(R.id.toolbar);
        //用toolbar替换actionBar
        setSupportActionBar(toolbar);
        drawerlayout = findViewById(R.id.drawerlayout);
        //最后两个参数是两个资源文件的名称,int类型 并无实际意义,只要填上即可
        ActionBarDrawerToggle drawerToggle = new ActionBarDrawerToggle(this,
        drawerlayout,toolbar,R.string.drawer_open,R.string.drawer_close);
        //状态同步
        drawerToggle.syncState();
       //给DrawerLayout设置监听
        drawerlayout.setDrawerListener(drawerToggle);

4 如果要自定义DrawerLayout打开关上的动画,只需要重写DrawerListener即可

       drawerlayout.addDrawerListener(new DrawerLayout.DrawerListener() {

            /**
             * Called when a drawer's position changes.
             * @param drawerView The child view that was moved
             * @param slideOffset The new offset of this drawer within its range, from 0-1
             */
            @Override
            public void onDrawerSlide(View drawerView, float slideOffset) {
                //drawerView :侧滑布局
                //slideOffset :滑动时的偏移量
            }
            /**
             * Called when a drawer has settled in a completely open state.
             * The drawer is interactive at this point.
             *
             * @param drawerView Drawer view that is now open
             */
            @Override
            public void onDrawerOpened(View drawerView) {
                //打开时调用

            }
            /**
             * Called when a drawer has settled in a completely closed state.
             *
             * @param drawerView Drawer view that is now closed
             */
            @Override
            public void onDrawerClosed(View drawerView) {
                //关闭时调用

            }
            /**
             * Called when the drawer motion state changes. The new state will
             * be one of {@link #STATE_IDLE}, {@link #STATE_DRAGGING} or {@link #STATE_SETTLING}.
             *
             * @param newState The new drawer motion state
             *
             *  STATE_IDLE:空闲 Indicates that any drawers are in an idle, 
             *                 settled state. No animation is in progress.
             *  STATE_DRAGGING:正在拖拽 Indicates that a drawer is currently 
             *                 being dragged by the user.
             *  STATE_SETTLING:拖拽完成 Indicates that a drawer is in 
             *                 the process of settling to a final position.
             *
             */
            @Override
            public void onDrawerStateChanged(int newState) {
                //拖拽状态改变时调用

            }
        });

        //方法内容为空,也可以点击ToolbarNavigation 打开关闭DrawerLayout
        drawerToggle.setToolbarNavigationClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
            }
        });


4.2 NavigationView

1.是对DrawerLayout更完善的封装
2.是谷歌在侧滑的MaterialDesign的一种规范,用来规范侧滑的基本样式。
NavigationView就是封装好的侧滑区域的布局,它有几个属性
app:headerLayout 和 app:menu 是头布局和头布局底下的菜单布局
在使用NavigationView时也需要将其android:layout_gravity 设置成start


插播:自定义Toast

       Toast result = new Toast(this);
        LayoutInflater inflate = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        View v = inflate.inflate(自定义Toast的layout, null);
        TextView tv = (TextView)v.findViewById(com.android.internal.R.id.message);
        tv.setText("我是自定义吐司");

        result.setView(v);
        result.setDuration(Toast.LENGTH_SHORT);

        result.show();

5.SnackBar

一种新的吐司,介于Toast和Dialog之间的产物:即做到提示用户,又不会打断用户操作,并且还能与用户交互。
使用:

      //无穷时间 Snackbar.LENGTH_INDEFINITE  view :为了找到父控件,获取上下文 
        Snackbar snackbar = Snackbar.make(view,"SnackBar",Snackbar.LENGTH_INDEFINITE);
        snackbar.setAction("确定", new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(DrawerlayoutActivity.this,"点击了",Toast.LENGTH_SHORT).show();
            }
        });
        snackbar.setActionTextColor(Color.WHITE);
        snackbar.addCallback(new Snackbar.Callback(){
            @Override
            public void onShown(Snackbar sb) {
                super.onShown(sb);
            }

            @Override
            public void onDismissed(Snackbar transientBottomBar, int event) {
                super.onDismissed(transientBottomBar, event);
            }
        });
        snackbar.show();

6.TextInputLayout

MD风格的带有提示的EditText
1.使用:

         

                

            

其中TextInputLayout属性:
app:errorEnabled="true" 是否开启错误提示
app:errorTextAppearance="@android:color/holo_red_dark" 自定义错误提示字体的颜色和大小等 app:hintTextAppearance="@android:color/darker_gray" 自定义hint字体的样式
app:counterTextAppearance="@android:color/holo_green_dark" 自定义计数字体的样式
app:counterOverflowTextAppearance="@android:color/holo_orange_dark"自定义字数超过最大值时 字体的样式
app:counterEnabled="true" 是否开启计数
app:counterMaxLength="20" 能输入的最大值
app:hintAnimationEnabled="true" hint移动到EditText上方时的动画是否开启
app:hintEnabled="false" 是否允许hint移动到EditText上方

  1. 自定义EditText错误监听
       text_input.getEditText().addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {

            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {

            }

            @Override
            public void afterTextChanged(Editable s) {
                String string = text_input.getEditText().getText().toString();
                if(!TextUtils.isEmpty(string)){
                    if(string.length() <= 6 ){
                        text_input.setErrorEnabled(false);
                    }else{
                        text_input.setErrorEnabled(true);
                        text_input.setError("长度不可大于6位");
                        
                    }
                }

            }
        });

7.Toolbar

顶部导航:显示标题、返回键、快捷操作、菜单等。Toolbar不一定要放在顶部,也可以放底部。
使用时要注意,把主题设置成NoActionBar.
在ToolBar上设置菜单

   //重写 oncreateOptionsMenu
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main,menu);
        return true;
    }

app:popupTheme 弹出菜单样式
菜单中设置:
orderInCategory:设置菜单项的排列顺序,必须设置大于等于0的整数值。数值小的排列在前,如果值相等,则按照xml中的顺序展现。
showAsAction
该属性有五个值,可以混合使用。
always
总是显示在Toolbar上。
ifRoom
如果Toolbar上还有空间,则显示,否则会隐藏在溢出列表中。
never
永远不会显示在Toolbar上,只会在溢出列表中出现。
withText
文字和图标一起显示。
collapseActionView
声明了这个操作视窗应该被折叠到一个按钮中,当用户选择这个按钮时,这个操作视窗展开。一般要配合ifRoom一起使用才会有效。

8 SearchView

1.在xml中使用,在menu中设置



    

    

在代码中对searchView进行操作

  //重写 oncreateOptionsMenu
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main,menu);
        
        //1.找到SearchView
        MenuItem item = menu.findItem(R.id.action_search);
        SearchView searchView = (SearchView) MenuItemCompat.getActionView(item);

        //2. 使用官方api设置searchView
        //设置一出来就直接呈现搜索框---SearchView
        searchView.setIconified(false);
        //进来就呈现搜索框并且不能被隐藏
        searchView.setIconifiedByDefault(false);
        //设置提交按钮是否可用(可见)
        searchView.setSubmitButtonEnabled(true);
        //监听焦点改变
        searchView.setOnQueryTextFocusChangeListener(new View.OnFocusChangeListener() {

            @Override
            public void onFocusChange(View v, boolean hasFocus) {
                // TODO Auto-generated method stub

            }
        });
        //searchView的关闭监听
        searchView.setOnCloseListener(new SearchView.OnCloseListener() {

            @Override
            public boolean onClose() {
                // TODO Auto-generated method stub
                return false;
            }
        });
        searchView.setOnSearchClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                Toast.makeText(DrawerlayoutActivity.this, "提交", 0).show();
            }
        });
        //监听文本变化,调用查询
        searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {

            @Override
            public boolean onQueryTextSubmit(String text) {
                //提交文本
                Toast.makeText(DrawerlayoutActivity.this, "提交文本:"+text, 0).show();
                return false;
            }

            @Override
            public boolean onQueryTextChange(String text) {
                // 文本改变的时候回调
                System.out.println("文本变化~~~~~"+text);

                return false;
            }
        });

        //3.自定义一些需要的样式  查看源码v7包中的abc_search_view.xml
        // 找到id 通过findViewById找到控件,进行自定义

        //自定义go_btn按钮
        ImageView icon = (ImageView) searchView.findViewById(R.id.search_go_btn);
        icon.setImageResource(R.drawable.ic_event);
        icon.setVisibility(View.VISIBLE);

        //自定义EditText
        SearchView.SearchAutoComplete et =  searchView.findViewById(R.id.search_src_text);
        et.setHint("输入商品名或首字母");
        et.setHintTextColor(Color.WHITE);

        
        return true;
    }

9.Palette 调色板

使用之前添加:

 implementation 'com.android.support:palette-v7:27.1.1'

分析已有图片的色彩特性:主色调、鲜艳的颜色、柔和颜色等等
用法:

        BitmapDrawable drawable = (BitmapDrawable) iv.getDrawable();
        Bitmap bitmap = drawable.getBitmap();
        //得到bitmap里面的的一些色彩信息---通过Palette类分析出来的
//异步任务---可能分析的图片会比较大或者颜色分布比较复杂,会耗时比较久,防止卡死主线程。
        Palette.from(bitmap).generate(new PaletteAsyncListener() {
            
            @Override
            public void onGenerated(Palette palette) {
                //暗、柔和的颜色
                int darkMutedColor = palette.getDarkMutedColor(Color.BLUE);//如果分析不出来,则返回默认颜色
                //亮、柔和
                int lightMutedColor = palette.getLightMutedColor(Color.BLUE);
                //暗、鲜艳
                int darkVibrantColor = palette.getDarkVibrantColor(Color.BLUE);
                //亮、鲜艳
                int lightVibrantColor = palette.getLightVibrantColor(Color.BLUE);
                //柔和
                int mutedColor = palette.getMutedColor(Color.BLUE);
                //鲜艳
                int vibrantColor = palette.getVibrantColor(Color.BLUE);
                //获取某种特性颜色的样品
//              Swatch lightVibrantSwatch = palette.getLightVibrantSwatch();
                Swatch lightVibrantSwatch = palette.getVibrantSwatch();
                //谷歌推荐的:图片的整体的颜色rgb的混合值---主色调
                int rgb = lightVibrantSwatch.getRgb();
                //谷歌推荐:图片中间的文字颜色
                int bodyTextColor = lightVibrantSwatch.getBodyTextColor();
                //谷歌推荐:作为标题的颜色(有一定的和图片的对比度的颜色值)
                int titleTextColor = lightVibrantSwatch.getTitleTextColor();
                //颜色向量
                float[] hsl = lightVibrantSwatch.getHsl();
                //分析该颜色在图片中所占的像素多少值
                int population = lightVibrantSwatch.getPopulation();
                
                //使用分析出的颜色
                tv_title.setBackgroundColor(getTranslucentColor(0.6f,rgb));
                tv_title.setTextColor(titleTextColor);
                
                tv1.setBackgroundColor(darkMutedColor);
                tv1.setText("darkMutedColor");
                tv2.setBackgroundColor(lightMutedColor);
                tv2.setText("lightMutedColor");
                tv3.setBackgroundColor(darkVibrantColor);
                tv3.setText("darkVibrantColor");
                tv4.setBackgroundColor(lightVibrantColor);
                tv4.setText("lightVibrantColor");
                tv5.setBackgroundColor(mutedColor);
                tv5.setText("mutedColor");
                tv6.setBackgroundColor(vibrantColor);
                tv6.setText("vibrantColor");
                
            }
        });
        
        
    }

    protected int getTranslucentColor(float percent, int rgb) {
        // 10101011110001111
        int blue = Color.blue(rgb);
        int green = Color.green(rgb);
        int red = Color.red(rgb);
        int alpha = Color.alpha(rgb);

//      int blue = rgb & 0xff;
//      int green = rgb>>8 & 0xff;
//      int red = rgb>>16 & 0xff;
//      int alpha = rgb>>>24;
        
        alpha = Math.round(alpha*percent);
        return Color.argb(alpha, red, green, blue);
    }

10.TabLayout

1.TabLayout和ViewPager一起用:
tablayout.setupWithViewPager(mViewPager);
tablayout.setTabMode(TabLayout.MODE_FIXED);
在xml文件中:
app:tabMode 与 app:tabGravity 一起使用 可以改变标签的位置
tabMode :fixed 适应屏幕填充,不能滑动 SCROLLABLE:标签可以滑动
tabGravity:fill tab平均填充整个宽度 center tab居中显示
2.自定义Tab

       for(int i = 0;i< tablayout.getTabCount();i++){
            TabLayout.Tab tab = tablayout.getTabAt(i);
            View view = View.inflate(this,layoutId,null);
            TextView tv_title =  view.findViewById(R.id.tv_title);
            tv_title.setText(titles[I]);
            tab.setCustomView(view);//自定义Tab
        }

11 沉浸式设计

官方的沉浸式: 就是让整个App充满了整个屏幕,没有显示状态栏,没有显示底部导航栏,而现在我们常说的沉浸式就是状态栏和ToolBar颜色一致。
沉浸式开发, Api必须 > 4.4 Api低于4.4的官方系统无法做到沉浸式,但有些品牌的系统做了处理,Api < 4.4也可以做到沉浸式 所以在做沉浸式开发时,需要判断手机品牌和系统。
做沉浸式兼容时,需要判断当前SDK版本,4.4 和5.0是两个分割线

Android 4.4 之前,Android 的状态栏是黑色背景,无法修改。
Android 4.4 推出了透明状态栏的效果。
Android 5.0 提供了方法可以直接修改状态栏的颜色。

沉浸式ToolBar

1.Android5.0之后,

系统已经实现了沉浸式设计,想要修改状态栏的颜色可以有几种办法:

1)

直接修改主题中的colorPrimaryDark ,但这种方式会使整个App的主题都是这个颜色

2)

修改@color/colorPrimary
这个属性需要写在style-21文件中

3)

代码中修改:
getWindow().setStatusBarColor(getResources().getColor(R.color.cardview_dark_background));

2.Android4.4 设置沉浸式状态栏

方法1:

在主题中设置:true

方法2:

1)设置状态栏透明
2)在toolbar中添加属性 android:fitsSystemWindows="true"
但是这种办法在布局中有scrollView时会有bug,布局中没有scrollView时可以使用。
如果非要使用的话,需要在最外层的布局中添加属性
android:fitsSystemWindows="true"
android:clipToPadding="true"
android:background="@color/colorPrimary"
把最外层布局的背景设置成和toolbar相同的颜色,再把状态栏和ToolBar之外的部分设置成背景色。

方法3:

1)设置状态栏透明
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
2)获取状态栏高度,把toolbar用一个布局包裹,布局的高度为toolbar.height+status.height 或者直接修改ToolBar的高度
给Toolbar设置 android:fitsSystemWindows="true"
第2步还可以设置ToolBar的paddingTop:
toolbar.setPadding(toolbar.getPaddingLeft(),toolbar.getPaddingTop()+getStatusBarHeight(this), toolbar.getPaddingRight(),toolbar.getPaddingBottom());

沉浸式NavigationBar 底部虚拟导航

1 Android5.0之后

1) 属性解决
在主题中设置
@color/colorPrimary
在style-21上设置
2)代码解决
在setContentView之前设置
getWindow().setNavigationBarColor(color);

2 Android 4.4 - Android5.0

方法1

1.虚拟导航栏设置为透明
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
2.反射拿到底部导航栏的高度:

     /**获取底部导航栏的高度*/
     private int getNavigationBarHeight(Context context) {
        return getSystemComponentDimen(this, "navigation_bar_height");
      }

    /**
     * 获取状态栏的高度
     * @param context
     * @return
     */
    private int getStatusBarHeight(Context context) {
        // 反射手机运行的类:android.R.dimen.status_bar_height.
        return getSystemComponentDimen(this, "status_bar_height");
    }
    
    public static int getSystemComponentDimen(Context context, String dimenName){
        // 反射手机运行的类:android.R.dimen.status_bar_height.
        int statusHeight = -1;
        try {
            Class clazz = Class.forName("com.android.internal.R$dimen");
            Object object = clazz.newInstance();
            String heightStr = clazz.getField(dimenName).get(object).toString();
            int height = Integer.parseInt(heightStr);
            //dp--->px
            statusHeight = context.getResources().getDimensionPixelSize(height);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return statusHeight;
    }

3.使用一个View放在底部,设置高度为1dp,如果能够获取到navigationBar的高度,就把高度设置给View view的颜色设置成想要设置成的颜色即可。

12.CardView

兼容性开发,准备两套布局,一套放在layout文件夹下,一套放在layout-v21文件夹下。5.0以上不需要设置contentPadding属性 而 4.x需要设置contentPadding属性
5.0以上
1.阴影一般为16dp
2.点击cardView有水波纹效果:android:foreground="?attr/selectableItemBackground"
android:clickable="true"
3.按下z轴位移效果
自定义动画 z轴平移动画



    
        
    

    
        
        
    





使用:android:stateListAnimator="@drawable/z"

13.FloatingActionButton 悬浮动作按钮

兼容性开发:fab的宽高是58dp
1.需要写两个layout/layout-v21
layout-v21:添加layout_margin="16dp"
layout: 添加layout_margin="0dp"
2.水波纹效果
5.0以上才有水波纹的效果:
app:rippleColor="#f00"//水波纹的颜色
android:clickable="true"//注意 要设置该属性才会有水波纹效果
3.按下Z轴移动动画
app:pressedTranslationZ="12dp"//z轴动画
其他:
app:srcCompat="@android:drawable/ic_dialog_email"//设置src
android:backgroundTint="@color/colorAccent"//设置FAB
背景色
app:fabSize="mini" fab的大小
4.锚:anchor
app:layout_anchor=”@id/appbar”
app:layout_anchorGravity=”end|bottom|right”
app:layout_anchor设置这个属性可以让FloatingActionButton以某一个控件为基准调整位置,我们这里设置这个控件就是appbar
app:layout_anchorGravity
设置基于appbar控件的位置,我们这里设置了end|bottom|right
这样两行代码就可以将FloatingActionButton设置在appbar的右下角,并且行为会和appbar的滚动行为协作。

传统方式滑动RecyclerView实现ToolBar和Fab的显示和隐藏

14. CoordinatorLayout

继承自ViewGroup。
通过协调并调度里面的子控件或者布局来实现触摸(一般是指滑动)产生一些相关的动画效果。
可以通过设置view的Behavior来实现触摸的动画调度。
点击Fab弹出SnackBar,SnackBar弹出后会遮挡住Fab,需要把最外层的布局更改为CoordinatorLayout即可。

使用CoordinatorLayout 和 beHavior来实现随RecyclerView的滑动ToolBar和fab显示和隐藏效果。
原理:fab需要监听RecyclerView的滑动,给fab设置behavior,在behavior中有几个回调方法,当RecyclerView滑动时,就会回调那几个方法,把滑动状态告诉fab,我们一般所写的behavior都是继承自CoordinatorLayout.Behavior,而CoordinatorLayout位于最外层,当触摸事件发生时,CoordinatorLayout负责事件分发和传递,CoordinatorLayout可以通过behavior把事件传递给fab,同时作用到fab。
简单的说,CoordinatorLayout的作用就是监听滑动控件的滑动通过Behavior反馈到其他子控件并执行一些动画。滑动控件是指:
RecyclerView/NestedScrollView/ViewPager 而ListView/ScrollView不可以。

1# fab的显示和隐藏。
xml中 编写xml时要注意:
1.最外层控件使用CoordinatorLayout
2.fab要设置app:layout_behavior=“com.kimliu.materialdesign.behavior.FabBehavior”
属性值为自定义behavior





    

    

    
    

代码:只需要写一个自定义behavior即可

public class FabBehavior extends FloatingActionButton.Behavior {

    public FabBehavior() {

    }

    public FabBehavior(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    private boolean visible = true;

    @Override
    public boolean onStartNestedScroll(@NonNull CoordinatorLayout coordinatorLayout,
                                       @NonNull FloatingActionButton child,
                                       @NonNull View directTargetChild,
                                       @NonNull View target, int axes, int type) {
        return
                super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, axes, type);
    }

    @Override
    public boolean onStartNestedScroll(@NonNull CoordinatorLayout coordinatorLayout,
                                       @NonNull FloatingActionButton child,
                                       @NonNull View directTargetChild,
                                       @NonNull View target, int axes) {
        return axes == ViewCompat.SCROLL_AXIS_VERTICAL || super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, axes);
    }

    @Override
    public void onNestedScroll(@NonNull CoordinatorLayout coordinatorLayout,
                               @NonNull FloatingActionButton child,
                               @NonNull View target, int dxConsumed,
                               int dyConsumed, int dxUnconsumed,
                               int dyUnconsumed, int type) {
        super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, type);
        // 当观察的view滑动的时候回调的
        //根据情况执行动画
        if(dyConsumed>0&&visible){
            //向上滑动 且可见 隐藏
            visible = false;//不可见
            onHide(child);
        }else if(dyConsumed<0){
            //向下滑动
            visible = true;
            onShow(child);
        }
    }


    public void onHide(FloatingActionButton fab) {
        // 隐藏动画--属性动画
//      toolbar.animate().translationY(-toolbar.getHeight()).setInterpolator(new AccelerateInterpolator(3));
        ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) fab.getLayoutParams();

        fab.animate().translationY(fab.getHeight()+layoutParams.bottomMargin).setInterpolator(new AccelerateInterpolator(3));
//        ViewCompat.animate(fab).scaleX(0f).scaleY(0f).start();
    }

    public void onShow(FloatingActionButton fab) {
        // 显示动画--属性动画
//      toolbar.animate().translationY(0).setInterpolator(new DecelerateInterpolator(3));

        ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) fab.getLayoutParams();
        fab.animate().translationY(0).setInterpolator(new DecelerateInterpolator(3));
//        ViewCompat.animate(fab).scaleX(1f).scaleY(1f).start();
    }
}

2 Toolbar的滑动和隐藏

只需要在xml中设置,关键点:
1.ToolBar需要包裹在AppBarLayout中
2.AppBarLayout中的控件都有一个属性layout_scrollFlags
这个属性有5个值,分别是:
scroll:所有想滚动出屏幕的view都需要设置这个flag, 没有设置这个flag的view将被固定在屏幕顶部。
enterAlways:这个flag让任意向下的滚动都会导致该view变为可见,启用快速“返回模式”。
enterAlwaysCollapsed:假设你定义了一个最小高度(minHeight)同时enterAlways也定义了,那么view将在到达这个最小高度的时候开始显示,并且从这个时候开始慢慢展开,当滚动到顶部的时候展开完。
exitUntilCollapsed:当你定义了一个minHeight,此布局将在滚动到达这个最小高度的时候折叠。
snap:当一个滚动事件结束,如果视图是部分可见的,那么它将被滚动到收缩或展开。例如,如果视图只有底部25%显示,它将折叠。相反,如果它的底部75%可见,那么它将完全展开。
3.RecyclerView 要设置一个behavior app:layout_behavior="@string/appbar_scrolling_view_behavior"



    

    

    

    
    
    


3.AppBarLayout和NestedScrollView结合使用

要在NestedScrollView上添加属性:
app:layout_behavior="@string/appbar_scrolling_view_behavior"



    

        

            

                
            

            

                
            

            

                
            

            

                
            

            

                
            

            

                
            

            

                
            
        
    

    

        
    

    


4.AppBarLayout和ViewPager结合使用

在ViewPager上设置属性:app:layout_behavior="@string/appbar_scrolling_view_behavior"



    

        
        
        
    

    


5.ToolBar的折叠与隐藏 CollapsingToolbarLayout的使用

CollapsingToolbarLayout需要包裹在AppBarLayout中使用。

注意:

1.既然是需要折叠toolbar的高度,那么CollapsingToolbarLayout的高度应设置为match_parent
2. AppBarLayout需要设置固定高度,要实现折叠效果,这个高度需要比toolbar的高度高
3.要实现折叠效果,需要给CollapsingToolbarLayout设置属性: app:layout_scrollFlags="scroll|enterAlways"

属性:

1.在CollapsingToolbarLayout中的子控件都会有这个属性:
app:layout_collapseMode="" 它有三个值:none/pin/parallax
none:没有任何效果,往上滑动的时候toolbar会首先被固定并推出去。
parallax:在滑动的时候这个View 会呈现 出 视觉特差效果
layout_collapseParallaxMultiplier:值为0-1之间 与parallax结合使用,
pin:当这个View到达 CollapsingToolbarLayout的底部的时候,这个View 将会被放置,即代替整个CollapsingToolbarLayout
2.contentScrim :当CollapsingToolbarLayout完全折叠后的背景颜色。
通常设置为:app:contentScrim=”?attr/colorPrimary”,这样当CollapsingToolbarLayout完全折叠后就会显示主题颜色。
3.expandedTitleMarginStart :布局张开的时候title与左边的距离
4.app:collapsedTitleGravity="center_horizonta"折叠后标题的gravity

  1. layout_scrollFlags: 设置滚动表现:
    1、 Scroll, 表示手指向上滑动的时候,CollapsingToolbarLayout也会向上滚出屏幕并且消失,这个属性必须要有。
    2、 exitUntilCollapsed: 表示这个layout会一直滚动离开屏幕范围,直到它收折成它的最小高度.
    (如果设置了该属性,toolbar最后会停留在最高处,如果没有设置,toolbar最终会滚出屏幕)
    3、enterAlways: 一旦手指向下滑动这个view就可见。
    4、enterAlwaysCollapsed: 这个flag定义的是已经消失之后何时再次显示。假设你定义了一个最小高度(minHeight)同时enterAlways也定义了, 那么view将在到达 这个最小高度的时候开始显示,并且从这个时候开始慢慢展开,当滚动到顶部的时候展开完。
    即当你的视图设置了minHeight属性的时候,那么视图只能以最小高度进入,只有当滚动视图到达顶部时才扩大到完整高度。




    


        


            

            
                
            
            
        
        
        
        
    


    

        

            
          
        
        
    
    

15.自定义Behavior

Behavior:抽象类,监听者,包裹在其中的所有子控件或者容器产生联动效果。
Behavior可以做到两种情况:
1.某个View需要监听另一个View的状态(比如:位置,大小,显示状态)
(需要重写layoutDependsOn/onDependentViewChanged)
demo: 当点击TextView时,另一个TextView跟着一起动
xml:





    


    
    


java:

public class CustomBehavior extends CoordinatorLayout.Behavior {
    
    public CustomBehavior(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    /**
     *用来决定需要监听那些控件或容器的状态
     * @param parent 父容器
     * @param child 子控件  监听别人的view 如果有多个,每个都要配置behavior 然后用id判断是谁
     * @param dependency 被观察者 被监听的view
     * @return 两种条件:1.知道监听的是哪个控件 2. 知道这个控件是什么状态改变 如果这两个条件都知道 return true
     *          如果返回true 会调 onDependentViewChanged()
     */
    @Override
    public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {

        return dependency instanceof TextView || super.layoutDependsOn(parent, child, dependency);
    }

    /**
     *当被监听的view发生改变时回调,可以在其中做一些联动动画
     * @param parent
     * @param child
     * @param dependency
     * @return
     */
    @Override
    public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
        
        int offset = dependency.getTop() - child.getTop();
        ViewCompat.offsetLeftAndRight(child,-offset);
        //child垂直方向平移offset
        ViewCompat.offsetTopAndBottom(child,offset);

        return super.onDependentViewChanged(parent, child, dependency);
    }
}

2.某个View需要监听CoordinatorLayout里面所有控件的滑动状态。
(需要重写方法:onStartNestedScroll / onNestedScroll / onNestedPreScroll )
能被CoordinatorLayout捕获到滑动状态的控件有: NestedScrollView / RecyclerView / ViewPager
跟1相似,只是重写的方法不同而已,就不再赘述。

你可能感兴趣的:(Material Design)