Android Design Support Library

1.Navigation View

对于应用程序,它代表着一个标准的导航菜单。菜单内容可以由菜单资源文件填充。

效果图

NavigationView通常放在一个DrawerLayout里面。

<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true">

    <!-- your content layout -->
    <android.support.v7.widget.RecyclerView
        android:id="@+id/recycler_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layoutManager="LinearLayoutManager" />

    <android.support.design.widget.NavigationView
        android:id="@+id/navigation_view"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        app:headerLayout="@layout/drawer_header"
        app:menu="@menu/drawer" />
</android.support.v4.widget.DrawerLayout>

这里有两个需要注意的属性:
app:headerLayout=”@layout/drawer_header”(可选的)控制着header的布局,app:menu=”@menu/drawer”是菜单资源用于填充导航的条目(可以在运行的时候更新)。

/res/layout/drawer_header.xml:

<?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="match_parent"
    android:background="?attr/colorPrimary"
    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">


    <ImageView
        android:id="@+id/drawer_header_portrait"
        android:layout_width="@dimen/size_90dp"
        android:layout_height="@dimen/size_90dp"
        android:layout_gravity="left|top"
        android:scaleType="centerInside"
        android:src="@drawable/me" />


    <TextView
        android:layout_width="@dimen/size_90dp"
        android:layout_height="wrap_content"
        android:gravity="center_horizontal"
        android:text="王诚宇"
        android:textColor="@android:color/white"
        android:textSize="@dimen/textSize_16sp" />


</LinearLayout>

最简单的抽屉菜单是一组可勾选的菜单条目的集合,/res/menu/drawer.xml:

<?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/navigation_item_1"
            android:checked="true"
            android:icon="@drawable/ic_home_2x"
            android:title="@string/navigation_item_1" />
        <item
            android:id="@+id/navigation_item_2"
            android:icon="@drawable/ic_message_2x"
            android:title="@string/navigation_item_2" />
    </group>
</menu>

被选中的条目将在导航抽屉中突出显示,以确保用户知道哪一个导航条目是当前选中的。

也可以在菜单中通过使用subheaders来分离菜单条目组:

<item
    android:id="@+id/navigation_subheader"
    android:title="@string/navigation_subheader">
    <menu>
        <item
            android:id="@+id/navigation_sub_item_1"
            android:icon="@drawable/ic_android"
            android:title="@string/navigation_sub_item_1"/>
        <item
            android:id="@+id/navigation_sub_item_2"
            android:icon="@drawable/ic_android"
            android:title="@string/navigation_sub_item_2"/>
    </menu>
</item>

要获得选中条目时的回调方法,可以通过NavigationView的
setNavigationItemSelectedListener()来设置。

        navigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
            @Override
            public boolean onNavigationItemSelected(MenuItem item) {
                if (item.getItemId() == R.id.navigation_item_1) {
                    //do something
                    return true;
                }
                return false;
            }
        });

onNavigationItemSelected()方法中提供了点击的MenuItem,可以处理选择事件,改变选择状态,加载新的内容,关闭抽屉,或者其它任何动作行为。

小技巧:

  • 通过NavigationView.setItemTextColor()设置条目文本颜色。
  • 通过NavigationView.addView(customeView)自定义NavigationView的布局。

监听打开和关闭事件


要监听drawer的打开和关闭事件,可以通过
DrawerLayout.setDrawerListener(new DrawerLayout.DrawerListene接口的实现类())来设置。DawerLayout.DrawerListener接口为drawer事件提供了回调方法,如 onDrawerOpened() 和 onDrawerClosed()方法。

然而,与其实现DawerLayout.DrawerListener接口,如果activity中含有action bar,可以继承ActionBarDrawerToggle类来代替。ActionBarDrawerToggle类实现了DawerLayout.DrawerListener接口,因此可以覆盖那些回调方法。

        mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
        mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout, getToolbar(), R.string.open, R.string.close) {
            /** Called when a drawer has settled in a completely closed state. */
            public void onDrawerClosed(View view) {
                super.onDrawerClosed(view);
                getSupportActionBar().setTitle(R.string.menu);
                invalidateOptionsMenu(); // creates call to onPrepareOptionsMenu()
            }

            /** Called when a drawer has settled in a completely open state. */
            public void onDrawerOpened(View drawerView) {
                super.onDrawerOpened(drawerView);
                getSupportActionBar().setTitle(R.string.detail);
                invalidateOptionsMenu(); // creates call to onPrepareOptionsMenu()
            }

        };
        mDrawerLayout.addDrawerListener(mDrawerToggle);

