Android Design Support Library

Google在2015的IO大会上,给我们带来了更加详细的Material Design设计规范,同时,也给我们带来了全新的Android Design Support Library,在这个support库里面,Google给我们提供了更加规范的MD设计风格的控件。最重要的是,Android Design Support Library的兼容性更广,直接可以向下兼容到Android 2.2。

使用Support Library非常简单,添加引用即可:

compile 'com.android.support:design:23.2.0' //可修改版本号为自己匹配

Design Support Library包含8个控件,具体如下:

下面分别详细介绍。
介绍这几个控件之前,我们先来看看Material Design主题的风格。

MD的Theme

md的主题有:

  • @android:style/Theme.Material (dark version)
  • @android:style/Theme.Material.Light (light version)
  • @android:style/Theme.Material.Light.DarkActionBar

与之对应的Compat Theme:

  • Theme.AppCompat
  • Theme.AppCompat.Light
  • Theme.AppCompat.Light.DarkActionBar

我们可以根据我们的app的风格,去定制Color Palette(调色板),重点有以下几个属性:

<resources>
    <!-- Base application theme. -->
    <style name="AppBaseTheme" parent="Theme.AppCompat"> <!-- customize the color palette --> <item name="colorPrimary">@color/material_blue_500</item> <item name="colorPrimaryDark">@color/material_blue_700</item> <item name="colorAccent">@color/material_green_A200</item> </style>
</resources>

colorPrimary 对应ActionBar的颜色。
colorPrimaryDark对应状态栏的颜色
colorAccent 对应EditText编辑时、RadioButton选中、CheckBox等选中时的颜色。
Android Design Support Library_第1张图片

注:对于5.0以下的设备,目前colorPrimaryDark无法去个性化状态栏的颜色;底部的navagationBar可能也不一样,更别说设置颜色了。

Snackbar

Snackbar提供了一个介于Toast和AlertDialog之间轻量级控件,它可以很方便的提供消息的提示和动作反馈。Snackbar和Toast比较相似,但是用途更加广泛,并且它是可以和用户进行交互的。Snackbar使用一个动画效果从屏幕的底部弹出来,过一段时间后也会自动消失。

Snackbar.make(snackBar, "Snackbar comes out", Snackbar.LENGTH_LONG)
        .setAction("Action", new View.OnClickListener() {
                  @Override
                  public void onClick(View v) {
                  Toast.makeText( MainActivity.this,"Toast comes out",Toast.LENGTH_SHORT).show();
                  }
        }).show();

这里调用Snackbar的make()方法来创建一个Snackbar对象,make()方法的第一个参数是Snackbar显示的基准元素,需要传入一个view,只要是当前界面布局的任意一个view都可以,Snackbar会使用这个view来自动查找最外层的布局,用于展示Snackbar。第二个参数就是Snackbar中显示的内容,第三个参数是Snackbar显示的时长。这些和Toast都是类似的。
接着这里又调用了一个setAction()方法来设置一个动作,从而让Snackbar不仅仅是一个提示,而是可以和用户进行交互的,这里Action可以设置多个。最后调用show()方法让Snackbar显示出来。

现在重新运行一下程序,效果如下图所示:
Android Design Support Library_第2张图片

Google API Doc 官方说明:

TextInputLayout

TextInputLayout功能非常简单,就是用于用户在EditText中输入时hint的提示和错误的提示。
先来看看效果图吧:
Android Design Support Library_第3张图片
从上图很明显的看出:
1、当EditText获得焦点时候,TextInputLayout会在左上角默认生成一个Label用来显示EditText中hint的内容,所以当用户输入时候hint内容会浮动到左上角,这极大便利了用户输入提交数据的体验。
2、当EditText中输入的内容不合法时,TextInputLayout便会在EditText的左下角用红色显示错误的提示信息。
下面来看看怎么用代码实现上面功能吧:
先看看布局文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin">

    <Button  android:id="@+id/snackBar" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="SnackBar" android:layout_margin="10dp"/>

    <android.support.design.widget.TextInputLayout  android:id="@+id/til_name" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="10dp">

        <EditText  android:layout_width="match_parent" android:layout_height="wrap_content" />
    </android.support.design.widget.TextInputLayout>

    <android.support.design.widget.TextInputLayout  android:id="@+id/til_password" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="10dp">

        <EditText  android:layout_width="match_parent" android:layout_height="wrap_content" />
    </android.support.design.widget.TextInputLayout>

