[TOC]

本文为翻译文章,原文地址在这里

Material Design是由Google提出的关于如何构建一个Android应用的完整的指导方案,该方案不仅仅可以被用于Android应用的设计,同样可以被用于Web端的设计。目前在Web端上已经出现了大量践行Material Design的开源的组件库。在开发个APP的过程种,Android提供了多个辅助库来帮助开发者实践这些设计指南。其中最重要的几个库就是:

  • com.android.support:appcompat-v7:23.1.0

  • com.android.support:design :23.1.0

总而言之,如果使用Android Studio来开发应用的话,这两个库会被自动作为依赖引入项目中。在开发应用时,一个重要的方面就是应用的颜色模式的选择,而Material Design的原则中也阐述了如何来选择颜色。

Material Design: Colors

创建应用的第一步即是选择合适的颜色方案。在这种目标下可以利用这个站点 website 来根据Material Design来选取合适的颜色。在经过选择之后我们可以下载colors.xml:

<resources>
    <color name="primary">#3F51B5</color>
    <color name="primary_dark">#303F9F</color>
    <color name="primary_light">#C5CAE9</color>
    <color name="accent">#03A9F4</color>
    <color name="primary_text">#212121</color>
    <color name="secondary_text">#727272</color>
    <color name="icons">#FFFFFF</color>
    <color name="divider">#B6B6B6</color>
</resources>

在选择了合适的颜色之后就要创建自定义的主题,一般来说应用需要支持尽可能多的不同的设备,而不仅仅是Lollipop或者之后的版本。基于这个原因,我们需要在values目录下创建两个文件夹:

  • style

  • style-v21

第一个文件夹下面的样式设计会被低于Lollipop版本的手机使用,第二个文件夹下面的样式设计会被高于Lollipop版本的手机使用,在第一个目录下,style.xml文件应该被定义如下:

<resources>
    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">    <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/primary</item>
        <item name="colorPrimaryDark">@color/primary_dark</item>
        <item name="colorAccent">@color/accent</item>
    </style>
    <style name="MyAppTheme" parent="@style/AppTheme" />
</resources>

而在第二个文件夹下面只需要添加如下的配置:

<resources>
   <style name="MyAppTheme" parent="AppTheme">
     <item name="android:windowContentTransitions">true</item>
     <item name="android:windowAllowEnterTransitionOverlap">true</item>
     <item name="android:windowAllowReturnTransitionOverlap">true</item>
     <item name="android:windowSharedElementEnterTransition">@android:transition/move</item>
     <item name="android:windowSharedElementExitTransition">@android:transition/move</item>
  </style>
</resources>

最后,在Manifest.xml文件中,应该将application的主题修正为:

<application
     android:theme="@style/MyAppTheme" >
     ...
</application>

Android Toolbar

在开发一个Android应用中另一个比较重要的组件就是Toolbar,它的作用很类似于早年的Android中的Action Bar,它主要担负着如下几个功能:

  • 导航按钮

  • 应用标题与副标题

  • 活动菜单

  • 图标

根据Material Design的设计原则,Toolbar会包含之前选择的几个主要的颜色,可以将其加入到Android应用中:

<RelativeLayout 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"
    tools:context=".MainActivity"
    android:id="@+id/layout">
   <include layout="@layout/toolbar" />
</RelativeLayout>

其中Toolbar的布局如下:

<android.support.v7.widget.Toolbar xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:local="http://schemas.android.com/apk/res-auto"
    android:id="@+id/toolbar"
    android:layout_width="match_parent"
    android:layout_height="?attr/actionBarSize"
    android:background="@color/primary"
    local:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
    local:popupTheme="@style/ThemeOverlay.AppCompat.Light" />

注意到在第五行中是利用?attr/actionBarSize设置了Toolbar的高度。同时在Activity种需要利用代码设置Toolbar:

@Override
protected void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   setContentView(R.layout.activity_main);
   setToolBar();
}
...
private void setToolBar() {
    Toolbar tb = (Toolbar) findViewById(R.id.toolbar);
    setSupportActionBar(tb);
    ActionBar ab = getSupportActionBar();
    ab.setHomeAsUpIndicator(R.drawable.ic_menu_white_24dp);
    ab.setDisplayHomeAsUpEnabled(true);
}

