除了Google官方提供的组件,Material Design另一个非常重要的特性是它的视图效果,这些效果和官方的组件一起,为安卓app的交互提供了更好的素材
1.Elevation:高度
ImageView iv = findViewById(R.id.iv);
ViewOutlineProvider provider=new ViewOutlineProvider() {
@Override
public void getOutline(View view, Outline outline) {
outline.setOval(0, 0, view.getWidth(), view.getHeight());
}
};
iv.setOutlineProvider(provider);
ViewOutlineProvider是官方提供的一个抽象类,重写其中的方法可以轻松的改变基本的轮廓显示。同样在xml布局中,可以通过android:outlineProvider
来指定轮廓的判定方式。有四种:bounds,background,none,paddedBounds,系统默认为background。谷歌5.0之后推出了对矢量图的支持(Vector),借鉴了Web前端强大的自定义图形的特性。好处是很明显的:可以自适配、可代替不是十分复杂的PNG图片来减小体积、可将导出的标准SVG图像直接转化为PNG图像、除了静态图片还可以实现一些复杂的动画。
(1)实现静态图片:
通过查看官方文档,其实就是一个xml文件,根元素是vector
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="300dp"
android:height="300dp"
android:viewportHeight="40"
android:viewportWidth="40" >
<path
android:fillColor="#ff0040"
android:pathData="M20.5,9.5
c-1.955,0,-3.83,1.268,-4.5,3
c-0.67,-1.732,-2.547,-3,-4.5,-3
C8.957,9.5,7,11.432,7,14
c0,3.53,3.793,6.257,9,11.5
c5.207,-5.242,9,-7.97,9,-11.5
C25,11.432,23.043,9.5,20.5,9.5z" />
</vector >
上述代码定义的是一个桃心,几个标签的含义: android:width \ android:height:图片的宽高、android:viewportWidth\ android:viewportHeight:图片被划分的比例大小,比如这里就是将300dp划分为40份,后面path标签中的坐标就全部使用这里划分后的坐标系统。重点看path中的pathData标签,用SVG语法定义了画笔该怎么画出目标图形,而不用自定义View。支持的指令如下:
tips:坐标轴以(0,0)为中心,X轴水平向右,Y轴水平向下。所有指令大小写都可以,但是注意大写会参照全局坐标系,小写会参照父容器坐标系。指令和数据之间的空格可以省略。各种各样的SVG工具也可以帮我们导出图形,大概知道SVG语法含义即可。
<?xml version="1.0" encoding="utf-8"?>
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/vector_triangle" >
<target
android:name="v"
android:animation="@anim/path_morph" />
<!--android:name="v" 指定要驱动的vetordrawable内的group和path名称 -->
<!--android:animation="@anim/path_morph" 指定用哪个动画驱动该vectordrawable -->
</animated-vector >
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="64dp"
android:height="64dp"
android:viewportHeight="600"
android:viewportWidth="600" >
<path
android:name="v"
android:fillColor="#1a65cf"
android:pathData="M300,70 l 0,-70 70,70 0,0 -70,70z" />
</vector >
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" >
<objectAnimator
android:duration="5000"
android:propertyName="pathData"
android:valueFrom="M300,70 l 0,-70 70,70 0,0 -70,70z"
android:valueTo="M300,70 l 0,-70 70,0 0,140 -70,0 z"
android:valueType="pathType" >
</objectAnimator >
</set >
4.布局文件中使用该drawable的控件,点击后开启动画final View view = findViewById(R.id.view);
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Drawable background = view.getBackground();
if (background instanceof Animatable) {
((Animatable) background).start();
}
}
});
5.2 触摸反馈android:background="?android:attr/selectableItemBackground" 波纹有边界
public static Animator createCircularReveal(View view,
int centerX, int centerY, float startRadius, float endRadius) {
return new RevealAnimator(view, centerX, centerY, startRadius, endRadius);
}
五个参数,view:要操作的控件;centerX、centerY:动画开始的中心点坐标,相对view自身的;startRadius:开始半径;endRadius:结束时的半径。RecyclerView应该算是安卓5.0中引入的最重要的控件了,和ListView一样,它也是列表容器,同样要用适配器绑定数据源。谷歌设计此控件是为了能替代ListView,它和ListView相比有以下几个特点:
(1)都要使用到适配器。ListView一般我们定义一个适配器,继承自BaseAdapter。包括里面的convertView和ViewHolder都是谷歌建议我们的一种写法,可以提供条目重用节省内存。RecyclerView同样需要适配器,继承自其内部的一个抽象类Adapter,我们只需要实现onCreateViewHolder和onBindViewHolder这两个方法就可以。
(2)RecyclerView需要设置布局管理器,否则不会显示数据。因为ListView只提供了纵向列表的展现,而RecyclerView中引入了LayoutManager(布局管理器)的概念。可以根据列表所要展现的特性自己扩展该抽象类。API提供了三种布局管理器,一般就可以满足需求
(3)RecyclerView不再像ListView一样自带条目间的分隔线,需要自己实现ItemDecoration这个抽象类。第三方有很多各种各样的相关资料,现在对于竖直排列的布局,谷歌官方也提供了一个DividerItemDecoration,已经可以满足要求了,分隔线设计的过于复杂没有什么意义。
(4)列表的增删移改具有了动画效果,官方内置默认使用的动画实现是DefaultItemAnimator,另外提供了一个SimpleItemAnimator,同样你也可以自己实现RecyclerView内部的抽象类ItemAnimator来获取你要的效果。并且Adapter中提供了很多局部刷新的方法:notifyItemChanged、notifyItemInserted、notifyItemMoved、notifyItemRemoved,并且都带很自然的动画效果。不一定非使用notifyDataSetChanged来全局刷新。下例为添加和删除条目时的动画效果:
(5)不同于ListView,RecyclerView没有提供列表点击的回调事件setOnItemClickListener,可以在自定义的Adapter内部通过接口回调实现。不过我认为ViewHolder里面的成员变量itemView本身就代表了点击,我们可以将点击事件放到Adapter里面实现,不一定非要在组件上取得回调。
需要注意的是:在创建Adapter时,onCreateViewHolder方法中如果用LayoutInflater加载布局,这里的父容器不能再像ListView一样置为null。否则条目中的match_parent属性等并不会真正的和父容器(RecyclerView定义的布局)参数一致,而会根据当前内容自适应宽度。