与应用程序图标一起打开和关闭


用户可以以一个滑动手势或指向屏幕左边缘来打开和关闭导航抽屉,如果使用了action bar,应该允许用户通过触摸应用程序图标来打开和关闭它。而且应用程序图标也应该用一个特殊的图标来指示导航抽屉的存在。这些操作完全可以用ActionBarDrawerToggle类来实现。

为了让ActionBarDrawerToggle工作,通过它的构造器创建对象,需要的参数有:

  • 持drawer的activity
  • DrawerLayout
  • 指示drawer的drawable资源
  • 描述“打开drawer”的string资源
  • 描述“关闭drawer”的string资源

另外还需要在activity生命周期方法中做一些操作:

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.navigation_view, menu);
        return super.onCreateOptionsMenu(menu);
    }

    @Override
    protected void onPostCreate(@Nullable Bundle savedInstanceState) {
        super.onPostCreate(savedInstanceState);
        mDrawerToggle.syncState();
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        mDrawerToggle.onConfigurationChanged(newConfig);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Pass the event to ActionBarDrawerToggle, if it returns
        // true, then it has handled the app icon touch event
        if (mDrawerToggle.onOptionsItemSelected(item)) {
            return true;
        }
        // Handle your other action bar items...

        return super.onOptionsItemSelected(item);
    }

效果图 效果图

DrawerLayout的Bug


如果直接使用android.support.v4.widget.DrawerLayout,会抛出
java.lang.IllegalArgumentException: DrawerLayout must be measured with MeasureSpec.EXACTLY异常。

EXACTLY表示:父容器已经检测出View所需要的精确大小,这个时候View的最终大小就是SpecSize(测量模式下的规格大小)所指定的值。它对应于LayoutParams中的match_parent和具体的数值这两种模式。(这里已经是给DrawerLayout指定了match_parent,然而没有用)

解决方案:

  • 指定DrawerLayout的大小
ViewGroup.LayoutParams params = mDrawerLayout.getLayoutParams();
        params.width = 屏幕宽度;
        params.height = 内容宽度;//除去StatusBar高度、ActionBar高度
        mDrawerLayout.setLayoutParams(params);
  • 重写DrawerLayout的onMeasure()方法
 @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        widthMeasureSpec = MeasureSpec.makeMeasureSpec(
                MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY);
        heightMeasureSpec = MeasureSpec.makeMeasureSpec(
                MeasureSpec.getSize(heightMeasureSpec), MeasureSpec.EXACTLY);
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

2.Floating Action Button

floating action button 是一个在操作界面上表示主要动作的圆形按钮,设计库中的FloatingActionButton提供了一个单独的实现,它的默认颜色使用来自主题中的colorAccent。

这里写图片描述

floating action button 除了正常大小,还支持mini大小(fabSize=”mini”,为了配合与其它元素保持视觉连续性)。FloatingActionButton继承自ImageView,所以可以通过android:src或setImageDrawable()来设置显示的图标。

<android.support.design.widget.FloatingActionButton
        android:id="@+id/floating_action_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_alignParentRight="true"
        android:onClick="onClick"
        android:src="@android:drawable/ic_menu_add"
        app:fabSize="mini"
        app:rippleColor="@android:color/holo_red_light" />

3.Snackbar

snackbar 提供了一个轻量级、快速反馈的操作。它显示在屏幕的底部,而且包含一个可选的单独的动作文本。在一定时间后会带着动画的方式自动从屏幕中消失。此外,也可以在时间到之前手动关闭它。

效果图

            Snackbar snackbar = Snackbar.make(v, "这是Snackbar!", Snackbar.LENGTH_LONG)
                    .setAction("取消", new View.OnClickListener() {

                        @Override
                        public void onClick(View v) {

                        }
                    });
            snackbar.setActionTextColor(Color.RED);//设置动作文本颜色
            View sbView = snackbar.getView();//得到显示文本的View
            TextView textView = (TextView) sbView.findViewById(android.support.design.R.id.snackbar_text);
            textView.setTextColor(Color.YELLOW);
            snackbar.show();