</LinearLayout>

在Java代码中实现:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        til_name = (TextInputLayout) findViewById(R.id.til_name);
        til_password = (TextInputLayout) findViewById(R.id.til_password);
        //设置Hint信息
        til_name.setHint("Name");
        til_password.setHint("Password");
        //设置输入监听
        til_name.getEditText().addTextChangedListener(new MyTextWatcher(til_name, "用户名长度不能小于6位"));
        til_password.getEditText().addTextChangedListener(new MyTextWatcher(til_password, "密码长度不能小于6位"));
    }

    //自定义监听器
    class MyTextWatcher implements TextWatcher {
        private TextInputLayout mTextInputLayout;
        private String errorInfo;

        public MyTextWatcher(TextInputLayout textInputLayout, String errorInfo) {
            this.mTextInputLayout = textInputLayout;
            this.errorInfo = errorInfo;
        }

        @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) {
            if (mTextInputLayout.getEditText().getText().toString().length() < 6) {
                mTextInputLayout.setErrorEnabled(true);//打开错误提示
                mTextInputLayout.setError(errorInfo);//设置错误提示信息
            } else {
                mTextInputLayout.setErrorEnabled(false);//关闭错误提示
            }
        }
    }

其中,需要注意以下几点:
1、TextInputLayout布局中只能包含一个EditText子View,不能包含多个EditText。
2、TextInputLayout中有个方法getEditText();该方法返回的是它的子EditText对象,所以我们可通过mTextInputLayout.getEditText();来得到EditText对象,不需要findViewById找了。
3、设置错误提示信息时一定要先setErrorEnabled(true);再设置setError(…);因为TextView只在setErrorEnabled(true)方法中创建,必须创建好TextView才能往TextView上设置信息。而不需要设置时直接setErrorEnabled(false);即可,因为它自身会remove移除TextView。
4、TextInputLayout的颜色来自style中的colorAccent的颜色。

<item name="colorAccent">#FF4081</item>

Google API Doc 官方说明:
Android Design Support Library_第4张图片

Floating Action Button

FloatingActionButton 是一个负责显示界面基本操作的圆形悬浮按钮。FAB继承自ImageView,你可以使用android:src或者ImageView的任意方法,比如setImageDrawable()来设置FloatingActionButton里面的图标。

    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="10dp"
        android:src="@drawable/good"/>

其他设置:
注意:使用以下属性时,需要在Layout中添加命名空间:xmlns:app=”http://schemas.android.com/apk/res-auto”
(1)悬浮操作按钮支持两种size(normal和mini),默认是normal,可以通过app:fabSize指定。
(2)FAB背景颜色默认取的是style中的colorAccent,可以通过app:backgroundTint来指定。
(3)FAB点击时颜色效果默认取的是theme中的colorControlHighlight,可以通过app:rippleColor来指定。
(4)和立体感相关有两个属性,elevation和pressedTranslationZ,前者用户设置正常显示的阴影大小;后者是点击时显示的阴影大小。

    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/good"
        app:fabSize="mini"
        app:backgroundTint="#ff0000ff"
        app:rippleColor="#ff00ff00"
        app:elevation="6dp"
        app:pressedTranslationZ="12dp"/>

Android Design Support Library_第5张图片

TabLayout

通过选项卡的方式切换View并不是MD中才有的新概念,但是Google却是第一次在support库中提供了完整的支持,而且,Design library的TabLayout 既实现了固定的选项卡 (View的宽度平均分配),也实现了可滚动的选项卡(View宽度不固定同时可以横向滚动)。选项卡可以在程序中动态添加:

    <android.support.design.widget.TabLayout
        android:id="@+id/tabs"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:tabBackground="@color/colorPrimary"
        app:tabTextColor="#ffffffff"
        app:tabSelectedTextColor="#ffff0000"
        app:tabIndicatorColor="#ffffff00"
        app:tabIndicatorHeight="5dp"
        app:tabMode="fixed"/>
TabLayout tabLayout = (TabLayout) findViewById(R.id.tabs);
tabLayout.addTab(tabLayout.newTab().setText("tab1"));
tabLayout.addTab(tabLayout.newTab().setText("tab2"));
tabLayout.addTab(tabLayout.newTab().setText("tab3"));

