前言
作为Android 开发者,不仅要学习功能的实现,还需要定制用户的界面。如何制作一款符合大家审美的app是个根令人头疼的问题。
不过好在google在15发布了MD设计规范,帮助程序员设计更好的app。
MD概述
说明:本文参考资料极客学院 Material Design 中文版
翻译不是很可靠,也不及时。最好还是去谷歌查看原文。
我们挑战自我,为用户创造了崭新的视觉设计语言。与此同时,新的设计语言除了遵循经典设计定则,还汲取了最新的科技,秉承了创新的设计理念。这就是原质化设计(Material Design)
。这份文档是动态更新的,将会随着我们对 Material Design 的探索而不断迭代、升级。
目标
我们希冀创造一种新的视觉设计语言,能够遵循优秀设计的经典定则,同时还伴有创新理念和新的科技。
我们希望创造一种独一无二的底层系统,在这个系统的基础之上,构建跨平台和超越设备尺寸的统一体验。遵循基本的移动设计定则,同时支持触摸、语音、鼠标、键盘等输入方式。
设计原则
实体感就是(通过设计方式来表达)隐喻
通过构建系统化的动效和空间合理化利用,并将两个理念合二为一,构成了实体隐喻。与众不同的触感是实体的基础,这一灵感来自我们对纸墨的研究,但是我们相信,随着科技的进步,应用前景将不可估量。
实体的表面和边缘提供基于真实效果的视觉体验,熟悉的触感让用户可以快速地理解和认知。实体的多样性可以让我们呈现出更多反映真实世界的设计效果,但同时又绝不会脱离客观的物理规律。
光效、表面质感、运动感这三点是解释物体运动规律、交互方式、空间关系的关键。真实的光效可以解释物体之间的交合关系、空间关系,以及单个物体的运动。
鲜明、形象、深思熟虑
新的视觉语言,在基本元素的处理上,借鉴了传统的印刷设计——排版、网格、空间、比例、配色、图像使用——这些基础的平面设计规范。在这些设计基础上下功夫,不但可以愉悦用户,而且能够构建出视觉层级、视觉意义以及视觉聚焦。精心选择色彩、图像、选择合乎比例的字体、留白,力求构建出鲜明、形象的用户界面,让用户沉浸其中。
Material Design 设计语言强调根据用户行为凸显核心功能,进而为用户提供操作指引。
有意义的动画效果
动画效果(简称动效)可以有效地暗示、指引用户。动效的设计要根据用户行为而定,能够改变整体设计的触感。
动效应当在独立的场景呈现。通过动效,让物体的变化以更连续、更平滑的方式呈现给用户,让用户能够充分知晓所发生的变化。
动效应该是有意义的、合理的,动效的目的是为了吸引用户的注意力,以及维持整个系统的连续性体验。动效反馈需细腻、清爽。转场动效需高效、明晰。
所有的关于MD设计的有关规范都能在该文档里找到,下面我们跟随文档,看下一些符合MD设计的应用是如何做到简约大方的。
热门开源MD风格设计App
- 简影讯,简约精彩影讯。仓库地址
CoordinatorLayout + AppBarLayout + CollapsingToolbarLayout
关于CollapsingToolbarLayout的说明
CollapsingToolbarLayout作用是提供了一个可以折叠的Toolbar,它继承至FrameLayout,给它设置layout_scrollFlags,它可以控制包含在CollapsingToolbarLayout中的控件(如:ImageView、Toolbar)在响应layout_behavior事件时作出相应的scrollFlags滚动事件(移除屏幕或固定在屏幕顶端)。
所以我们尝试加一个ImageView在里面试试,同时添加
app:layout_scrollFlags="scroll|exitUntilCollapsed"
再加一个响应滑动的控件,除了RecyclerView还有一个NestedScrollView控件同理加上
app:layout_behavior="@string/appbar_scrolling_view_behavior"
布局文件:
<?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" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true" android:id="@+id/detail_content" tools:context="com.othershe.mdview.DetailActivity"> <android.support.design.widget.AppBarLayout android:id="@+id/app_bar" android:layout_width="match_parent" android:layout_height="200dp" android:fitsSystemWindows="true" android:theme="@style/AppTheme.AppBarOverlay"> <android.support.design.widget.CollapsingToolbarLayout android:id="@+id/toolbar_layout" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true" app:contentScrim="?attr/colorPrimary" <!---------------------------折叠--------------------------> app:layout_scrollFlags="scroll|exitUntilCollapsed"> <ImageView android:id="@+id/detail_img" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true" android:scaleType="fitXY" <!---------------------------折叠--------------------------> 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" app:popupTheme="@style/AppTheme.PopupOverlay" /> </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"> <WebView android:id="@+id/webview" android:layout_width="match_parent" android:layout_height="match_parent" /> </android.support.v4.widget.NestedScrollView> <android.support.design.widget.FloatingActionButton android:id="@+id/fab" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="16dp" android:src="@android:drawable/ic_menu_share" app:layout_anchor="@id/app_bar" app:layout_anchorGravity="bottom|end" /> </android.support.design.widget.CoordinatorLayout>
可以看到:
CollapsingToolbarLayout添加了两个子控件,由于CollapsingToolbarLayout本质是一个Fragmentlayout,所以层叠显示。同时设置ToolBar背景透明。关于返回箭头:
//使mToolbar取代原来的ActionBar setSupportActionBar(mToolbar); //设置mToolbar左上角显示返回图标 if (getSupportActionBar() != null) { getSupportActionBar().setDisplayHomeAsUpEnabled(true); } //设置返回图标的点击事件 mToolbar.setNavigationOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { finish(); } });
其他代码
//设置还没收缩时状态下字体颜色 mToolbarLayout.setExpandedTitleColor(Color.RED); //设置收缩后Toolbar上字体的颜色 mToolbarLayout.setCollapsedTitleTextColor(Color.WHITE); //设置image背景 mDetailImg.setBackgroundResource(R.mipmap.bg); mWebView.loadUrl("http://www.jianshu.com/"); mWebView.setWebChromeClient(new WebChromeClient() { @Override public void onReceivedTitle(WebView view, String title) { super.onReceivedTitle(view, title); //必须把title设置到CollapsingToolbarLayout上,设置到Toolbar上则不会显示 mToolbarLayout.setTitle(title); } });
结合CollapsingToolbarLayout,我们向上滑动时ImageView隐藏、Toolbar显示,向下滑动则反之,类似折叠展开的效果。
在CollapsingToolbarLayout中通过
app:contentScrim="?attr/colorPrimary"
属相设置Toolbar折叠到顶部的背景,同时设置了app:layout_scrollFlags="scroll|exitUntilCollapsed"
属相,其中scroll的=含义已经解释过了,exitUntilCollapsed的含义如下:exitUntilCollapsed: 滚动退出屏幕,最后折叠在顶端
同时AppBarLayout的高度需要时第一个固定值,这样CollapsingToolbarLayout就有了折叠的效果。
折叠后Toolbar可以固定在顶部是因为使用了
app:layout_collapseMode="pin"
属性,属性值除了pin还有一个parallax:
pin:固定模式,在折叠的时候最后固定在顶端
parallax:视差模式,在折叠时会有个视差折叠的效果同时在ImageView中使用了
app:layout_collapseMode="parallax"
属性,来产生视差渐变的效果,使用app:layout_collapseParallaxMultiplier="0.7"
可以控制视差的变化,属性值的范围是[0.0, 1.0],值越大视察越大。最后还需要在NestedScrollView中设置
app:layout_behavior="@string/appbar_scrolling_view_behavior
属性。CoordinatorLayout+FloatingActionButton
这个很简单,只要加入了FloatingActionButton即可响应
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/main_content" android:layout_width="match_parent" android:layout_height="match_parent"> <android.support.design.widget.AppBarLayout android:id="@+id/appbar" android:layout_width="match_parent" android:layout_height="wrap_content" android:theme="@style/AppTheme.AppBarOverlay"> ........................................ <android.support.design.widget.FloatingActionButton android:id="@+id/fab" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="end|bottom" android:layout_margin="16dp" android:src="@android:drawable/ic_menu_share" /> </android.support.design.widget.CoordinatorLayout>
代码
mFab.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Snackbar.make(mCoordinatorLayout, "点我分享哦!", Snackbar.LENGTH_SHORT).show(); } });
在Snackbar展示的时候FloatingActionButton上移,隐藏时下移。如果FloatingActionButton的父View是RelativeLayout或其它Container则FloatingActionButton不会上移而导致被Snackbar覆盖。
要做到我们上滑时FloatingActionButton会折叠消失,其实只需要如下两个属性就可以
app:layout_anchor="@id/app_bar" app:layout_anchorGravity="bottom|end"
第一个属性使FloatingActionButton显示在AppBarLayout的区域,
第二个属性确定显示的具体位置。<?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" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true" android:id="@+id/detail_content" tools:context="com.othershe.mdview.DetailActivity"> <android.support.design.widget.AppBarLayout android:id="@+id/app_bar" android:layout_width="match_parent" android:layout_height="200dp" android:fitsSystemWindows="true" android:theme="@style/AppTheme.AppBarOverlay"> ............................. </android.support.design.widget.AppBarLayout> ......................... <android.support.design.widget.FloatingActionButton android:id="@+id/fab" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="16dp" android:src="@android:drawable/ic_menu_share" <!--------------------------在app_bar中-------------------> app:layout_anchor="@id/app_bar" app:layout_anchorGravity="bottom|end" /> </android.support.design.widget.CoordinatorLayout>
用来替换yuanlaide
Toolbar
ToolBar的出现是为了替换之前的ActionBar的各种不灵活使用方式,相反,ToolBar的使用变得非常灵活,因为它可以让我们自由往里面添加子控件。我们可以单独的使用也可以代替原来的ActionBar。
替换ActionBar
- 使用Toolbar首先要隐藏原有的ActionBar,我们可以增加一个主题
<style name="AppTheme.NoActionBar"> <item name="windowActionBar">false</item> <item name="windowNoTitle">true</item> </style>
也可以扩展原有的主题,
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar"> <item name="colorPrimary">@color/colorPrimary</item> <item name="colorPrimaryDark">@color/colorPrimaryDark</item> <item name="colorAccent">@color/colorAccent</item> </style>
colorPrimary:对应ActionBar的颜色。
colorPrimaryDark:对应状态栏的颜色
colorAccent:对应EditText编辑时、FloatingActionButton背景等颜色。配置文件修改:
Android:theme=”@style/AppTheme”
- 在布局文件中的用法可以参考上边的代码,之后在java代码中得到Toolbar,通过
setSupportActionBar(mToolbar);
使Toolbar取代原本的ActionBar。
相关属性
<android.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary"> <!--添加Toolbar的子控件--> <Button android:id="@+id/btn_diy" android:layout_width="60dp" android:layout_height="wrap_content" android:layout_gravity="right" android:background="#80ffffff" android:text="自定义按钮" android:textColor="#000000" android:textSize="11sp" /> <TextView android:id="@+id/tv_title" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_gravity="center" android:gravity="center" android:text="首页" android:textColor="@android:color/black" android:textSize="20sp" /> </android.support.v7.widget.Toolbar>
?attr/actionBarSize
:表示根据屏幕的分辨率采用系统默认的高度关于图标以及其他设置:参考图片对应方法
mToolbar = (Toolbar) findViewById(R.id.toolbar); // App Logo // mToolbar.setLogo(R.drawable.app_icon); // 主标题,默认为app_label的名字 mToolbar.setTitle("Title"); mToolbar.setTitleTextColor(Color.YELLOW); // 副标题 mToolbar.setSubtitle("Sub title"); mToolbar.setSubtitleTextColor(Color.parseColor("#80ff0000")); //侧边栏的按钮 mToolbar.setNavigationIcon(R.drawable.back); //取代原本的actionbar setSupportActionBar(mToolbar); //设置NavigationIcon的点击事件,需要放在setSupportActionBar之后设置才会生效, //因为setSupportActionBar里面也会setNavigationOnClickListener mToolbar.setNavigationOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mToast.setText("click NavigationIcon"); mToast.show(); } }); //设置toolBar上的MenuItem点击事件 mToolbar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() { @Override public boolean onMenuItemClick(MenuItem item) { switch (item.getItemId()) { case R.id.action_edit: mToast.setText("click edit"); break; case R.id.action_share: mToast.setText("click share"); break; case R.id.action_overflow: //弹出自定义overflow popUpMyOverflow(); return true; } mToast.show(); return true; } }); //ToolBar里面还可以包含子控件 mToolbar.findViewById(R.id.btn_diy).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mToast.setText("点击自定义按钮"); mToast.show(); } }); mToolbar.findViewById(R.id.tv_title).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mToast.setText("点击自定义标题"); mToast.show(); } });
设置menu,以及item点击事件
//如果有Menu,创建完后,系统会自动添加到ToolBar上 @Override public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); getMenuInflater().inflate(R.menu.menu_main, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { int id = item.getItemId(); switch (id) { case R.id.menu_share: break; case R.id.menu_search: break; } return super.onOptionsItemSelected(item); }
NavigationView
NavigationView的就是抽屉,是隐藏在界面外的一个布局。
在MD设计规范里,主要的东西应该是显眼的,用户直达的。所以NavigationView只能放一些次要的东西。比如说明、设置等等布局文件
其实NavigationView就是用来实现我们常见的侧滑菜单的效果的。
在布局文件中的代码:<?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"> <include layout="@layout/content_main" android:layout_width="match_parent" android:layout_height="match_parent" /> <android.support.design.widget.NavigationView android:id="@+id/nav_view" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_gravity="start" android:fitsSystemWindows="true" app:menu="@menu/menu_drawer" app:headerLayout="@layout/nav_head_main" /> </android.support.v4.widget.DrawerLayout>
以DrawerLayout作为根布局,content_main作为主界面布局,最后是NavigationView,
通过android:layout_gravity="start"
设置为左侧滑菜单,
同理android:layout_gravity="end"
代表右侧滑菜单。
其中app:menu="@menu/menu_drawer" app:headerLayout="@layout/nav_head_main"
两个属性分别代表NavigationView的顶部header布局以及下边的menu item布局,具体可以参考源代码。
nav_head_main.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="180dp" android:background="#309393" android:gravity="center_vertical" android:orientation="horizontal" android:theme="@style/ThemeOverlay.AppCompat.Dark"> <ImageView android:id="@+id/nav_head_icon" android:layout_width="80dp" android:layout_height="80dp" android:layout_marginLeft="20dp" android:scaleType="centerCrop" /> <TextView android:id="@+id/nav_head_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="30dp" android:textSize="18sp" /> </LinearLayout>
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/nav_item1" android:icon="@android:drawable/ic_menu_compass" android:title="我的圈子" /> <item android:id="@+id/nav_item2" android:icon="@android:drawable/ic_menu_add" android:title="我的收藏" /> </group> <item android:title="其它"> <menu> <item android:id="@+id/nav_set" android:icon="@android:drawable/ic_menu_crop" android:title="设置" /> <item android:id="@+id/menu_share" android:icon="@android:drawable/ic_menu_share" android:title="分享" /> <item android:id="@+id/nav_about" android:icon="@android:drawable/ic_menu_help" android:title="关于" /> </menu> </item> </menu>
代码
准备好了布局文件,接下来就是初始化工作:
private void initNavigationView() { //初始化NavigationView顶部head的icon和name ImageView icon = (ImageView) mNavView.getHeaderView(0).findViewById(R.id.nav_head_icon); icon.setImageResource(R.mipmap.ic_launcher); TextView name = (TextView) mNavView.getHeaderView(0).findViewById(R.id.nav_head_name); name.setText("VipOthershe"); //设置NavigationView对应menu item的点击事情 mNavView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() { @Override public boolean onNavigationItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.nav_item1: break; case R.id.nav_item2: break; case R.id.nav_set: break; case R.id.menu_share: break; case R.id.nav_about: break; } //隐藏NavigationView mDrawerLayout.closeDrawer(GravityCompat.START); return true; } }); }
很简单,就是初始化NavigationView的顶部header和Menu Item。
接下来看一下如何将右滑菜单和Toolbar结合使用:
ActionBarDrawerToggle toggle = new ActionBarDrawerToggle( this, mDrawerLayout, mToolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close); mDrawerLayout.addDrawerListener(toggle); //设置左上角显示三道横线 toggle.syncState();
通过设置将两者绑定在一起,同时Toolbar左上角显示三道横线,左上角可打开左滑菜单。
还可以采用另外一种方式:
//设置Toolbar左上角图标 mToolbar.setNavigationIcon(R.mipmap.ic_launcher); mToolbar.setNavigationOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { mDrawerLayout.openDrawer(GravityCompat.START); } });
先设置Toolbar左上角图标,并绑定事件,通过
mDrawerLayout.openDrawer(GravityCompat.START);
打开左滑菜单。以上两个方法,分别对应打开和关闭左滑菜单:
mDrawerLayout.openDrawer(GravityCompat.START); mDrawerLayout.closeDrawer(GravityCompat.START);
如果使用右滑菜单则将GravityCompat.START改成GravityCompat.END,即可实现右滑菜单的开关。
效果图
侧滑菜单
网友评论