4.Tabs

通过tabs来切换不同的视图,对于material design 来说已经不是一个新概念了,它们和顶级层导航模式或组织不同内容类别(如不同曲风的音乐)是一样的。

这里写图片描述

设计库中的TabLayout实现了固定的(所有的tab均分视图的宽度)和滑动的(没有一个固定的大小,但可以水平滑动)tabs。在代码中添加tabs:

TabLayout tabLayout = ...;
tabLayout.addTab(tabLayout.newTab().setText("Tab 1"))

然而,如果用ViewPager在tabs之间进行水平分页,可以直接从PagerAdapter的getPageTitle()创建tabs,然后通过setupWithViewPager()将两者结合起来。这确保tab选择事件触发时可以更新ViewPager,页面改变时可以更新选中的tab。

        TabLayout tabLayout = (TabLayout) findViewById(R.id.tab_layout);
        final ViewPager viewPager = (ViewPager) findViewById(R.id.view_pager);
        viewPager.setAdapter(new ViewPagerAdapter(getSupportFragmentManager()));
        tabLayout.setupWithViewPager(viewPager);
    private class ViewPagerAdapter extends FragmentPagerAdapter {


        private final String[] mTitles = {"音乐", "电影", "小说", "美术"};
        private List<Fragment> mFragments = new ArrayList<>(mTitles.length);

        public ViewPagerAdapter(FragmentManager fm) {
            super(fm);
            mFragments.add(your fragment);
            mFragments.add(your fragment);
            mFragments.add(your fragment);
            mFragments.add(your fragment);
        }

        @Override
        public int getCount() {
            return mTitles.length;
        }

        @Override
        public Fragment getItem(int position) {
            return mFragments.get(position);
        }

        @Override
        public CharSequence getPageTitle(int position) {
            return mTitles[position];
        }

    }

CoordinatorLayout, motion, and scrolling


独特的视觉效果只是material design 的一部分:运动也是创建 material design 应用程序的重要的一部分。然而在material design 中有很多关于运动的部分,包括触摸波纹和意味深长的过渡,设计库中引进了CoordinatorLayout,它在子视图之间的触摸事件控制上提供了另外一种层级,这让设计库中的许多组件都可以利用它。

CoordinatorLayout and floating action buttons


一个很好的例子是当将FloatingActionButton作为CoordinatorLayout的子View,然后将CoordinatorLayout传递给调用Snackbar.make()的时候-snackbar 不会显示在floating action button上,而是FloatingActionButton会利用CoordinatorLayout提供的额外回调方法,在snackbar动画显示时向上移动,在snackbar动画消失时回到原来的位置,这仅支持Android 3.0或者版本更高的设备-不需要额外的代码。

CoordinatorLayout还提供了一个layout_anchor 属性,它和layout_anchorGravity属性一起,可以用来放置浮动的视图,如FloatingActiongButton,相对于其它的视图。

    <android.support.design.widget.FloatingActionButton
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="@dimen/activity_horizontal_margin"
        android:src="@android:drawable/ic_menu_add"
        app:fabSize="normal"
        app:layout_anchor="@id/app.bar.layout"
        app:layout_anchorGravity="bottom|right|end" />

CoordinatorLayout and the app bar


CoordinatorLayout其它主要的用例是关于app bar(以前为action bar)和滚动技巧。在布局中使用Toolbar,更容易自定义应用程序标志性部分的外观。设计库把这个带到了另一个层级:使用AppBarLayout可以让Toolbar和其它视图(TabLayout提供的tabs)对标记有ScrollingViewBehavior的相邻视图的滚动事件作出反应。因此,可以创建一个布局,如:

<?xml version="1.0" encoding="utf-8"?>
<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:fitsSystemWindows="true"
    app:contentScrim="?attr/colorPrimary"
    app:elevation="@dimen/size_2dp"
    app:statusBarBackground="?attr/colorPrimary">

    <android.support.design.widget.AppBarLayout
        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="wrap_content"
            android:background="?attr/colorPrimary"
            android:clipToPadding="true"
            android:fitsSystemWindows="true"
            android:minHeight="?attr/actionBarSize"
            app:elevation="@dimen/size_2dp"
            app:layout_scrollFlags="scroll|enterAlways"
            app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
            app:theme="@style/ThemeOverlay.AppCompat.ActionBar" />

        <android.support.design.widget.TabLayout
            android:id="@id/tab_layout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

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

    <android.support.v4.view.ViewPager
        android:id="@+id/view_pager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior" />

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