这里写图片描述

但实际开发中我们很少这样用,通常滑动布局都会和ViewPager配合起来使用,所以,我们需要ViewPager来帮忙:
布局文件:

    <android.support.design.widget.TabLayout
        android:id="@+id/tabs"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:tabBackground="@color/colorPrimary"
        app:tabTextColor="#ffffffff"
        app:tabSelectedTextColor="#ffff0000"
        app:tabIndicatorColor="#ffffff00"
        app:tabIndicatorHeight="5dp"
        app:tabMode="fixed"/>

    <android.support.v4.view.ViewPager
        android:id="@+id/viewPager"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

Java中设置ViewPager数据,并将ViewPager和TabLayout关联起来。

   private void setupViewPager() {
        mInflater = LayoutInflater.from(this);
        view1 = mInflater.inflate(R.layout.tab_main, null);
        view2 = mInflater.inflate(R.layout.tab_main, null);
        view3 = mInflater.inflate(R.layout.tab_main, null);
        view4 = mInflater.inflate(R.layout.tab_main, null);
        view5 = mInflater.inflate(R.layout.tab_main, null);
        //添加页卡视图
        mViewList.add(view1);
        mViewList.add(view2);
        mViewList.add(view3);
        mViewList.add(view4);
        mViewList.add(view5);
        //添加页卡标题
        mTitleList.add("No:1");
        mTitleList.add("No:2");
        mTitleList.add("No:3");
        mTitleList.add("No:4");
        mTitleList.add("No:5");
        mTabLayout.setTabMode(TabLayout.MODE_FIXED);//设置tab模式,当前为系统默认模式
        mTabLayout.addTab(mTabLayout.newTab().setText(mTitleList.get(0)));
        mTabLayout.addTab(mTabLayout.newTab().setText(mTitleList.get(1)));
        mTabLayout.addTab(mTabLayout.newTab().setText(mTitleList.get(2)));
        mTabLayout.addTab(mTabLayout.newTab().setText(mTitleList.get(3)));
        mTabLayout.addTab(mTabLayout.newTab().setText(mTitleList.get(4)));

        MyPagerAdapter mAdapter = new MyPagerAdapter(mViewList, mTitleList);
        mViewPager.setAdapter(mAdapter);//给ViewPager设置适配器
        mTabLayout.setupWithViewPager(mViewPager);//将TabLayout和ViewPager关联起来。
        mTabLayout.setTabsFromPagerAdapter(mAdapter);//给Tabs设置适配器
    }

ViewPager的Adapter适配器:

class MyPagerAdapter extends PagerAdapter {
    private List<View> mViewList;
    private List<String> mTitleList;

    public MyPagerAdapter(List<View> mViewList, List<String> mTitleList) {
        this.mViewList = mViewList;
        this.mTitleList = mTitleList;
    }

    @Override
    public int getCount() {
        return mViewList.size();//页卡数
    }

    @Override
    public boolean isViewFromObject(View view, Object object) {
        return view == object;//官方推荐写法
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        container.addView(mViewList.get(position));//添加页卡
        return mViewList.get(position);
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        container.removeView(mViewList.get(position));//删除页卡
    }

    @Override
    public CharSequence getPageTitle(int position) {
        return mTitleList.get(position);//页卡标题
    }

}

Android Design Support Library_第6张图片

NavigationView在MD设计中非常重要,之前Google也提出了使用DrawerLayout来实现导航抽屉。这次,在support library中,Google提供了NavigationView来实现导航菜单界面,所以,新的导航界面可以这样写了:

<android.support.v4.widget.DrawerLayout  android:id="@+id/dl_main_drawer" 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:fitsSystemWindows="true">

    <!-- 你的内容布局-->
    <include layout="@layout/content"/>

    <android.support.design.widget.NavigationView  android:id="@+id/nv_main_navigation" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_gravity="start" app:headerLayout="@layout/navigation_header" app:menu="@menu/drawer_view"/>

</android.support.v4.widget.DrawerLayout>

