Material Design,中文名:材料设计语言,是由Google推出的全新的设计语言,谷歌表示,这种设计语言旨在为手机、平板电脑、台式机和“其他平台”提供更一致、更广泛的“外观和感觉”。然而,Material Design却是UI设计者和Android开发者同时应该注意和学习的事。为什么呢,美工是设计者,程序员是实现者。程序员要知道怎么快速地去实现美工根据Material Design设计的UI,当然,谷歌也考虑到了这一点,于是也有了Material Design Library。
Material Design Library,可以说是颇受开发者瞩目的一个控件效果库,能够让开发者在Android 2.2系统上使用Android 5.0才支持的控件效果,比如扁平、矩形、浮动按钮,复选框以及各式各样的进度指示器等。
那么,对于Material Design的风格,开发者要怎么实现呢?
1. ToolBar
实现:android.support.v7.widget.Toolbar
- app_bar_main.xml : 标题栏布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.Toolbar
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:background="@color/colorPrimary"
android:layout_height="56dp"
app:contentInsetStart="0.0dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="@+id/fl_title_left"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:background="?attr/selectableItemBackgroundBorderless"
android:paddingLeft="15dp"
android:paddingRight="15dp"
android:visibility="gone">
<ImageView
android:id="@+id/iv_title_left"
android:layout_width="23dp"
android:layout_height="wrap_content"
android:layout_gravity="center" />
</FrameLayout>
<TextView
android:id="@+id/tv_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/app_name"
android:textColor="#fff"
android:textSize="18sp"
android:gravity="center_horizontal"
android:layout_marginLeft="50dp"
android:layout_marginRight="50dp"
android:lines="1"
android:layout_centerVertical="true"/>
<FrameLayout
android:id="@+id/fl_title_right"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:background="?attr/selectableItemBackgroundBorderless"
android:paddingLeft="15dp"
android:paddingRight="15dp"
android:layout_alignParentRight="true"
android:visibility="gone">
<ImageView
android:id="@+id/iv_title_right"
android:layout_width="23dp"
android:layout_height="wrap_content"
android:layout_gravity="center"/>
</FrameLayout>
</RelativeLayout>
</android.support.v7.widget.Toolbar>
</LinearLayout>
- ToolBarUtil.java : ToolBar帮助类
public class ToolBarUtil {
private Toolbar toolbar;
private FrameLayout fl_title_left;
private ImageView iv_title_left;
private TextView tv_title;
private FrameLayout fl_title_right;
private ImageView iv_title_right;
public ToolBarUtil(Toolbar toolbar){
this.toolbar = toolbar;
this.fl_title_left = (FrameLayout) toolbar.findViewById(R.id.fl_title_left);
this.iv_title_left = (ImageView) toolbar.findViewById(R.id.iv_title_left);
this.tv_title = (TextView) toolbar.findViewById(R.id.tv_title);
this.fl_title_right = (FrameLayout) toolbar.findViewById(R.id.fl_title_right);
this.iv_title_right = (ImageView) toolbar.findViewById(R.id.iv_title_right);
}
// 设置标题位置
public void setTitleGravity(int gravity){
this.tv_title.setGravity(gravity);
}
// 设置左边按钮图标以及点击事件
public void setLeftButton(int icon, View.OnClickListener listener){
this.iv_title_left.setImageResource(icon);
this.fl_title_left.setVisibility(View.VISIBLE);
this.fl_title_left.setOnClickListener(listener);
}
// 设置右边按钮图标以及点击事件
public void setRightButton(int icon, View.OnClickListener listener){
this.iv_title_right.setImageResource(icon);
this.fl_title_right.setVisibility(View.VISIBLE);
this.fl_title_right.setOnClickListener(listener);
}
- MainActivity.java : 设置ToolBar标题及其左右Button的图标和点击事件
@Override
public void initToolBar() {
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
ToolBarUtil toolBarUtil = new ToolBarUtil(toolbar);
toolBarUtil.setTitleGravity(Gravity.LEFT);
toolBarUtil.setLeftButton(R.drawable.titlebar_menu, new View.OnClickListener() {
@Override
public void onClick(View view) {
// 菜单键
}
});
toolBarUtil.setRightButton(R.drawable.titlebar_search, new View.OnClickListener() {
@Override
public void onClick(View view) {
// 搜索键
}
});
}
另外,为了实现这种标题栏的效果,还需要设置app的主题(去掉默认标题栏,将状态栏颜色设置为colorPrimaryDark)
<style name="AppTheme02" parent="Theme.AppCompat.Light.NoActionBar">
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
</style>
2. TabLayout
实现:android.support.design.widget.TabLayout
常用属性:
属性名 | 作用 |
---|---|
app:tabTextColor="" | 标签颜色 |
app:tabSelectedTextColor="" | 标签选中时颜色 |
app:tabIndicatorColor="" | 标签下标指示器颜色 |
app:tabIndicatorHeight="" | 标签下标指示器高度 |
app:tabMode="" | 标签能否进行横向滑动 |
- build.gradle(Module:app) : 导入Material Design Library
dependencies {
...
compile 'com.android.support:design:25.0.1'
}
- activity_main.xml : TabLayout布局
<android.support.design.widget.TabLayout
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/tabLayout"
android:layout_width="match_parent"
android:layout_height="45dp"
app:tabTextColor="#666"
app:tabSelectedTextColor="@color/colorPrimary"
app:tabIndicatorColor="@color/colorPrimary"/>
- MainActivity.java : 设置Tab标题和ViewPager
public void initTabs(){
TabLayout tabLayout = (TabLayout) findViewById(R.id.tabLayout);
tabLayout.addTab(tabLayout.newTab().setText("design01"));
tabLayout.addTab(tabLayout.newTab().setText("design02"));
tabLayout.addTab(tabLayout.newTab().setText("design03"));
tabLayout.setupWithViewPager(viewPager);
}
- activity_main.xml : TabLayout布局
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#ddd"
android:layout_above="@+id/tabLayout"/>
<android.support.design.widget.TabLayout
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/tabLayout"
android:layout_width="match_parent"
android:layout_height="60dp"
app:tabTextColor="#666"
app:tabSelectedTextColor="@color/colorPrimary"
app:tabIndicatorHeight="0dp"
android:layout_alignParentBottom="true"/>
</RelativeLayout>
- MainActivity.java : 设置Tab标题和ViewPager,以及选中图标改变事件
public void initTabs(){
tabLayout = (TabLayout) findViewById(R.id.tabLayout);
tabLayout.addTab(tabLayout.newTab().setText("design01").setIcon(R.drawable.tab_design_selected));
tabLayout.addTab(tabLayout.newTab().setText("design02").setIcon(R.drawable.tab_design_unselected));
tabLayout.addTab(tabLayout.newTab().setText("design03").setIcon(R.drawable.tab_design_unselected));
tabLayout.setOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override
public void onTabSelected(TabLayout.Tab tab) {
tabLayout.getTabAt(0).setIcon(R.drawable.tab_design_unselected);
tabLayout.getTabAt(1).setIcon(R.drawable.tab_design_unselected);
tabLayout.getTabAt(2).setIcon(R.drawable.tab_design_unselected);
if (tab == tabLayout.getTabAt(0)) {
tab.setIcon(R.drawable.tab_design_selected);
}
else if (tab == tabLayout.getTabAt(1)) {
tab.setIcon(R.drawable.tab_design_selected);
}
else if (tab == tabLayout.getTabAt(2)) {
tab.setIcon(R.drawable.tab_design_selected);
}
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {
}
@Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
}
3. DrawerLayout + NavigationView
实现:android.support.v4.widget.DrawerLayout + android.support.design.widget.NavigationView
NavigationView 常用属性:
属性名 | 作用 |
---|---|
app:headerLayout="" | 头布局 |
app:menu="" | 菜单布局 |
android:layout_gravity="left" | 在父视图中的布局 |
android:fitSystemWindows="true" | 适应系统 |
- build.gradle(Module:app) : 导入Material Design Library
dependencies {
...
compile 'com.android.support:design:25.0.1'
compile 'com.jaeger.statusbaruitl:library:1.2.7'
}
- activity_main.xml : DrawerLayout和NavigationView布局
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v4.widget.DrawerLayout
android:id="@+id/drawerLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<!--你的布局-->
<android.support.design.widget.NavigationView
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/nv_main"
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:clickable="true"
android:fitsSystemWindows="true" />
</android.support.v4.widget.DrawerLayout>
</RelativeLayout>
- navigation_view.xml : 侧滑菜单头布局
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="163dp"
android:background="@drawable/navigation_header"
android:orientation="vertical"
android:theme="@style/ThemeOverlay.AppCompat.Dark">
</LinearLayout>
- drawer_view.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/nav_home"
android:icon="@drawable/tab_design_selected"
android:title="Design01"/>
<item
android:id="@+id/nav_messages"
android:icon="@drawable/tab_design_selected"
android:title="Design02"/>
<item
android:id="@+id/nav_friends"
android:icon="@drawable/tab_design_selected"
android:title="Design03"/>
<item
android:id="@+id/nav_discussion"
android:icon="@drawable/tab_design_selected"
android:title="Design04"/>
</group>
<item android:title="More">
<menu>
<item
android:icon="@drawable/tab_design_selected"
android:title="Design05"/>
<item
android:icon="@drawable/tab_design_selected"
android:title="Design06"/>
</menu>
</item>
</menu>
- MainActivity.java : 设置侧滑菜单选项点击事件
public void initDrawer(){
drawerLayout = (DrawerLayout) findViewById(R.id.drawerLayout);
StatusBarUtil.setColorNoTranslucentForDrawerLayout(MainActivity.this,
drawerLayout, getResources().getColor(R.color.colorPrimaryDark));
navigationView = (NavigationView) findViewById(R.id.navigationView);
navigationView.setNavigationItemSelectedListener(
new NavigationView.OnNavigationItemSelectedListener() {
@Override
public boolean onNavigationItemSelected(MenuItem menuItem) {
drawerLayout.closeDrawers();
if (menuItem.getTitle().equals("Design01")){
// 选择Design01
}
else if (menuItem.getTitle().equals("Design02")){
// 选择Design02
}
else if (menuItem.getTitle().equals("Design03")){
// 选择Design03
}
else if (menuItem.getTitle().equals("Design04")){
// 选择Design04
}
else if (menuItem.getTitle().equals("Design05")){
// 选择Design05
}
else if (menuItem.getTitle().equals("Design06")){
// 选择Design06
}
return false;
}
});
}
4. FloatingActionButton
实现:android.support.design.widget.FloatingActionButton
常用属性:
属性名 | 作用 |
---|---|
app:backgroundTint="" | 背景色 |
app:fabSize="" | 包裹图片类型 |
app:elevation="" | Z轴,用来控制控件阴影效果 |
app:rippleColor="" | 按钮点击时颜色效果 |
app:layout_anchor="" | 显示位置的锚点 |
app:layout_anchorGravity="" | 按钮在锚点处的位置 |
- build.gradle(Module:app) : 导入Material Design Library
dependencies {
...
compile 'com.android.support:design:25.0.1'
}
- activity_main.xml : 悬浮按钮的布局
<android.support.design.widget.FloatingActionButton
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:src="@drawable/titlebar_search"
android:clickable="true"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_marginBottom="130dp"
android:layout_marginRight="30dp"/>
5. TextInputLayout
实现:android.support.design.widget.TextInputLayout
- build.gradle(Module:app) : 导入Material Design Library
dependencies {
...
compile 'com.android.support:design:25.0.1'
}
- activity_main.xml : 输入框的布局
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp"
android:orientation="vertical">
<android.support.design.widget.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="16sp"
android:hint="Phone"
android:inputType="phone"/>
</android.support.design.widget.TextInputLayout>
<android.support.design.widget.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp">
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="16sp"
android:hint="Password"
android:inputType="textPassword"/>
</android.support.design.widget.TextInputLayout>
</LinearLayout>
6.Snackbar
实现:android.support.design.widget.Snackbar
- build.gradle(Module:app) : 导入Material Design Library
dependencies {
...
compile 'com.android.support:design:25.0.1'
}
- MainActivity.java : 点击弹出Snackbar
Snackbar.make(view, "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();
7.CoordinatorLayout
实现:android.support.design.widget.CoordinatorLayout
<android.support.design.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- Your Scrollable View -->
<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
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.Toolbar
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:background="@color/colorPrimary"
android:layout_height="56dp"
app:contentInsetStart="0.0dp"
app:layout_scrollFlags="scroll|enterAlways">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/app_name"
android:textColor="#fff"
android:textSize="16sp"
android:gravity="center_horizontal"/>
</android.support.v7.widget.Toolbar>
<android.support.design.widget.TabLayout
android:id="@+id/tabLayout"
android:layout_width="match_parent"
android:layout_height="45dp"
app:tabTextColor="#666"
app:tabSelectedTextColor="@color/colorPrimary" />
</android.support.design.widget.AppBarLayout>
</android.support.design.widget.CoordinatorLayout>
其中,一个可以滚动的组件,例如RecyclerView、ListView(这里需要注意的是,貌似只支持RecyclerView、ListView,如果你用一个ScrollView,是没有效果的)。如果:
1、给这个可滚动组件设置了layout_behavior
2、给另一个控件设置了layout_scrollFlags
那么,当设置了layout_behavior的控件滑动时,就会触发设置了layout_scrollFlags的控件发生状态的改变。
设置的layout_scrollFlags的选项:
- scroll - 想滚动就必须设置这个。
- enterAlways - 实现quick return效果, 当向下移动时,立即显示View(比如Toolbar)。
- exitUntilCollapsed - 向上滚动时收缩View,但可以固定Toolbar一直在上面。
- enterAlwaysCollapsed - 当你的View已经设置minHeight属性又使用此标志时,你的View只能以最小高度进入,只有当滚动视图到达顶部时才扩大到完整高度。
PS : 所有使用scroll flag的view都必须定义在没有使用scroll flag的view的前面,这样才能确保所有的view从顶部退出,留下固定的元素。
8.CollapsingToolbarLayout
实现:android.support.design.widget.CollapsingToolbarLayout
<android.support.design.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- Your Scrollable View -->
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
<android.support.design.widget.AppBarLayout
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
<android.support.design.widget.CollapsingToolbarLayout
android:id="@+id/collapsingToolbarLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
app:contentScrim="@color/colorPrimary"
app:expandedTitleMarginEnd="64dp"
app:expandedTitleMarginStart="32dp"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<ImageView
android:id="@+id/backdrop"
android:layout_width="match_parent"
android:layout_height="200dp"
android:fitsSystemWindows="true"
android:scaleType="centerCrop"
android:src="@drawable/navigation_header"
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"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
</android.support.design.widget.CoordinatorLayout>
我们在CollapsingToolbarLayout中设置了一个ImageView和一个Toolbar。并把这个CollapsingToolbarLayout放到AppBarLayout中作为一个整体。
1、在CollapsingToolbarLayout中:
- 设置了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也一直会固定在最顶端。
PS:使用CollapsingToolbarLayout时必须把title设置到CollapsingToolbarLayout上,设置到Toolbar上不会显示。即:
- 设置标题:mCollapsingToolbarLayout.setTitle(" ");
- 设置扩张时候的title颜色:mCollapsingToolbarLayout.setExpandedTitleColor();
- 设置收缩后在Toolbar上显示时的title的颜色:mCollapsingToolbarLayout.setCollapsedTitleTextColor();