Material Design with the Android Design Support Library
阅读原文时间:2023年07月08日阅读:2

Material Design with the Android Design Support Library

原文http://www.sitepoint.com/material-design-android-design-support-library/

Material Design,Android 5.0发布时为android app 和其他平台app引入的一门新的设计语言。

它带来了一些新的UI组件,如“Floating Action Button”。实施这些新组件,同时确保向后兼容性是通常一个繁琐的过程。第三方库通常会需要简化过程。

在今年的谷歌IO大会上,谷歌推出了Android设计支持库带来了一些重要的Material Design组件的开发。该组件是向后兼容,向后兼容到Android 2.1,并实现他们的是比以前更容易。该库包括一个抽屉式导航视图,浮动编辑文本,浮动操作按钮,Snackbar,标签和motion和滚动框架绑在一起的标签。在本教程中,我们将创建一个应用程序,展示了这些组件。

工程源码:Github https://github.com/sitepoint-editors/Design-Demo

在开始使用这些组件之前,我们将建立项目,并设置一些样式。采用Android Studio中创建一个新的Android项目。将它命名为“Material Design”并保留其他设置为默认值,保证了最低的SDK版本是在API级别15。

在 build.gradle(module:app)文件添加库依赖:

compile 'com.android.support:design:22.2.1'

compile 'com.android.support:cardview-v7:22.2.1'

第一个声明添加 design support library,第二个声音添加 CardView 库,稍候我们将用到。接下来同步更新一下project。它可能会下载一些其他的支持库。

创建文件夹 res/values下资源文件 colors.xml,内容更改如下:

#3F51B5 #303F9F #FF4081 在res/values/strings.xml修改如下,我们在工程需要用到的字符串: Design Demo Hello world! Settings Attachment Images My Location Sub Menu Sub Menu Item 1 Sub Menu Item 2 Drawer Header Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin consectetur diam id aliquam scelerisque. Donec ultrices lacus vel dignissim pharetra. Vivamus pharetra augue quis rhoncus placerat. Sed ultricies at risus non cursus. Nam rutrum leo nec placerat consectetur. Vestibulum feugiat eleifend diam, nec interdum augue tincidunt sit amet. Praesent feugiat est auctor lacus consectetur, vitae pellentesque dolor laoreet. SecondActivity 修改 res/values/styles.xml 文件: 在上面代码中,我们根据Material Design指南自定义了app的primary, primary-dark and accent color颜色 可定制其它主题设置如下图所示。 注意 item 名称不包含android: 前缀(如android:colorPrimaryDark),这是为了向后兼容。android: 注解需要将最低的API级别设置为 21。我们需要NoActionBar主题。 因为我们将使用一个 Toolbar作为 Action Bar(或者称为 AppBar). (SOURCE: [https://developer.android.com/training/material/theme.html](https://developer.android.com/training/material/theme.html)) 添加 Toolbar, res/layout/activity\_main.xml 上面代码中,我们删除了RelativeLayout 默认的 padding,然后添加支持库的 Toolbar。如果你只支持API级别21以上的设备,那么你可以使用默认的 Toolbar组件,而不需要支持库。 修改 MainActivity.java 的 onCreate(Bundle)方法: @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity\_main); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); ActionBar actionBar = getSupportActionBar(); actionBar.setHomeAsUpIndicator(R.drawable.ic\_menu); actionBar.setDisplayHomeAsUpEnabled(true); } [Download the project resources from gitHub](https://github.com/sitepoint-editors/Design-Demo/tree/master/app/src/main/res) 包含drawable文件夹。你需要复制res目录到你的工程,上面的代码才不会显示错误信息。 上面代码,我们引用了 Toolbar 作为 Action Bar,然后为 Action Bar设置 Home 图标。 运行代码,你会看一个设置了 Toolbar的AppBar. Navigation Drawer是一个Android应用公用组件。它是建立Android导航层次结构的方式之一, Tabs和 Spinners。实现一个从来就不是一个很快的过程,但现在随着设计支持库,它的实现要简单得多。 接着,改修 activity\_main.xml: 上面代码,我们在DrawerLayout布局添加了一个NavigationView,这里你会注意到两个属性:`app:heanderLaytout` 用来控制 header 部分的布局;`app:menu` 指定了菜单资源,这两个资源我们将在下面创建。 创建 res/layout/drawer\_header.xml文件,内容为: 这里我们创建了NavigationView的头部视图,然后设置视图背景颜色,高度和显示的文本。 接下来在 res/menu目录下创建 drawer.xml文件: 在上文中,我们创建了drawer里的菜单项。第一部分显示了菜单项的集合。选中的项目将在导航抽屉突出显示,确保用户知道哪些导航项目当前选择。在第二部分中,我们使用一个subheader以区别第一部分。 在MainActivity.java 添加以下成员变量: private DrawerLayout mDrawerLayout; 然后再 `onCreate(Bundle)`函数添加以下代码: mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer\_layout); 修改onOptionsItemSelected(MenuItem) 函数: @Override public boolean onOptionsItemSelected(MenuItem item) { // Handle action bar item clicks here. The action bar will // automatically handle clicks on the Home/Up button, so long // as you specify a parent activity in AndroidManifest.xml. int id = item.getItemId(); switch (id) { case android.R.id.home: mDrawerLayout.openDrawer(GravityCompat.START); return true; case R.id.action\_settings: return true; } return super.onOptionsItemSelected(item); } 当按钮屏幕的 home 按钮,drawer将从左边滑出。运行代码看看效果。 我们还需要为NavigationView设置 OnNavigationItemSelectedListener监听捕获菜单选择点击事件,在onCreate(Bundle)添加代码: NavigationView navigationView = (NavigationView) findViewById(R.id.navigation\_view); navigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() { @Override public boolean onNavigationItemSelected(MenuItem menuItem) { menuItem.setChecked(true); mDrawerLayout.closeDrawers(); Toast.makeText(MainActivity.this, menuItem.getTitle(), Toast.LENGTH\_LONG).show(); return true; } }); 上面的代码设置在导航视图中的监听器,这样当一个抽屉菜单项被选中,菜单项设置为选中状态(这只会影响到标记为可检查的菜单项),抽屉被关闭,并Toast显示选定菜单项的标题。 一个浮动的操作按钮是一个圆形按钮,表示您界面上的主要操作。设计库的FloatingActionButton提供一个一致的实现,在默认情况下使用colorAccent的主题色。 添加一个正常大小(56dp)浮动动作按钮,它支持最低大小(40dp),当与其他元件视觉连续性是至关重要的。 修改 activity\_main.xml添加 FAB: 在上面,我们添加FloatingActionButton在布局的右下角。android:src设置按钮中显示的图标。在代码中,你可以使用setImageDrawable()。 运行代码,显示结果 下一节我们会为 FAB设置onClickListener 以前如果你想快速反馈信息给用户,你可以用 Toast,现在你有另一个选择了,那就是 Snackbar Snackbar显示在屏幕的底部,并包含文字与可选的单个动作。超时会自动滑出屏幕。用户也可以在超时前手动将其滑出。 它的API与Toast类似,但是功能却被Toast强大 在 MainActivity.java的`onCreate(Bundle)`:添加以下代码 FloatingActionButton fab = (FloatingActionButton)findViewById(R.id.fab); fab.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Snackbar.make(findViewById(R.id.drawer\_layout), "I'm a Snackbar", Snackbar.LENGTH\_LONG).setAction("Action", new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(MainActivity.this, "Snackbar Action", Toast.LENGTH\_LONG).show(); } }).show(); } }); 在上面代码中,我们为FAB设置一个onClickListener 监听对象,然后显示Snackbar,当用户触发 Action 按钮时,显示一个 Toast信息。 注意函数make()的第一个参数,它将作为Snackbar父视图,以确实Snackbar显示在父视图底部的锚点。 运行程序: 注意到Snackbar与FAB重叠,我们将在下一节通过CoordinatorLayout修复这个问题。 在Android里通过标签来切换两个不同的视图已经不是新概念了。但是通过支持库 TabLayout可以简化添加标签的操作。实现了固定 tab(所有 tab 平分秋色,宽度固定)和滚动 tab(宽度根据标题长度自适应,可以水平滑动)两种形式 添加 TabLayout 到应用中,修改 res/layout/activity\_main.xml: 在上文中,我们添加一个TabLayout以及一个ViewPager。该ViewPager将用于使标签水平切换。 在 MainActivity.java文件下添加以下类: public static class DesignDemoFragment extends Fragment { private static final String TAB\_POSITION = "tab\_position"; public DesignDemoFragment() { } public static DesignDemoFragment newInstance(int tabPosition) { DesignDemoFragment fragment = new DesignDemoFragment(); Bundle args = new Bundle(); args.putInt(TAB\_POSITION, tabPosition); fragment.setArguments(args); return fragment; } @Nullable @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { Bundle args = getArguments(); int tabPosition = args.getInt(TAB\_POSITION); TextView tv = new TextView(getActivity()); tv.setGravity(Gravity.CENTER); tv.setText("Text in Tab #" + tabPosition); return tv; } } static class DesignDemoPagerAdapter extends FragmentStatePagerAdapter { public DesignDemoPagerAdapter(FragmentManager fm) { super(fm); } @Override public Fragment getItem(int position) { return DesignDemoFragment.newInstance(position); } @Override public int getCount() { return 3; } @Override public CharSequence getPageTitle(int position) { return "Tab " + position; } } 然后在onCreate(Bundle)方法添加以下代码: DesignDemoPagerAdapter adapter = new DesignDemoPagerAdapter(getSupportFragmentManager()); ViewPager viewPager = (ViewPager)findViewById(R.id.viewpager); viewPager.setAdapter(adapter); TabLayout tabLayout = (TabLayout)findViewById(R.id.tablayout); tabLayout.setupWithViewPager(viewPager); 上面代码中,我们创建一个简单的Fragment,并包含一个TextView视图,然后创建一个FragmentStatePagerAdapter 作为ViewPager的适配器,适配器的getCount()函数返回了 标签总数,getItem(int) 返回了当前标签页的fragment, getPageTitle(int) 则设置了标签title,要让它工作,我们使用了 TabLayout的setupWithViewPager()函数,更保标签更新时,viewpager跟着一起更新。 运行程序: CoordinatorLayout 将FloatingActionButton 作为`CoordinatorLayout`  的子视图,然后将Snackbar.make() 函数的第一个参数指定为`CoordinatorLayout` 大概意思也就是说:当Snackbar向上移出时,FAB会跟着往上移动,就是同步。就解决了重叠的问题了。(自已的英语水平真差出新高度了) 修改 activity\_main.xml文件: 以上代码中,FAB作为CoordinatorLayout的子视图,就是将RelativeLayout布局更改成CoordinatorLayout,注意设置FAB的位置 `android:layout_gravity="bottom|right"`. 接下来就是在 MainActivity.java创建Snackbar时,CoordinatorLayout为作Snackbar的视图参数(第一个参数): Snackbar.make(findViewById(R.id.coordinator), "I'm a Snackbar", Snackbar.LENGTH\_LONG).setAction("Action", new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(MainActivity.this, "Snackbar Action", Toast.LENGTH\_LONG).show(); } }).show(); 运行程序,现在当你点击FAB按钮时,Snackbar会往向滑进界面,但是不会与FAB按钮重叠,FAB按钮会跟着往上移动,但Snackbar移出屏幕时,FAB按钮会跟着回到原来的位置。 另一个主要用例CoordinatorLayout涉及应用栏和滚动技术。设计库提供了AppBarLayout允许工具栏和其他视图(如由TabLayout提供选项卡)反应以在标有ScrollingViewBehavior同级视图滚动事件。 实现这个功能之前,让我们先创建一些东西,我们可以滚动。我们将使用一个RecyclerView创建,我们可以滚动项目列表。 (作者使用 ListView, GridView和 ScrollViews不能让CoordinatorLayout正常工作) _build.gradle_ (Module: app) 文件添加如下内容: compile 'com.android.support:recyclerview-v7:22.2.1' 创建 res/layout/ _fragment\_list\_view.xml__文件_ 接着创建另一个布局文件 res/layout/list\_row.xml文件 创建一个java文件_DesignDemoRecyclerAdapter__.java_ package com.echessa.designdemo; // Rename as Appropriate import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; import java.util.List; /\*\* \* Created by echessa on 7/24/15. \*/ public class DesignDemoRecyclerAdapter extends RecyclerView.Adapter { private List mItems; DesignDemoRecyclerAdapter(List items) { mItems = items; } @Override public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) { View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.list\_row, viewGroup, false); return new ViewHolder(v); } @Override public void onBindViewHolder(ViewHolder viewHolder, int i) { String item = mItems.get(i); viewHolder.mTextView.setText(item); } @Override public int getItemCount() { return mItems.size(); } public class ViewHolder extends RecyclerView.ViewHolder { private final TextView mTextView; ViewHolder(View v) { super(v); mTextView = (TextView)v.findViewById(R.id.list\_item); } } } 修改`DesignDemoFragment.onCreateView()` 函数的代码: @Nullable @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { Bundle args = getArguments(); int tabPosition = args.getInt(TAB\_POSITION); ArrayList items = new ArrayList(); for (int i = 0; i < 50; i++) { items.add("Tab #" + tabPosition + " item #" + i); } View v = inflater.inflate(R.layout.fragment\_list\_view, container, false); RecyclerView recyclerView = (RecyclerView)v.findViewById(R.id.recyclerview); recyclerView.setLayoutManager(new LinearLayoutManager(getActivity())); recyclerView.setAdapter(new DesignDemoRecyclerAdapter(items)); return v; } 使用创建的 fragment\_list\_view.xml布局来填充视图,现在fragment视图将会包含一个列表项。运行程序: 修改 activity\_main.xml文件: 在上面代码中,我们将 Toolbar和TabLayout嵌入到 AppbarLayout中, AppBarLayout允许Toolbar及标志为ScrollingViewBehavior的其他视图响应滚动事件。当通过RecyclerView用户滚动时,AppBarLayout响应通过其子的滚动标志来控制他们是如何进入(滚动在屏幕上)和出口(滚动关闭屏幕)。也就是所有标志为ScrollingViewBehavior 的子视图都会被推入和拉出。 Flags 包括: * **scroll****:** 这个标志会被设置到所有希望滚出屏幕的视图上,如果不设置这一标志,则视图会被一直保留在屏幕顶部。 * **enterAlways****:** 这个标志会确保任何下滑滚屏都会触发视图的展现,等于开启了一种「快速返回」模式。 * **enterAlwaysCollapsed****:** 如果设置了 minHeight 和这个标志,你的视图通常会折叠显示,只有当滚动视图已经到达了它的顶点以后才会打开到完整高度。 * **exitUntilCollapsed****:** 这个标志会导致视图在退出之前,一直被锁定 注意一点:所有设置了 scroll 标志的视图必须在未设该标志的视图之前进行声明,这样可以确保所有的滚动视图都从顶部退出,而固定元素都不受影响。 CollapsingToolbarLayout为AppBar提供了另一种滚动行为。我们将创建另一个Activity,只包含一个Toolbar. 创建一个空的 Activity,命名:SecondActivity 修改 res/layout/activity\_second.xml:

<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="250dp"

    android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">

    <android.support.design.widget.CollapsingToolbarLayout

        android:id="@+id/collapsing\_toolbar"

        android:layout\_width="match\_parent"

        android:layout\_height="match\_parent"

        app:contentScrim="?attr/colorPrimary"

        app:layout\_scrollFlags="scroll|exitUntilCollapsed">

        <ImageView

            android:layout\_width="match\_parent"

            android:layout\_height="match\_parent"

            android:scaleType="centerCrop"

            android:src="@drawable/image"

            app:layout\_collapseMode="parallax"/>

        <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.v7.widget.Toolbar>

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

    <LinearLayout

        android:orientation="vertical"

        android:paddingTop="24dp"

        android:layout\_width="match\_parent"

        android:layout\_height="match\_parent">

        <android.support.v7.widget.CardView

            android:layout\_margin="16dp"

            android:layout\_width="match\_parent"

            android:layout\_height="wrap\_content">

            <LinearLayout

                android:orientation="vertical"

                android:padding="16dp"

                android:layout\_width="match\_parent"

                android:layout\_height="wrap\_content">

                <TextView

                    android:layout\_width="match\_parent"

                    android:layout\_height="wrap\_content"

                    android:text="Lorem ipsum"/>

                <TextView

                    android:layout\_width="match\_parent"

                    android:layout\_height="wrap\_content"

                    android:text="@string/second\_activity\_text"/>

            </LinearLayout>

        </android.support.v7.widget.CardView>

        <android.support.v7.widget.CardView

            android:layout\_margin="16dp"

            android:layout\_width="match\_parent"

            android:layout\_height="wrap\_content">

            <LinearLayout

                android:orientation="vertical"

                android:padding="16dp"

                android:layout\_width="match\_parent"

                android:layout\_height="wrap\_content">

                <TextView

                    android:layout\_width="match\_parent"

                    android:layout\_height="wrap\_content"

                    android:text="Lorem ipsum"/>

                <TextView

                    android:layout\_width="match\_parent"

                    android:layout\_height="wrap\_content"

                    android:text="@string/second\_activity\_text"/>

            </LinearLayout>

        </android.support.v7.widget.CardView>

        <android.support.v7.widget.CardView

            android:layout\_margin="16dp"

            android:layout\_width="match\_parent"

            android:layout\_height="wrap\_content">

            <LinearLayout

                android:orientation="vertical"

                android:padding="16dp"

                android:layout\_width="match\_parent"

                android:layout\_height="wrap\_content">

                <TextView

                    android:layout\_width="match\_parent"

                    android:layout\_height="wrap\_content"

                    android:text="Lorem ipsum"/>

                <TextView

                    android:layout\_width="match\_parent"

                    android:layout\_height="wrap\_content"

                    android:text="@string/second\_activity\_text"/>

            </LinearLayout>

        </android.support.v7.widget.CardView>

        <android.support.v7.widget.CardView

            android:layout\_margin="16dp"

            android:layout\_width="match\_parent"

            android:layout\_height="wrap\_content">

            <LinearLayout

                android:orientation="vertical"

                android:padding="16dp"

                android:layout\_width="match\_parent"

                android:layout\_height="wrap\_content">

                <TextView

                    android:layout\_width="match\_parent"

                    android:layout\_height="wrap\_content"

                    android:text="Lorem ipsum"/>

                <TextView

                    android:layout\_width="match\_parent"

                    android:layout\_height="wrap\_content"

                    android:text="@string/second\_activity\_text"/>

            </LinearLayout>

        </android.support.v7.widget.CardView>

        <android.support.v7.widget.CardView

            android:layout\_margin="16dp"

            android:layout\_width="match\_parent"

            android:layout\_height="wrap\_content">

            <LinearLayout

                android:orientation="vertical"

                android:padding="16dp"

                android:layout\_width="match\_parent"

                android:layout\_height="wrap\_content">

                <TextView

                    android:layout\_width="match\_parent"

                    android:layout\_height="wrap\_content"

                    android:text="Lorem ipsum"/>

                <TextView

                    android:layout\_width="match\_parent"

                    android:layout\_height="wrap\_content"

                    android:text="@string/second\_activity\_text"/>

            </LinearLayout>

        </android.support.v7.widget.CardView>

    </LinearLayout>

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