可以看到我们的最外层是DrawerLayout,里面一个content,一个作为drawer。我们的drawer为NavigationView。
其中NavigationView 中的 android:layout_gravity=”start” 属性来控制抽屉菜单从哪边滑出,一般 “start ”从左边滑出,“end”从右边滑出。
其中最重要的就是下面两个属性:
app:headerLayout
app:menu
通过这两个属性,我们可以非常方便的指定导航界面的头布局和菜单布局。

来看看headerLayout的布局文件(layout目录)和menu配置文件(menu目录):

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="200dp" android:background="#ff0000ff" android:gravity="center" android:orientation="vertical" android:padding="16dp" android:theme="@style/ThemeOverlay.AppCompat.Dark">

    <ImageView  android:layout_width="100dp" android:layout_height="100dp" android:layout_marginTop="16dp" android:background="@drawable/head"/>

    <TextView  android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="16dp" android:gravity="center" android:text="Watson" android:textSize="20sp"/>
</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">

    <group android:checkableBehavior="single">
        <item  android:id="@+id/nav_home" android:icon="@drawable/setting" android:title="Home"/>
        <item  android:id="@+id/nav_messages" android:icon="@drawable/setting" android:title="Messages"/>
        <item  android:id="@+id/nav_friends" android:icon="@drawable/setting" android:title="Friends"/>
        <item  android:id="@+id/nav_discussion" android:icon="@drawable/setting" android:title="Discussion"/>
    </group>

    <item android:title="Sub items">
        <menu>
            <item  android:icon="@drawable/star" android:title="Sub item 1"/>
            <item  android:icon="@drawable/star" android:title="Sub item 2"/>
        </menu>
    </item>

</menu>

默认的颜色很多是从当前的主题中提取的,比如icon的stateColor,当然你也可以通过以下方法或属性修改部分样式:

  • setItemBackgroundResource(int):给menu设置背景资源,对应的属性app:itemBackground
  • setItemIconTintList(ColorStateList):给menu的icon设置颜色,对应的属性app:itemIconTint
  • setItemTextColor(ColorStateList):给menu的item设置字体颜色,对应的属性app:itemTextColor

你可以通过设置一个OnNavigationItemSelectedListener,使用其setNavigationItemSelectedListener()来获得元素被选中的回调事件。它为你提供被点击的 菜单元素 ,让你可以处理选择事件,改变复选框状态,加载新内容,关闭导航菜单,以及其他任何你想做的操作。例如这样:

private void setupDrawerContent(NavigationView navigationView) {
        navigationView.setNavigationItemSelectedListener(
                new NavigationView.OnNavigationItemSelectedListener() {
                    @Override
                    public boolean onNavigationItemSelected(MenuItem menuItem) {
                        menuItem.setChecked(true);
                        mDrawerLayout.closeDrawers();
                        return true;
                    }
                });
    }

同时,为了不遮住导航抽屉,我们选择隐藏ActionBar:
隐藏可以通过修改我们继承的主题为:Theme.AppCompat.Light.NoActionBar,当然也可以通过设置以下属性完成:

<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>

我们这里选择前者:

<style name="AppBaseTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <!-- customize the color palette -->
        <item name="colorPrimary">#673AB7</item>
        <item name="colorPrimaryDark">#512DA8</item>
        <item name="colorAccent">#FF4081</item>
</style>
<application
     ...
     android:theme="@style/AppTheme">
     ...
</application>

来看看运行效果

效果不错,不过存在一个问题,此时如果你点击Sub items里面的Sub item,发现它一直是选中状态,点击多个Sub item后会出现多个同时选中的情况。那怎么办呢?只能我们来手动切换了:

navigationView.setNavigationItemSelectedListener(
                new NavigationView.OnNavigationItemSelectedListener() {
                    private MenuItem mPreMenuItem;
                    @Override
                    public boolean onNavigationItemSelected(MenuItem menuItem) {
                        if (mPreMenuItem != null) mPreMenuItem.setChecked(false);
                        menuItem.setChecked(true);
                        //关闭抽屉侧滑菜单
                        mDrawerLayout.closeDrawers();
                        mPreMenuItem = menuItem;
                        return true;
                    }
                });

这里我们的导航菜单是通过手势滑动打开的,其实我们也可以通过函数实现:

/**打开抽屉侧滑菜单*/
mDrawerLayout.openDrawer(GravityCompat.START);

Toolbar

