Material Design技术分享
Material design 的核心思想是把物理世界的体验带进屏幕。去掉现实中的杂质和随机性,保留其最原始纯净的形态、空间关系、变化与过渡,配合虚拟世界的灵活特性,还原最贴近真实的体验,达到简洁与直观的效果。
Material design引入了z轴的概念,z轴垂直于屏幕,用来表现元素的层叠关系。z值(海拔高度)越高,元素离界面底层(水平面)越远,投影越重。这里有一个前提,所有的元素的厚度都是1dp。所有元素都有默认的海拔高度,对它进行操作会抬升它的海拔高度,操作结束后,它应该落回默认海拔高度。同一种元素,同样的操作,抬升的高度是一致的。
Toolbar简单使用
Toolbar结构 <android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:minHeight="?attr/actionBarSize"
app:contentInsetStartWithNavigation="0dp"
app:layout_collapseMode="pin"
app:logo="@drawable/ic_android_white_24dp"
app:navigationIcon="@drawable/ic_arrow_back_white_24dp"
app:popupTheme="@style/AppTheme.PopupOverlay"
app:subtitle="subtitle"
app:title="title">
// Logo
toolbar.setLogo(R.drawable.ic_android_white_24dp);
// 主标题
toolbar.setTitle("Title");
// 副标题
toolbar.setSubtitle("subtitle");
//设置toolbar
setSupportActionBar(toolbar);
//左边的小箭头(注意需要在setSupportActionBar(toolbar)之后才有效果)
toolbar.setNavigationIcon(R.drawable.ic_arrow_back_white_24dp);
//返回按钮的点击事件
mToolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
finish();
}
});
//菜单的点击事件
mToolbar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
switch (item.getItemId()){
case R.id.action_search:
Toast.makeText(ToolbarActivity.this, "search", Toast.LENGTH_SHORT).show();
break;
case R.id.action_share:
Toast.makeText(ToolbarActivity.this, "share", Toast.LENGTH_SHORT).show();
break;
case R.id.action_setting:
Toast.makeText(ToolbarActivity.this, "setting", Toast.LENGTH_SHORT).show();
break;
case R.id.action_about:
Toast.makeText(ToolbarActivity.this, "about", Toast.LENGTH_SHORT).show();
break;
default:
break;
}
return true;
}
});
设置Toolbar的theme,可以让弹窗不遮挡标题栏,处于标题栏的下方。
<item name="overlapAnchor">false</item>
Toolbar
AppBarLayout + CollapsingToolbarLayout + Toolbar的使用
- CollapsingToobbarLayout常用属性:
- app:contentScrim设置折叠时工具栏布局的颜色,默认是colorPrimary的色值
- app:statusBarScrim设置折叠时状态栏的颜色,默认是colorPrimaryDark的色值
- app:collapsedTitleGravity设置折叠时标题的对齐方式
- app:expandedTitleGravity设置展开时标题的对齐方式
- app:titleEnabled设置是否显示标题
- app:toolbarId设置关联的Toolbar的id
-
AppBarLayout是一种支持响应滚动手势的app bar布局(比如工具栏滚出或滚入屏幕),CollapsingToolbarLayout则是专门用来实现子布局内不同元素响应滚动细节的布局,CollapsingToolbarLayout是不能独立存在的,它必须只能作为AppBarLayout 的子布局来使用。
-
与AppBarLayout组合的滚动布局(Recyclerview、NestedScrollView等)需要设置app:layout_behavior="@string/appbar_scrolling_view_behavior"(上面代码中NestedScrollView控件所设置的)。没有设置的话,AppBarLayout将不会响应滚动布局的滚动事件。
-
AppBarLayout的子布局有5种滚动标识
- scroll:将此布局和滚动时间关联。这个标识要设置在其他标识之前,没有这个标识则布局不会滚动且其他标识设置无效。
- enterAlways:任何向下滚动操作都会使此布局可见。这个标识通常被称为“快速返回”模式。只要向下滑动,布局就完全展开。
- enterAlwaysCollapsed:假设你定义了一个最小高度(minHeight)同时enterAlways也定义了,那么view将在到达这个最小高度的时候开始显示,并且从这个时候开始慢慢展开,当滚动到顶部的时候展开完。向下滑动时,先显示ToolBar,当内容滑动都滑动完时,再显示图片。
- exitUntilCollapsed:当你定义了一个minHeight,此布局将在滚动到达这个最小高度的时候折叠。ToolBar不会被隐藏。
- snap:当一个滚动事件结束,如果视图是部分可见的,那么它将被滚动到收缩或展开。例如,如果视图只有底部25%显示,它将折叠。相反,如果它的底部75%可见,那么它将完全展开。
- CollapsingToolbarLayout的子布局有3种折叠模式。CollapsingToobarLayout折叠后的高度就是Toorbar的高度。
- none:这个是默认属性,布局将正常显示。布局会被滑动进去
- pin:CollapsingToolbarLayout折叠后,此布局将固定在顶部。
- parallax:CollapsingToolbarLayout折叠时,此布局也会有视差折叠效果。当CollapsingToolbarLayout的子布局设置了parallax模式时,我们还可以通过app:layout_collapseParallaxMultiplier设置视差滚动因子,值为:0~1。
CollasToolbarLayout + TabLayout的使用
TabLayout没有设置app:layout_collapseMode,在CollapsingToolbarLayout收缩时就不会消失。
CollapsingToolbarLayout收缩时的高度是Toolbar的高度,所以我们需要把Toolbar的高度增加,给TabLayout留出位置,这样收缩后TabLayout就不会和Toolbar重叠。
Toolbar的高度增加,title会相应下移。android:gravity="top"方法使Toolbar的title位于Toolbar的上方,然后通过app:titleMarginTop调整下title距顶部高度,这样Toolbar就和原来显示的一样了。
折叠状态
TabLayout悬停
项目中有时候会遇到这样的UI设计需求,标题 + 头部布局 + 可滑动切换的tab页 + ViewPager(里面的Fragment是可滑动的列表)。
可以利用AppBarLayout + CollapsingToolbarLayout可折叠的特性,把头部布局折叠起来,呈现出TabLayout在标题栏下方悬停的效果。
使用AppBarLayout + CollapsingToolbarLayout把头部布局包裹起来,这样 整个头部布局都可以折叠和展开,当头部布局全部折叠时,TabLayout就悬停在标题下面。
优点:实现简单,代码量少。只需要写XML文件就可以实现该效果,不需要额外写代码来处理滑动事件的冲突。
TabLayout展开状态
TabLayout悬停状态
CardView的使用
CardView继承自FramLayout。CardView常用属性:
- app:cardCornerRadius设置四格圆角的半径
- app:cardBackgroundColor设置背景色,使用android:background设置无效
- app:cardElevation设置阴影
- app:cardMaxElevation:设置阴影最大高度
- android:foreground="?android:attr/selectableItemBackground"设置点击的波纹效果
- app:contentPadding设备CardView内容的padding,用android:padding设置无效
- app:cardUseCompatPadding,在Android 5.0及以下的系统中,CardView会添加一个额外的padding来绘制阴影,但是在5.0以上的系统中是没有这个padding的,是直接绘制阴影。所以这个属性只对5.0以上的系统起作用,如果设置为true,则会在卡片与卡片之间增加额外的padding。
- app:cardPreventCornerOverlap,是否设置卡片和卡片里面内容的padding,只对5.0以下系统起作用,如果设置为false,content与圆角会重叠,圆角被覆盖。
BottomNavigationView
- 主要需要设置五个个属性
- app:itemBackground背景颜色
- app:itemTextColor文字颜色
- app:menu指定菜单文件
- app:layout_behavior设置滑动行为,可以设置BottomNavigationView上滑消失,下滑显示
<android.support.design.widget.BottomNavigationView
android:id="@+id/bottom_navigation"
style="@style/Widget.Design.BottomNavigationView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_gravity="bottom"
app:elevation="@dimen/dp_16"
app:itemBackground="@color/viewBackground"
app:itemIconTint="@drawable/selector_nav_item_color"
app:itemTextColor="@drawable/selector_nav_item_color"
app:layout_behavior="com.example.think.widget.BottomNavigationBehavior"
app:menu="@menu/bottom_navigation_main"/>
BottomNavigationView可以设置一个OnNavigationItemSelectedListener用来响应切换的动作
mBottomNavigation.setOnNavigationItemSelectedListener(item -> {
switch (item.getItemId()) {
case R.id.action_news:
mToolbar.setTitle(R.string.title_news);
NewsFragment newsFragment = NewsFragment.newInstance();
switchFragment(newsFragment, R.id.frame_layout);
break;
case R.id.action_photo:
mToolbar.setTitle(R.string.title_photo);
PictureFragment pictureFragment = PictureFragment.newInstance();
switchFragment(pictureFragment, R.id.frame_layout);
break;
case R.id.action_video:
mToolbar.setTitle(R.string.title_video);
VideoFragment videoFragment = VideoFragment.newInstance();
switchFragment(videoFragment, R.id.frame_layout);
break;
case R.id.action_media:
mToolbar.setTitle(R.string.title_media);
ChannelFragment channelFragment = ChannelFragment.newInstance();
switchFragment(channelFragment, R.id.frame_layout);
break;
default:
break;
}
return true;
});
- 使用BottomNavigationView的问题
当BottomNavigationView超过三个menu时(三个及三个以下是正常显示的),只有选中的menu会显示图片和文字,其他的menu都只显示图片。且宽度不是均匀分布的,选中的menu宽度比其他的大。
可以通过一个工具方法解决这个问题
public void disableShiftMode(BottomNavigationView navigationView) {
BottomNavigationMenuView menuView = (BottomNavigationMenuView) navigationView.getChildAt(0);
try {
Field shiftingMode = menuView.getClass().getDeclaredField("mShiftingMode");
shiftingMode.setAccessible(true);
shiftingMode.setBoolean(menuView, false);
shiftingMode.setAccessible(false);
for (int i = 0; i < menuView.getChildCount(); i++) {
BottomNavigationItemView itemView = (BottomNavigationItemView) menuView.getChildAt(i);
itemView.setShiftingMode(false);
itemView.setChecked(itemView.getItemData().isChecked());
}
} catch (NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}
}
底部导航栏
DrawerLayout + NavigationView
DrawerLayout一般有两个子布局,第一子布局是内容布局,第二个子布局为侧滑菜单的布局。也可以有三个布局,第一子布局是内容布局,第二、三个布局分别为左右的侧滑菜单。
NavigationView,可以自行填充头部布局和菜单布局,还可以再添加任意布局。
- app:headerLayout指定头布局的布局文件
- app:menu指定目录xml文件
-
android:layout_gravity,一定要设置这个属性,start表示从左边滑出,可以和Toolbar联动。end表示从右边滑出,不能和Toolbar联动。如果不设置这个属性,NavigationView只是一个普通的布局。
侧滑菜单