现在,当滚动RecyclerView(ViewPager中的RecyclerView)的时候,AppBarLayout可以对有滚动标志的子View的进入(滚进屏幕)和退出(滚出屏幕)事件做出响应。标志包括:

  • scroll:这个标志应该为所有想在屏幕上滚动的视图设置-对于那些不使用这个标志的视图,将被固定在屏幕的顶部。
  • enterAlways:这个标志确保任何向下的滚动都将使这个View变得可见,从而实现“快速返回”模式。
  • enterAlwaysCollapsed:当View即声明了minHeight 又使用了这个标志,它将只显示它的最低高度(在“collapsed”情况下),当滚动的视图到达它的顶部时将扩大它的整个高度。
  • exitUntilCollapsed:这个标志会造成View在存在之前一直滚动直到它收拢(显示它的最小高度)。
注意:所有使用scroll标志的视图必须在不使用标志的视图之前声明。这才能够确保所有View能够在顶部退出,留下固定的元素。

效果图 效果图

###**Collapsing Toolbars** ———- 直接将Toolbar添加到AppBarLayout中,可以访问enterAlwaysCollapsed和exitUntilCollapsed标志,但是不能访问不同元素对收拢做出反映的详细控制。为此,可以使用CollapsingToolbarLayout:
<?xml version="1.0" encoding="utf-8"?>
<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:fitsSystemWindows="true">

    <android.support.design.widget.AppBarLayout
        android:id="@+id/app.bar.layout"
        android:layout_width="match_parent"
        android:layout_height="@dimen/size_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"
            android:fitsSystemWindows="true"
            app:contentScrim="?attr/colorPrimary"
            app:layout_scrollFlags="scroll|exitUntilCollapsed"
            app:title="@string/coordinator_layout">

            <ImageView
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:fitsSystemWindows="true"
                android:scaleType="centerCrop"
                android:src="@drawable/me"
                app:layout_collapseMode="parallax" />

            <android.support.v7.widget.Toolbar
                android:id="@id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                android:background="?attr/colorPrimary"
                android:clipToPadding="true"
                android:fitsSystemWindows="true"
                android:minHeight="?attr/actionBarSize"
                app:elevation="@dimen/size_2dp"
                app:layout_collapseMode="pin"
                app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
                app:theme="@style/ThemeOverlay.AppCompat.ActionBar" />
        </android.support.design.widget.CollapsingToolbarLayout>
    </android.support.design.widget.AppBarLayout>

    <android.support.v4.widget.NestedScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

        <TextView
            android:id="@+id/coordinator_layout_description"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:padding="@dimen/activity_horizontal_margin"
            android:textSize="@dimen/textSize_18sp" />
    </android.support.v4.widget.NestedScrollView>

    <android.support.design.widget.FloatingActionButton
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="@dimen/activity_horizontal_margin"
        android:src="@android:drawable/ic_menu_add"
        app:fabSize="normal"
        app:layout_anchor="@id/app.bar.layout"
        app:layout_anchorGravity="bottom|right|end" />
</android.support.design.widget.CoordinatorLayout>

效果图 效果图

当view收拢的时候,CollapsingToolbarLayout的
app:layout_collapseMode=”pin”可以确保Toolbar依然固定在屏幕的顶部。更好的是,当CollapsingToolbarLayout和Toolbar一起使用的时候,在布局全部显示时标题自动变大,收拢时过渡到它的默认大小。但需要注意的是,标题是通过CollapsingToolbarLayout的setTitle()来设置,而不是Toolbar。

除了固定一个view以外,还可以使用app:layout_collapseMode=”parallax” (可以选择app:layout_collapseParallaxMultiplier=”0.7” 来设置视差乘数)来实现视差滑动(如CollapsingToolbarLayout中的ImageView)。对CollapsingToolbarLayout来说,这个使用例子与app:contentScrim=”?attr/colorPrimary”属性配对会更好。

猜你喜欢

转载自blog.csdn.net/wangjiang_qianmo/article/details/52355694