众所周知,在使用ActionBar的时候,一堆的问题:位置固定在顶部不能改变,文字不能定制,图标的间距不能控制等等,由此暴露出了ActionBar的设计不够灵活。为此官方提供了ToolBar,并且提供了supprot library用于向下兼容。Toolbar之所以灵活,是因为它其实就是一个ViewGroup,我们在使用的时候和普通的组件一样,在布局文件中声明。下面来看看Toolbar的使用。
首先隐藏原本的ActionBar,实现方式见上面NavigationView一节。
接下来就是将Toolbar放入到布局文件:

<android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:background="?attr/colorPrimary"
        android:layout_height="?android:attr/actionBarSize"/>

最后可以将Toobar作为“ActionBar”来用,对Toolbar设置Nav Icon、Logo、Title 、Sub Title、Menu Items。
关于字体的样式,可以在布局文件设置属性app:titleTextAppearance、app:subtitleTextAppearance或者代码setTitleTextAppearance、setSubTitleTextAppearance设置。
至于Menu Item,依然支持在menu/menu_main.xml中去声明,然后复写onCreateOptionsMenu和onOptionsItemSelected即可。
也可以通过toolbar.setOnMenuItemClickListener实现点击MenuItem的回调。

toolbar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() {
            @Override
            public boolean onMenuItemClick(MenuItem item) {
                if(item.getItemId() == ...) {
                /**do something*/
                return true;
                }
                return false;
            }
        });

Java代码实现:

Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
toolbar.setTitle("这里是Title");
toolbar.setSubtitle("这里是子标题");
toolbar.setLogo(R.drawable.head); 
toolbar.setNavigationIcon(R.drawable.ic_menu);
setSupportActionBar(toolbar);

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        /**监听Nav Icon的点击事件*/
        if(item.getItemId() == android.R.id.home) {
            Toast.makeText(MainActivity.this,"NavigationIcon clicked",Toast.LENGTH_SHORT).show();
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

AppBarLayout

AppBarLayout 是继承LinerLayout实现的一个ViewGroup容器组件,它是为了Material Design设计的App Bar,支持手势滑动操作。默认的AppBarLayout是垂直方向的,它的作用是把AppBarLayout包裹的内容都作为AppBar。
下面演示将Toolbar 和TabLayout的组合部分共同构成 AppBar的效果。

        <android.support.design.widget.AppBarLayout
            android:id="@+id/appbar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">

            <android.support.v7.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?android:attr/actionBarSize"
                android:background="?attr/colorPrimary" />

            <android.support.design.widget.TabLayout
                android:id="@+id/tabs"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                app:tabBackground="?attr/colorPrimary"
                app:tabIndicatorColor="#ffffff00"
                app:tabIndicatorHeight="5dp"
                app:tabMode="fixed"
                app:tabSelectedTextColor="#ffff0000"
                app:tabTextColor="#ffffffff" />

        </android.support.design.widget.AppBarLayout>

注: AppBarLayout必须作为Toolbar的父布局容器
AppBarLayout是支持手势滑动效果的,不过要和CoordinatorLayout配合使用,接下来学习一下CoordinatorLayout组件怎么使用。

CoordinatorLayout

CoordinatorLayout是这次新添加的一个增强型的FrameLayout。在CoordinatorLayout中,我们可以在FrameLayout的基础上完成很多新的操作。它的作用主要是协调子view之间触摸事件的布局。
首先看一个例子,当你将FloatingActionButton作为一个子View添加进CoordinatorLayout并且将CoordinatorLayout传递给 Snackbar.make(),在3.0及其以上的设备上,Snackbar不会显示在悬浮按钮的上面,而是FloatingActionButton利用CoordinatorLayout提供的回调方法,在Snackbar以动画效果进入的时候自动向上移动让出位置,并且在Snackbar动画地消失的时候回到原来的位置,不需要额外的代码。

<android.support.design.widget.CoordinatorLayout
            android:id="@+id/coordinatorLayout"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

              <android.support.design.widget.FloatingActionButton
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:src="@drawable/good"
                    android:layout_gravity="center_horizontal"
                    app:backgroundTint="#ff0000ff"
                    app:elevation="6dp"
                    app:fabSize="mini"
                    app:pressedTranslationZ="12dp"
                    app:rippleColor="#ff00ff00" />

        </android.support.design.widget.CoordinatorLayout>
coordinatorLayout = (CoordinatorLayout) findViewById(R.id.coordinatorLayout);
...

@Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.snackBar:
                Snackbar.make(coordinatorLayout, "Snackbar comes out", Snackbar.LENGTH_LONG)
                        .setAction("Action", new View.OnClickListener() {
                            @Override
                            public void onClick(View v) {
                                Toast.makeText( MainActivity.this,"Toast comes out",Toast.LENGTH_SHORT).show();
                            }
                        }).show();
                break;
            default:
                break;
        }
    }