添加活动菜单

一旦Toolbar配置完毕之后,就需要为该菜单添加活动选项与出现在Toolbar上的菜单栏目。首先需要在res/menu目录下添加一个main_menu.xml:

<?xml version="1.0" encoding="utf-8"?>

<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <item android:id="@+id/menu_settings"
          android:title="Settings"
          android:icon="@android:drawable/ic_menu_preferences"
          app:showAsAction="always"
          android:orderInCategory="100"/>

    <item android:id="@+id/menu_help"
        android:title="Help"
        android:icon="@android:drawable/ic_menu_help"
        app:showAsAction="ifRoom"
        android:orderInCategory="110" />

    <item android:id="@+id/menu_compass"
        android:title="Compass"
        android:icon="@android:drawable/ic_menu_compass"
        app:showAsAction="never"
        android:orderInCategory="105"/>
</menu>

现在Activity就是如下:

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

当用户选择菜单中的某一项的时候,会被自动捕获:

@Override
public boolean onOptionsItemSelected(MenuItem item) {
   int itemId = item.getItemId();
   String btnName = null;
   switch(itemId) {
     case R.id.menu_settings:
        btnName = "Settings";
        break;
     case R.id.menu_compass:
        btnName = "Compass";
        break;
    case R.id.menu_help:
        btnName = "Help";
        break;
  }
  Snackbar.make(layout, "Button " + btnName, Snackbar.LENGTH_SHORT).show();
  return true;
}

Android Navigation Drawer

Navigation drawer是Google重点推荐的一种UI构建模式,它本质上是一种侧边菜单来辅助组织所有的导航相关的按钮。它提供了一种通用的方式来访问APP内的不同的页面与信息。首先我们需要将主Activity的布局文件修改为如下方式:

<?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"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:context=".MainActivity"
  >
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
        <include layout="@layout/toolbar" />
        <!-- Let's add fragment -->
        <FrameLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:id="@+id/frame"/>
    </LinearLayout>
    <android.support.design.widget.NavigationView
        android:id="@+id/navigation"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        app:menu="@menu/nav_items" />
</android.support.v4.widget.DrawerLayout>

在这种情况下toolbar被放置在了LinearLayout之中,不过App中处理Toolbar的方式和之前是一样的。而所有的页面内容被放置在了FrameLayout中,NavigationView才是真正的App中的菜单,所有的菜单项被写在了nav_item中:

<?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/fab"
              android:title="Floating Action Button"
              android:icon="@drawable/ic_action_fab" />
        <item android:id="@+id/star"
            android:title="Star"
            android:icon="@drawable/ic_action_star" />
        <item android:id="@+id/uploadr"
            android:title="Star"
            android:icon="@drawable/ic_action_upload" />
    </group>
</menu>

同样的对于用户点击的响应也是比较简单的:

private void setNavigationDrawer() {
    dLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
    NavigationView navView = (NavigationView) findViewById(R.id.navigation);
    navView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
        @Override
        public boolean onNavigationItemSelected(MenuItem menuItem) {
            Fragment frag = null;
            int itemId = menuItem.getItemId();
            if (itemId == R.id.fab) {
               frag = new Fragment1();
            }
            else if (itemId == R.id.star) {
                frag = new Fragment2();
            }
            if (frag != null) {
                FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
                transaction.replace(R.id.frame, frag);
                transaction.commit();
                dLayout.closeDrawers();
                return true;
            }
            return false;
        }
    });
}

最后一步就是将Drawer与用户的点击事件绑定到一起:

@Override
public boolean onOptionsItemSelected(MenuItem item) {
     int itemId = item.getItemId();
     String btnName = null;
     switch(itemId) {
         ...
         // Android home
         case android.R.id.home: {
             dLayout.openDrawer(GravityCompat.START);
             return true;
         }
     }
   .....
 }


王下邀月熊_Chevalier
22.5k 声望8.5k 粉丝

爱代码 爱生活 希望成为全栈整合师