在上文中,我们使用了CollapsingToolbarLayout,标志为 scroll和 exitUnitCollapsed,使它的子视图全部滚出屏幕,对于Toolbar,我们设置为:app:layout_collapseMode="pin" 可以确保当视图折叠时 Toolbar固定在顶部,在CollapsingToolbarLayout的标题将出现较大的时候,布局是完全可见,然后过渡到其默认大小,因为它是折叠的。我们在代码中设置此title。布局的其余部分是一个NestedScrollView包含几个CardView。

在SecondActivity.java中的onCreate(Bundle)函数添加代码:

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);

Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);  
setSupportActionBar(toolbar);  
getSupportActionBar().setDisplayHomeAsUpEnabled(true);

CollapsingToolbarLayout collapsingToolbar = (CollapsingToolbarLayout) findViewById(R.id.collapsing\_toolbar);  
collapsingToolbar.setTitle("Second Activity");  

}

这里我们为Toolbar添加向上符号和为 CollapsingToolbarLayout设置title

要使用向上符号工作,还需要在manifest文件中为SecondActivity添加标签

修改DesignDemoRecyclerAdapter.java 的onBindViewHolder()方法:

@Override
public void onBindViewHolder(ViewHolder viewHolder, int i) {
String item = mItems.get(i);
viewHolder.mTextView.setText(item);

viewHolder.mTextView.setOnClickListener(new View.OnClickListener() {  
    @Override  
    public void onClick(View view) {  
        Context context = view.getContext();  
        context.startActivity(new Intent(context, SecondActivity.class));  
    }  
});  

}

在上文中,我们的RecyclerView的每一行上的TextView设置一个onClick监听。这不是设置RecyclerView项监听的好方式,因为如果你运行应用程序,听者的触摸目标将只包括与该行的文本的区域,而不是整个行。我这样做是在这里,因为我要的是一个方法来启动新的活动,所以我选择一个的方式来写最少的代码。

最后要介绍的一个组件是支持库里改良型的EditText,譬如它在我们输入第一个字符的时候,就会自动隐藏掉提示标签。现在你该使用 TextInputLayout 了,它会在用户开始输入之后,自动将提示标签悬浮到 EditText 上方,这样用户就永远都能知道输入内容的上下文。

为了演示这个功能,我们修改 activity_second.xml布局文件的第二个CardView的第一个TextView为 EditText.

                <EditText  
                    android:layout\_width="match\_parent"  
                    android:layout\_height="wrap\_content"  
                    android:inputType="textEmailAddress"  
                    android:hint="Email" />  
            </android.support.design.widget.TextInputLayout>

运行程序,提示文件将浮现在EditView的上面

你还可以通过设置EditView的setError()函数来为EditView显示一个”错误信息”。

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器