Android Design Support Library_第7张图片

再来看看官方给出的例子:

<android.support.design.widget.CoordinatorLayout
            android:layout_width="match_parent"
            android:layout_height="200dp">

            <android.support.v7.widget.RecyclerView
                android:id="@+id/recyclerView"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                app:layout_behavior="@string/appbar_scrolling_view_behavior" />

            <android.support.design.widget.AppBarLayout
                android:id="@+id/appbar"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">

                <android.support.v7.widget.Toolbar
                    android:id="@+id/toolbar"
                    android:layout_width="match_parent"
                    android:layout_height="?android:attr/actionBarSize"
                    android:background="?attr/colorPrimary"
                    app:layout_scrollFlags="scroll|enterAlways"/>

                <android.support.design.widget.TabLayout
                    android:id="@+id/tabs"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    app:tabBackground="?attr/colorPrimary"
                    app:tabIndicatorColor="#ffffff00"
                    app:tabIndicatorHeight="5dp"
                    app:tabMode="fixed"
                    app:tabSelectedTextColor="#ffff0000"
                    app:tabTextColor="#ffffffff"/>

            </android.support.design.widget.AppBarLayout>
        </android.support.design.widget.CoordinatorLayout>

从上面布局看到,CoordinatorLayout协调布局中包裹了两个布局,一个是RecyclerView,一个是AppBarLayout,并且:
1、给这个可滚动组件设置了layout_behavior
2、给另一个控件设置了layout_scrollFlags
为滚动的布局设置layout_behavior属性后,这个布局会自动移动到AppBarLayout以下(类似MarginTop)。而且,当设置了layout_behavior的控件滑动时,就会触发设置了layout_scrollFlags的控件发生状态的改变。
Android Design Support Library_第8张图片

layout_behavior:coordinatorlayout可以协调子view之间的触动事件,每一个子view需要有各自的滑动处理方式。该属性就是用来指定处理自己滑动事件的Behavior对象。因此,该属性的值为自定义Behavior子类的全名(即包名+类名)。本例如下:

app:layout_behavior="@string/appbar_scrolling_view_behavior"
...
<string name="appbar_scrolling_view_behavior" translatable="false">android.support.design.widget.AppBarLayout$ScrollingViewBehavior</string>

layout_scrollFlags有如下几种选项:

  • scroll: 所有想滚动出屏幕的view都需要设置这个flag, 没有设置这个flag的view将被固定在屏幕顶部。
  • enterAlways: 这个flag让任意向下的滚动都会导致该view变为可见,启用快速“返回模式”。
  • enterAlwaysCollapsed:当你的视图已经设置minHeight属性又使用此标志时,你的视图只能以最小高度进入,只有当滚动视图到达顶部时才扩大到完整高度。
  • exitUntilCollapsed: 向上滚动时收缩View,但可以固定Toolbar一直在上面。

需要注意的是,后面两种模式基本只有在CollapsingToolbarLayout才有用,而前面两种模式基本是需要一起使用的,也就是说,这些flag的使用场景,基本已经固定了。
例如我们前面例子中的,也就是这种模式:

app:layout_scrollFlags="scroll|enterAlways"

我们上面的布局中 给Toolbar设置了app:layout_scrollFlags属性,因此,Toolbar是可以滚动出屏幕,且向下滚动又可以出现。
为了使得Toolbar可以滑动,我们必须还得有个条件,就是CoordinatorLayout布局下包裹一个具有滑动效果的组件,比如 RecyclerView,NestedScrollView(经过测试,ListView,ScrollView不支持)。并且给这些组件设置layout_behavior属性来告诉CoordinatorLayout,该组件是带有滑动行为的组件,然后CoordinatorLayout在接受到滑动时会通知AppBarLayout 中可滑动的Toolbar可以滑出屏幕了。

