作者:李旺成###
时间:2016年4月14日###
接上篇 AndroidStudyDemo 之 Android5.x 新 API 介绍(二)
今天主要给大家介绍下 Android 5.x 中提供的新控件(请不要纠结“控件”的定义)。
一、 RippleDrawable
简介
Android 5.x 提供了视图的水波纹效果,就是使用 RippleDrawable 实现的,看看 RippleDrawable 到底是什么。
- 自定义触摸反馈动画
- 以波纹效果来显示状态变化的 Drawable
- <ripple> 标签即对应一个 RippleDrawable
简单使用
RippleDrawable 其实就可以当作一个 Drawable,这里主要介绍如何使用,不谈论它的实现原理之类的。
1、没有边界的 Ripple(Ripple With No Mask)
先看效果:
很简单,就是在 drawable 文件夹中定义一个以 <ripple> 为根控件的 xml 文件,然后作为一个控件的背景即可。
定义 ripple_blue.xml :
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="#0000cc">
</ripple>
作为 TextView 的 background:
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/ripple_blue"
android:clickable="true"
android:gravity="center"
android:text="Ripple With No Mask" />
2、用颜色作为 Mask 的 Ripple(Ripple With Color Mask)
先看效果:
从上面的效果图可以看到,分别使用不同的颜色,但是好像没有看到效果;是的,用颜色作的 Mask,在这个 Mask 中颜色并没有起作用。但是可以看到与 1、 中的效果差别是很明显的,那就是这个 Mask 限定了边界。
先看代码,
ripple_blue_with_white_mask.xml:
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="#0000cc" >
<item android:id="@android:id/mask"
android:drawable="@android:color/white" />
</ripple>
ripple_blue_with_black_mask.xml:
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="#0000cc">
<item android:id="@android:id/mask"
android:drawable="@android:color/black" />
</ripple>
这里解释一下:如果在一个 ripple 标签中,添加一个 item,其 id 为@android:id/mask,drawable 属性为引用的颜色(color) ,则水波效果会限定在 drawable 对应的 RippleDrawable 本身矩形区域内部。
3、用图片作为 Mask 的 Ripple(Ripple With Picture Mask)
先看效果:
看代码,
ripple_blue_with_pic_mask.xml:
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="#0000cc" >
<item android:id="@android:id/mask"
android:drawable="@mipmap/icon_folder_r" />
</ripple>
这里解释一下:如果在一个 ripple 标签中,添加一个 item,其id为@android:id/mask,drawable 属性为引用的图片(png,jpg),则水波效果会限定在图片 drawable 中非透明部分对应的区域内部。
4、用设定形状作为 Mask 的 Ripple(Ripple With Shape Mask)
先看效果:
上代码
首先,要定义一个形状(shape),shape.xml:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#6666ff"/>
<corners
android:bottomRightRadius="100dp"/>
</shape>
然后,在 ripple 中使用形状,ripple_blue_with_shape_mask.xml:
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="#0000cc" >
<item
android:id="@android:id/mask"
android:drawable="@drawable/shape"/>
</ripple>
这里解释一下:如果在一个ripple标签中,添加一个 item,其id为@android:id/mask,drawable 属性为引用的形状(shape) ,则水波效果会限定在 shape 对应的区域内部。
5、搭配selector作为Ripple(Ripple With Selector)
先看效果:
上代码,ripple_blue_with_selector.xml:
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="#0000cc" >
<item>
<selector>
<item
android:drawable="@mipmap/icon_folder_i"
android:state_pressed="true">
</item>
<item
android:drawable="@mipmap/icon_folder_r"
android:state_pressed="false">
</item>
</selector>
</item>
</ripple>
解释一下:如果在一个 ripple 标签中,添加一个 item,在 item 的内部写上 <selector> 标签,那么这个 RippleDrawable 在按下的时候,同时具有水波效果和 selector 指定的图层。
注意事项
为了兼容可以将 <ripple> 元素定义在 drawable-v21 目录下。关于兼容,不是本文重点,以后专门讨论下。
说明:上面的示例 Demo 和描述参考自 Android L Ripple 的使用
这个 Demo 已经将简单的使用场景覆盖,当然可以组合出各种更丰富的效果,发挥你的想象力去试试吧。
二、 Toolbar
Toolbar 简介
Toolbar 是应用程序内容中使用的标准工具栏,可以说是** ActionBar 的升级版**。
Toolbar 与 ActionBar 并不是相互独立的,要使用 Toolbar 还是得跟 ActionBar 扯上关系的。相比 Actionbar,Toolbar 最明显的一点就是变得很自由,可随处放置。因为它是作为一个 ViewGroup 来定义使用的,所以单纯使用 ActionBar 已经稍显过时了,它的一些方法已被标注过时。
看一下官方文档上对 Toolbar 的介绍:
如上图所示,Toolbar 继承自 ViewGroup,这个熟悉。再来看看 ActionBar:
ActionBar 继承的是 Object,就只从继承结构来看,我都会觉得 Toolbar 会更灵活(或者说强大),确实如此。来,瞄一眼 Toolbar 提供的一些方法:
Toolbar 的简单使用
先看看 Toolbar 的使用效果:
Toolbar 的使用很简单,我没用过 ActionBar,很多人都说用过 ActionBar 再来使用 Toolbar 就很容易了。哈哈,我想告诉你,如果没用过 ActionBar 就不用去看 ActionBar 了,直接使用 Toolbar 即可,这个使用也很简单。
Toolbar 的使用可以采用如下步骤:
1、定义使用 Toolbar 的主题(Theme)
将 res/values/styles.xml 中原来新建项目时所生成的默认主题,修改为继承自 Theme.AppCompat(可以直接继承 Theme.AppCompat.Light.NoActionBar,这样就需要再去除 ActionBar 了,当然也可以手动去除),然后,设置相关属性。
这里只是为了演示如何使用 Toolbar,所以先把原本的 ActionBar 隐藏起来。
看代码:
<!-- 去掉 ActionBar,使用 Toolbar 的主题 -->
<style name="AppTheme.DIYToolbar" parent="Theme.AppCompat">
<!-- toolbar(actionbar)颜色 -->
<item name="colorPrimary">@color/colorPrimary</item>
<!-- 状态栏颜色 -->
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
<!-- 各控制元件(如:check box、switch 或是 radoi) 被勾选 (checked) 或是选定 (selected) 的颜色 -->
<item name="colorAccent">#003300</item>
<!-- 各控制元件的预设颜色 -->
<item name="colorControlNormal">#6600ff</item>
<!-- 导航栏的背景色 -->
<item name="android:navigationBarColor">#330099</item>
</style>
调整 Menu 位置
<!-- Toolbar 定制菜单的样式 -->
<style name="AppTheme.DIYToolbar2" parent="Theme.AppCompat.Light.NoActionBar">
<!-- toolbar(actionbar)颜色 -->
<!-- App bar 上的标题与更多菜单中的文字颜色 -->
<item name="colorPrimary">#ff6600</item>
<!-- 状态栏颜色 -->
<item name="colorPrimaryDark">#ff9900</item>
<!-- 窗口的背景颜色 -->
<item name="android:windowBackground">@android:color/white</item>
<!-- 溢出菜单 -->
<!-- Required for Lollipop. -->
<item name="android:actionOverflowMenuStyle">@style/DIYMenuStyle</item>
</style>
<!-- 溢出菜单样式 -->
<style name="DIYMenuStyle" parent="@style/Widget.AppCompat.Light.PopupMenu.Overflow">
<!-- Required for Lollipop. -->
<item name="android:overlapAnchor">false</item>
<item name="android:dropDownVerticalOffset">10dp</item>
<item name="android:popupBackground">#ff6600</item>
</style>
注意:在设置 style 时使用兼容库中的 Toolbar 与使用 Android 5.x 中提供的 Toolbar还是稍有区别的,我这里演示的是 Android 5.x 中的 Toolbar。如果你自己尝试的时候和我演示的效果不一致时,请确认你使用的是哪个 Toolbar,以及自定义 Theme 下的 item 的 name 与我演示使用的是否是一致的。
2、使用主题
关于 Theme 的作用范围,这里不做赘述。我的 Demo 项目中为了演示不同主题的效果,以及不破坏其他的页面,是直接为单个 Activity 应用该主题,当然在实际项目中一般都是直接应用在 app 上。
看代码:
<activity
android:name=".newwidget1.ToolbarActivity"
android:theme="@style/AppTheme.DIYToolbar" />
<activity android:name=".newwidget1.Toolbar2Activity"
android:theme="@style/AppTheme.DIYToolbar2"/>
3、在布局中加入 Toolbar
直接把 Toolbar 当成一个普通的 ViewGroup,它的使用很灵活,没有太多限制。这里做个简单的演示:
<Toolbar
android:id="@+id/tb_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:navigationIcon="@mipmap/btn_back"
android:logo="@mipmap/ic_logo"
android:title="@string/title_toolbar"
android:subtitle="@string/subtitle_toolbat"/>
上面用到的属性基本上可以见名知义,这里不打算细说,去试一下即可看到效果,稍后会小结一下常用属性的设置。
4、添加 Menu
Android 上的 Menu 生命力还是很顽强的,Toolbar 上的 Menu 个人感觉效果还可以,而且不少的 App 都有使用(当然大多都是 Meterial Design 风格的 App 在用)。来看看 Toolbar 中 Menu 的基本用法。
首先,使用 xml 定义 Menu:toolbar_menu.xml
(在代码中如何定义 Menu 可以参考 AndroidStudyDemo之Android4.x介绍中 PopupMenu 的使用)
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:id="@+id/menuitem0"
android:title="唐三藏"/>
<item
android:id="@+id/menuitem1"
android:title="孙悟空"/>
<item
android:id="@+id/menuitem2"
android:title="猪八戒"/>
<item
android:id="@+id/menuitem3"
android:title="沙和尚"/>
</menu>
这里为了演示动态改变 Toolbar 还定义了一个 toolbar_menu2.xml,如下:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:id="@+id/menuitem_new"
android:title="New"/>
<item
android:id="@+id/menuitem_open"
android:title="open"/>
</menu>
然后,在 Activity 中填充 Menu:
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// 使用 xml 定义的 Menu 文件,填充 Menu
getMenuInflater().inflate(R.menu.toolbar_menu, menu);
return true;
}
最后,为 Toolbar 的 Menu 按钮设置监听:
this.mTitleTB.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
Toast.makeText(getApplicationContext(), item.getTitle(), Toast.LENGTH_SHORT).show();
return false;
}
});
5、代码中使用 Toolbar
首先,初始化 Toolbar,并将 ActionBar 替换为 Toolbar:
this.mTitleTB = (Toolbar) findViewById(R.id.tb_title);
// 使用 setActionBar() 方法,取代原来的 ActionBar
setActionBar(mTitleTB);
然后,设置 Toolbar:
(这不是必要的,你完全可以在布局文件中定义这些属性,这里仅仅是为了演示如何在代码中设置 Toolbar)
private void changeToolbar() {
this.mTitleTB.setTitle("ChangeToolbar");
this.mTitleTB.setSubtitle("ToolbarSubtitle");
this.mTitleTB.setNavigationIcon(R.mipmap.btn_back_lightblue);
this.mTitleTB.setLogo(R.mipmap.ic_launcher);
// Menu 也可以替换
mToolbarMenu.clear();
getMenuInflater().inflate(R.menu.toolbar_menu2, mToolbarMenu);
}
综合使用
这里以 Toolbar+DrawerLayout 实现抽屉菜单为例,演示这两个控件的搭配,先看效果:
在效果图中可以看到,左侧菜单滑出和收起的时候,Toolbar 左边的菜单图标带有一个挺好看的动画。这是 Toolbar 和 DrawerLayout搭配的效果,实现很简单。
实现步骤:
1、定义 Toolbar 布局:include_toolbar.xml
<?xml version="1.0" encoding="utf-8"?>
<!-- 为了和 DrawerLayout 搭配,这里使用了兼容包中的 Toolbar -->
<android.support.v7.widget.Toolbar xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/toolbar"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:minHeight="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:logo="@mipmap/ic_logo"
android:title="@string/title_toolbar"
android:subtitle="@string/subtitle_toolbat">
</android.support.v7.widget.Toolbar>
2、添加动作菜单:toolbar_menu.xml
这个,上面已经说过了,不再赘述。
3、在布局中添加 Toolbar 和 DrawerLayout:activity_toolbardrawerlayout.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="match_parent"
android:orientation="vertical">
<include layout="@layout/include_toolbar"></include>
<android.support.v4.widget.DrawerLayout
android:id="@+id/dl_left"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
<!-- 内容界面 -->
<FrameLayout
android:id="@+id/fl_containor"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<!-- 菜单界面 -->
<LinearLayout
android:layout_width="270dp"
android:layout_height="match_parent"
android:layout_gravity="start"
android:background="#66ccff"
android:orientation="vertical">
<ListView
android:id="@+id/lv_menu"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp" />
</LinearLayout>
</android.support.v4.widget.DrawerLayout>
</LinearLayout>
4、设置 NoActionBar 主题,初始化 Toolbar
这个也说过了,注意一点,这里的 Toolbar 为 Support V7 中提供的,所以,要调用 setSupportActionBar() 让 Toolbar 替代 ActionBar。
5、绑定 Toolbar 与 DrawerLayout 状态
ActionBarDrawerToggle drawerToggle = new ActionBarDrawerToggle(
this,
mDrawerLayout,
mToolbar,
R.string.open,
R.string.close);
drawerToggle.syncState();
mDrawerLayout.addDrawerListener(drawerToggle);
6、初始化抽屉菜单
private void initLeftMenu() {
ListView menuList = (ListView) findViewById(R.id.lv_menu);
menuList.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1
, new String[]{"Menu Item 1","Menu Item 2","Menu Item 3"}));
menuList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Fragment fragment = new ContentFragment();
Bundle bundle = new Bundle();
bundle.putString("title", ((TextView) view).getText().toString());
fragment.setArguments(bundle);
mFm.beginTransaction().replace(R.id.fl_containor, fragment).commit();
mDrawerLayout.closeDrawer(Gravity.LEFT);
}
});
}
7、初始化内容视图
private void initContentView() {
Fragment fragment = new ContentFragment();
Bundle bundle = new Bundle();
bundle.putString("title","Menu Item 1");
fragment.setArguments(bundle);
mFm.beginTransaction().replace(R.id.fl_containor,fragment).commit();
}
代码很简单,其他的就不在此罗列了,具体请参考源码。(该示例参考自:android 5.X Toolbar+DrawerLayout实现抽屉菜单)
注意事项
我在使用 Toolbar 的过程中遇到过一些问题,在这里列一下,希望你不要再踩同样的坑。
1、不同 Toolbar 的区别
如果你是用的是兼容包中的 Toolbar,上面的演示代码有几处需要修改。
第一,是 Theme 的修改(这里只列举一个示例,其他属性类似):
<!-- Required for pre-Lollipop. -->
<item name="overlapAnchor">false</item>
<!-- Required for Lollipop. -->
<item name="android:overlapAnchor">false</item>
注意:这里如果设置的不对,会发现 Menu 的显示位置死活改不过来。
第二,在 /res/values-v21/styles.xml 中定义同样的 Theme
第三,在 Activity 中替换 ActionBar(当然这个写错了,编译通不过)
// 使用 support v7 里的 Toolbar 替换 ActionBar
setSupportActionBar(toolbar);
// 使用 Android 5.x 的 Toolbar 替换 ActionBar
setActionBar(toolbar);
2、不同的手机(ROM)对 Toolbar 的支持稍有区别
Toolbar 的属性全部在布局中设置,在不同的手机的显示效果,如下图:
在 MIUI 上,布局中设置的 logo 准确的展示出来了,在中兴的手机上,却没有展示出来。当然在代码中设置的话,效果都是一致的。
这里我没有去深究原因了,姑且认为是不同 ROM 对 Toolbar 的支持不同吧!
Toolbar 使用属性小结
下图总结了使用 Toolbar 中涉及到的常用属性:
下面就上图所标示出来的做个简要说明:
在布局中设置:
- navigationIcon
导航图标 - logo
Logo图标 - title
标题 - subtitle
副标题 - setOnMenuItemClickListener
设置 Menu 条目点击监听
在 style 的属性中设置:
- colorPrimaryDark
状态栏背景色 - colorPrimary
textColorPrimary
App bar 上的标题与更多菜单中的文字颜色
App bar 的背景色
在 style 中的 colorPrimary 设定
Toolbar 的背景色
在layout文件中设置 background 属性 - colorAccent
各控制元件(如:check box、switch 或是 radoi) 被勾选 (checked) 或是选定 (selected) 的颜色 - colorControlNormal
各控制元件的预设颜色 - windowBackground
App 的背景色 - navigationBarColor
导航栏的背景色,但只能用在 API Level 21 (Android 5) 以上的版本
进阶
也不是什么真的进阶,上面演示了 Toolbar 的基本使用,这样使用,在页面少的时候没什么问题;但是如果页面一多,就得为每个页面都拷贝一份同样的 Toolbar 设置,当然你可以使用 <include>,但总归不方便。
考虑到上述原因,就有人想到对 Toolbar 进行封装,使用模版模式把这些重复代码都封装起来。
具体实现这里就不讨论了,你要是感兴趣可以参考 Android ToolBar 的简单封装。原理和实现都比较简单,有兴趣可以自己试试。
三、Material Theme
简介
关于 Android 上的 Theme 本来是不想现在就讨论的,但是考虑到 Android 5.x 出现了个 Material 主题,所以在这里简单说一下。先看下效果:
官方提供的 Materal Theme
我们自定义的 Materal Theme 都需要(直接或者间接)继承自官方提供的 Materal Theme,Google 提供了如下三种:
- @android:style/Theme.Material
- @android:style/Theme.Material.Light
- @android:style/Theme.Material.Light.DarkActionBar
与之对应的 Compat Theme(兼容包中提供):
- Theme.AppCompat
- Theme.AppCompat.Light
- Theme.AppCompat.Light.DarkActionBar
分别对应上图所示的三种效果。
Material Theme 简单使用##
其实上面讲 Toolbar 的时候就已经在使用。
1、将自定义主题继承自 android:Theme.Material/Material.Light/Material.Light.DarkActionBar
说明:也可以用兼容包中提供的主题,上面介绍了 Android 5.x 与兼容包中 Materal Theme 的对应关系。
<style name="DIYMaterialTheme" parent="android:Theme.Material">
<!-- 自定义设置 -->
</style>
2、个性化颜色基调
<style name="DIYMaterialTheme" parent="android:Theme.Material">
<!-- 个性化颜色基调 -->
<item name="colorPrimary">#336600</item>
<item name="colorPrimaryDark">#339900</item>
<item name="colorAccent">#993300</item>
</style>
说明:
- colorPrimary:指定 ActionBar/Toolbar 的颜色
- colorPrimaryDark:指定状态栏的颜色
- colorAccent:指定 EditText 编辑时、RadioButton选中、CheckBox等选中时的颜色
3、使用自定义主题
<!-- 为 App 指定主题 --->
<application
android:name=".DIYApp"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/DIYMaterialTheme"/>
...
</application>
<!-- 为 Activity 指定主题 -->
<activity android:name=".newwidget1.MaterialThemeActivity"
android:theme="@style/DIYMaterialTheme"/>
看图吧:
与 Toolbar 的属性介绍部分重合,不要介意...
注意事项
1、不要把兼容包中的 Material Theme 使用到 Activity 上
兼容包中的 Material Theme 是提供给 AppCompatActivity 使用的
2、兼容包中与 Android 5.x 中的 Material Theme 在 style 中设置有区别
如:
<style name="DIYAppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<!-- 使用兼容包中的 Material Theme -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
<style name="DIYMaterialTheme.Base" parent="android:Theme.Material.Light"/>
<style name="DIYMaterialTheme" parent="DIYMaterialTheme.Base">
<!-- 使用 Android 5.x 的 Material Theme -->
<item name="android:colorPrimary">#336600</item>
<item name="android:colorPrimaryDark">#339900</item>
<item name="android:colorAccent">#993300</item>
<item name="android:statusBarColor">#ccffff</item>
<item name="android:navigationBarColor">#330099</item>
</style>
提示:用错了轻则没有效果,重则程序崩溃。
3、不同的 ROM 对 Material Theme 的支持不同
主要表现在有些 ROM 去除了虚拟 navigationBar,使用实体按键,效果如上图:左侧为 小米 Note,右侧为 中兴。
项目地址
有同学建议思维导图的暗黑风格与 AndroidStudio 严重重合,审美疲劳了;所以,思维导图换了个风格,喜欢大家喜欢,有任何建议或意见都欢迎给我留言。
如果对你有用,那帮忙点个喜欢吧!
未完待续,下一篇将继续介绍 Android 5.x 的新控件,考虑到 RecyclerView 内容较多,会单独做一篇,所以计划中关于 Android 5.x 的新控件还有两篇。
附录
参考
Android L Ripple的使用
RippleDrawable 的简单使用
RippleDrawable
Android-Toolbar 使用
利用Toolbar替换ActionBar
深入辨析Android四大组件(九)——Activity之AppCompatActivity与toolbar的结合
android acitonbar(toolbar)自定义溢出菜单样式
android 5.X Toolbar+DrawerLayout实现抽屉菜单
Android ToolBar 的简单封装
Using the Material Theme
Android Support Library 22.1
Material design
Android 5.x Theme 与 ToolBar 实战
ANDROID L - Material Design详解(主题和布局)
Material Theme
Android Material Design-Using the Material Theme(使用Material主题)-(二)