注:所有使用scrollFlags的view都必须定义在没有使用scrollFlags的view的前面,这样才能确保所有的view从顶部退出,留下固定的元素。

CollapsingToolbarLayout

CollapsingToolbarLayout作用是提供了一个可以折叠的Toolbar,它继承自FrameLayout,给它设置layout_scrollFlags,它可以控制包含在CollapsingToolbarLayout中的控件(如:ImageView、Toolbar)在响应layout_behavior事件时作出相应的scrollFlags滚动事件(移除屏幕或固定在屏幕顶端)。
使用CollapsingToolbarLayout:

<android.support.design.widget.CoordinatorLayout 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.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="256dp"
        android:fitsSystemWindows="true">

        <android.support.design.widget.CollapsingToolbarLayout
            android:id="@+id/collapsing_toolbar_layout"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:contentScrim="#30469b"
            app:expandedTitleMarginStart="48dp"
            app:layout_scrollFlags="scroll|exitUntilCollapsed">

            <ImageView
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:scaleType="centerCrop"
                android:src="@drawable/ic_banner"
                app:layout_collapseMode="parallax"
                app:layout_collapseParallaxMultiplier="0.7" />

            <android.support.v7.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                app:layout_collapseMode="pin" />
        </android.support.design.widget.CollapsingToolbarLayout>
    </android.support.design.widget.AppBarLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

        <android.support.v7.widget.RecyclerView
            android:id="@+id/recyclerView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:scrollbars="none" />
    </LinearLayout>
</android.support.design.widget.CoordinatorLayout>

我们在CollapsingToolbarLayout中设置了一个ImageView和一个Toolbar。并把这个CollapsingToolbarLayout放到AppBarLayout中作为一个整体。
(1)在CollapsingToolbarLayout中,设置了app:layout_scrollFlags=”scroll|exitUntilCollapsed”。关于layout_scrollFlags的值意义见上面。
其中还设置了一些属性,简要说明一下:
contentScrim – 设置当完全CollapsingToolbarLayout折叠(收缩)后的背景颜色。
expandedTitleMarginStart – 设置扩张时候(还没有收缩时)title向左填充的距离。
(2)在ImageView控件中,我们设置了:
layout_collapseMode (折叠模式) – 有两个值:

  • pin - 设置为这个模式时,当CollapsingToolbarLayout完全收缩后,Toolbar还可以保留在屏幕上。
  • parallax - 设置为这个模式时,在内容滚动时,CollapsingToolbarLayout中的View(比如ImageView)也可以同时滚动,实现视差滚动效果,通常和layout_collapseParallaxMultiplier(设置视差因子)搭配使用。

layout_collapseParallaxMultiplier(视差因子) - 设置视差滚动因子,值为:0~1。
(3)在Toolbar控件中,我们设置了layout_collapseMode(折叠模式):为pin。
综上分析:当设置了layout_behavior的控件响应起了CollapsingToolbarLayout中的layout_scrollFlags事件时,ImageView会有视差效果的向上滚动移除屏幕,当开始折叠时CollapsingToolbarLayout的背景色(也就是Toolbar的背景色)就会变为我们设置好的背景色,Toolbar也一直会固定在最顶端。
效果如图:

Java代码:

        mToolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(mToolbar);
        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
        mToolbar.setNavigationOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                onBackPressed();
            }
        });
        /**使用CollapsingToolbarLayout必须把title设置到CollapsingToolbarLayout上,设置到Toolbar上则不会显示*/
        mCollapsingToolbarLayout = (CollapsingToolbarLayout) findViewById(R.id.collapsing_toolbar_layout);
        mCollapsingToolbarLayout.setTitle("CollapsingToolbarLayout");
        //通过CollapsingToolbarLayout修改字体颜色
        mCollapsingToolbarLayout.setExpandedTitleColor(Color.WHITE);//设置还没收缩时状态下字体颜色
        mCollapsingToolbarLayout.setCollapsedTitleTextColor(Color.GREEN);//设置收缩后Toolbar上字体的颜色

注:使用CollapsingToolbarLayout时必须把title设置到CollapsingToolbarLayout上,设置到Toolbar上不会显示。

Demo下载地址

你可能感兴趣的:(Android Design Support Library)