1. Android5.0新特性
1.1. Material Design(MD)简介
Material Design材料设计,是的一种新的设计风格,谷歌希望寄由此来统一各种平台上的应用外观和用户体验。
官网介绍:
http://www.google.com/design/spec/material-design/introduction.html
1.2. 主题样式
l 系统自带的三个材料风格主题:
@android:style/Theme.Material (深色)
@android:style/Theme.Material.Light (亮色)
@android:style/Theme.Material.Light.DarkActionBar (亮色,actionbar为深色)
l 如何使用材料风格主题?
官方资料: http://developer.android.com/training/material/theme.html 或
file:///D:/Android/android-sdk-windows/docs/training/material/theme.html
颜色取色器:
http://www.materialpalette.com/
(1)定义主题:
<!-- api21以上版本生效 --> <style name="RedTheme" parent="android:Theme.Material"> <!-- 状态栏颜色,会被statusBarColor效果覆盖--> <item name="android:colorPrimaryDark">@color/status_red</item> <!-- 状态栏颜色,继承自colorPrimaryDark --> <item name="android:statusBarColor">@color/status_red</item> <!-- actionBar颜色 --> <item name="android:colorPrimary">@color/action_red</item> <!-- 窗口背景颜色 --> <item name="android:windowBackground">@color/window_bg_red</item> <!-- 底部导航栏颜色 --> <item name="android:navigationBarColor">@color/navigation_red</item> <!-- 前景色:ListView的分割线颜色,switch滑动区域色--> <item name="android:colorForeground">@color/fg_red</item> <!-- 背景色:popMenu的背景色 --> <item name="android:colorBackground">@color/bg_red</item> <!-- 控件默认颜色 ,效果会被colorControlActivated取代 --> <item name="android:colorAccent">@color/control_activated_red</item> <!-- 控件默认状态时的颜色 --> <item name="android:colorControlNormal">@color/control_normal_red</item> <!-- 控件按下时的颜色,会影响水波纹效果,继承自colorAccent --> <item name="android:colorControlHighlight">@color/control_highlight_red</item> <!-- 控件选中时(selected或checked)的颜色 --> <item name="android:colorControlActivated">@color/control_activated_red</item> <!-- Button的默认背景颜色 --> <item name="android:colorButtonNormal">@color/button_normal_red</item> <!-- Button,textView的文字颜色 --> <item name="android:textColor">@color/white_text</item> <!-- RadioButton checkbox等控件的文字 --> <item name="android:textColorPrimaryDisableOnly">@color/white_text</item> <!-- actionBar的标题文字颜色 --> <item name="android:textColorPrimary">@color/white_text</item> </style> |
(2)使用主题:(有两种方式)
a) 配置文件中配置
<application android:theme="@style/RedTheme"> |
b) 代码方式:
protected void onCreate(Bundle savedInstanceState) { setTheme(R.style.RedTheme); // 设置主题 super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } |
l 通过V7(部分)包兼容低版本
(1)添加support v7包
在build.gradle的dependencies节点下添加:
compile 'com.android.support:appcompat-v7:23.0.3' |
(2)使用Theme.AppCompat主题
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> |
(3)继承AppCompatActivity
界面不再继承 Activity, FragmentActivity 或ActionBarActivity, 而是继承AppCompatActivity, 目的是为了将MD的风格,
及Toolbar等新的特效兼容到低版本。
注意:V7包只是兼容了材料主题的部分功能,一些主题样式目前在低版本还无法使用
如:导航栏样式修改;
1.3. 高度与阴影
n 阴影大小Z = elevation + translationZ (elevation是相对于父控件的高度)
n 比父控件的高宽小,才能显示阴影
n 高度(阴影大小)决定了View的遮盖关系
阴影效果设置:
android:elevation="4dp" android:translationZ="4dp" |
1.4. 轮廓(outlineProvider属性)
n 指定控件的轮廓
// 默认为background android:outlineProvider="none|background|bounds|paddedBounds" |
n 若控件背景为半透明的shape或图片,则不会产生阴影。需要在代码中设置。
// 代码设置轮廓 circle1.setOutlineProvider(new ViewOutlineProvider() { public void getOutline(View view, Outline outline) { // 可以指定圆形,矩形,圆角矩形,path outline.setOval(0, 0, view.getWidth(), view.getHeight()); } }); // 代码设置轮廓:减少轮廓 circle2.setOutlineProvider(new ViewOutlineProvider() { public void getOutline(View view, Outline outline) { int padding = dp2px(5); outline.setOval(padding, padding, view.getWidth() - padding, view.getHeight() - padding); } }); |
1.5. 裁剪
n 裁剪视图可以轻松改变视图形状;
n 裁剪方法:
通过mView.clipToOutline(true)或android:clipToOutline="true"
将视图裁剪成轮廓指定形状
n 不是所有轮廓都可裁剪:
仅有矩形、圆形和圆角矩形轮廓支持裁剪,Path、椭圆无法进行裁剪;
可通过Outline.canClip()判断指定的轮廓是否可裁剪:
mTextView.setOutlineProvider(new ViewOutlineProvider() { public void getOutline(View view, Outline outline) { // 指定轮廓:圆形 outline.setOval(0, 0, view.getWidth(), view.getHeight()); // 指定轮廓:圆角矩形 // outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), dp2px(10)); cut2.setText("判断是否可裁剪: " + outline.canClip()); } }); mTextView.setClipToOutline(true); // 裁剪 |
1.6. 染色(Tint)
n 十六种PorterDuffXfermode(参考ApiDemo:graphics / xfermodes)
n 八种模式tint模式
android:background="@drawable/ring"
android:backgroundTint="@color/color_tint" // tint颜色
android:backgroundTintMode="add"// 染色模式,共8种模式
n 一张图片实现选择器:
按下的样式:drawable/ring_tint.xml
<?xml version="1.0" encoding="utf-8"?> <bitmap xmlns:android="http://schemas.android.com/apk/res/android" android:src="@drawable/ring" android:tint="#5677fc" android:tintMode="multiply" /> |
选择器的定义:
<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:drawable="@drawable/ring_tint" android:state_pressed="true" /> <item android:drawable="@drawable/ring" /> </selector> |
1.7. 调色板(Palette)
Palette:调色板,取色板,它是v7包中的类: android.support.v7.graphics.Palette
可以用来获取一张图片中的颜色值。
Vibrant 鲜艳的
Vibrant dark 鲜艳的暗色
Vibrant light 鲜艳的亮色
Muted 柔和的
Muted dark 柔和的暗色
Muted light 柔和的亮色
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.palette); // 对图片取色是一个比较消耗性能的操作,其内部会在子线程中运行。 Palette.from(bitmap).generate(new Palette.PaletteAsyncListener() { public void onGenerated(Palette palette) { v.setBackgroundColor(palette.getVibrantColor(Color.BLACK)); vd.setBackgroundColor(palette.getDarkVibrantColor(Color.BLACK)); vl.setBackgroundColor(palette.getLightVibrantColor(Color.BLACK)); m.setBackgroundColor(palette.getMutedColor(Color.BLACK)); md.setBackgroundColor(palette.getDarkMutedColor(Color.BLACK)); ml.setBackgroundColor(palette.getLightMutedColor(Color.BLACK)); } }); |
1.8. 矢量图
l SVG: 矢量图可以用一个根节点为svg的xml文件表示;
l w3矢量图文档: https://www.w3.org/TR/SVG11/paths.html
l 绘制矢量图: http://editor.method.ac/
l Svg --> vector:http://inloop.github.io/svg2android/
要在android中显示矢量图,需要把svg文件转换为vector文件
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:orientation="horizontal"> <ImageView android:layout_width="50dp" android:layout_height="50dp" android:background="@drawable/heart" /> <ImageView android:layout_width="200dp" android:layout_height="200dp" android:background="@drawable/heart" /> </LinearLayout> |
// drawable/heart.xml <?xml version="1.0" encoding="utf-8"?> <vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="100dp" android:height="100dp" android:viewportHeight="40" android:viewportWidth="40"> <path android:fillColor="#ff00ff" 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> |
1.9. 全新动画
在Material Design设计中,为用户与app交互反馈他们的动作行为和提供了视觉上的连贯性。Material主题为控件和Activity的过渡提供了一些默认的动画,在android L上,允许自定义这些动画:
Touch feedback 触摸反馈动画 (水波纹动画,涟漪动画)
Circular Reveal animation 圆形揭示动画
Path-based animations 基于路径的动画(Curved motion曲线运动)
View state changes 视图状态动画
Vector Drawables 矢量图动画
Activity transitions Activity转场动画
1.9.1. 触摸反馈Touch feedback
参见http://developer.android.com/reference/android/graphics/drawable/RippleDrawable.html
Button自带水波纹动画
1. 默认效果
2. android:background="?android:selectableItemBackground"
3. android:background="?android:selectableItemBackgroundBorderless"
全局的水波纹样式定义:
<item name="android:colorControlHighlight">@color/bg_red</item> |
按钮字母大写问题解决:
<item name="android:textAllCaps">false</item> |
单独给某个控件定义水波纹效果:
<?xml version="1.0" encoding="utf-8"?> <ripple xmlns:android="http://schemas.android.com/apk/res/android" android:color="@color/bg_red"> </ripple> |
1.9.2. 圆形揭示动画Circular Reveal animation
参见http://developer.android.com/training/material/animations.html#Reveal
ViewAnimationUtils.createCircularReveal(View, xO, yO, startR, endR);
// 例子1: Animator animator1 = ViewAnimationUtils.createCircularReveal(bt1, bt1.getWidth() / 2, bt1.getHeight() / 2, bt1.getWidth(), 0); animator1.setInterpolator(new LinearInterpolator()); animator1.setDuration(3000); animator1.start(); |
// 例子2: Animator animator2 = ViewAnimationUtils.createCircularReveal(bt2, 0, bt2.getHeight(), 0, (float) Math.hypot(bt2.getWidth(), bt2.getHeight())); animator2.setDuration(3000); animator2.start(); |
1.9.3. 基于路径的动画Path-based animations
参见 http://developer.android.com/training/material/animations.html#CurvedMotion
private void curved(){ Path path = new Path(); // 创建路径 path.moveTo(curved.getX(), curved.getY()); path.lineTo(dp2px(100), dp2px(100)); // a点 path.lineTo(dp2px(200), dp2px(100)); // b点 path.lineTo(dp2px(200), dp2px(200)); // c点 path.lineTo(dp2px(100), dp2px(200)); // d点 path.lineTo(dp2px(100), dp2px(100)); // a点 path.lineTo(curved.getX(), curved.getY()); ObjectAnimator mAnimator = ObjectAnimator.ofFloat(curved, View.X, View.Y, path); mAnimator.setDuration(4000); mAnimator.start(); } |
1.9.4. 状态动画View state changes
参见http://developer.android.com/training/material/animations.html#ViewState
// 例子1: <?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_pressed="true" android:drawable="@color/control_highlight_blue_grey"> <set> <objectAnimator android:propertyName="translationZ" android:duration="200" android:valueTo="10dp" android:valueType="floatType"/> <objectAnimator android:propertyName="scaleX" android:duration="200" android:valueTo="0.8" android:valueType="floatType"/> <objectAnimator android:propertyName="scaleY" android:duration="200" android:valueTo="0.8" android:valueType="floatType"/> </set> </item> <item android:state_pressed="false" android:drawable="@color/button_normal_blue_grey"> <set> <objectAnimator android:propertyName="translationZ" android:duration="200" android:valueTo="0" android:valueType="floatType"/> <objectAnimator android:propertyName="scaleX" android:duration="200" android:valueTo="1" android:valueType="floatType"/> <objectAnimator android:propertyName="scaleY" android:duration="200" android:valueTo="1" android:valueType="floatType"/> </set> </item> </selector> <TextView android:clickable="true" android:background="@drawable/state_anim" android:stateListAnimator="@drawable/state_anim"/> |
// 例子2: <?xml version="1.0" encoding="utf-8"?> <animated-selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@+id/pressed" android:drawable="@drawable/btn_check_15" android:state_pressed="true" /> <item android:id="@+id/normal" android:drawable="@drawable/btn_check_0" /> <transition android:fromId="@id/normal" android:toId="@id/pressed"> <animation-list> <item android:drawable="@drawable/btn_check_0" android:duration="100" /> <item android:drawable="@drawable/btn_check_1" android:duration="100" /> <item android:drawable="@drawable/btn_check_2" android:duration="100" /> ... </animation-list> </transition> </animated-selector> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/state_list" /> |
1.9.5. 矢量图动画Vector Drawables
参见http://developer.android.com/training/material/animations.html#AnimVector
1. 定义矢量图Vector,需要对路径设置名称
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="200dp" android:height="200dp" android:viewportHeight="1000" android:viewportWidth="1000"> <group android:name="rotation" android:pivotX="500.0" android:pivotY="500.0" android:rotation="0.0"> <path android:name="vector" android:fillColor="#00ffff" android:pathData="M67,750 L500,0 500,0 933,750 67,750 M146,853 A 0.1,500 0 0 1 146,148 M146,148 A 500,0.1 0 0 1 851,148 M851,148 A 0.1,500 0 0 1 851,853 M851,853 A 500,0.1 0 0 1 146,853" /> </group> </vector> |
2. 定义动画anim:
// vector_anim.xml: <?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android"> <objectAnimator android:duration="3000" android:propertyName="pathData" android:valueFrom="M67,750 L500,0 500,0 933,750 67,750 M146,853 A 0.1,500 0 0 1 146,148 M146,148 A 500,0.1 0 0 1 851,148 M851,148 A 0.1,500 0 0 1 851,853 M851,853 A 500,0.1 0 0 1 146,853" android:valueTo="M147,853 L147,147 853,147 853,853 147,853 M146,853 A 0.1,500 0 0 1 146,148 M146,148 A 500,0.1 0 0 1 851,148 M851,148 A 0.1,500 0 0 1 851,853 M851,853 A 500,0.1 0 0 1 146,853" android:valueType="pathType" /> <objectAnimator android:duration="3000" android:propertyName="pathData" android:startOffset="3000" android:valueFrom="M147,853 L147,147 853,147 853,853 147,853 M147,853 A 0.1,500 0 0 1 147,147 M147,147 A 500,0.1 0 0 1 853,147 M853,147 A 0.1,500 0 0 1 853,853 M851,853 A 500,0.1 0 0 1 147,853" android:valueTo="M147,853 L147,147 853,147 853,853 147,853 M147,853 A 500,500 0 0 1 147,147 M147,147 A 500,500 0 0 1 853,147 M853,147 A 500,500 0 0 1 853,853 M853,853 A 500,500 0 0 1 147,853" android:valueType="pathType" /> </set> |
3. 定义animated-vector
//res/drawable/vector_animation.xml <?xml version="1.0" encoding="utf-8"?> <animated-vector xmlns:android="http://schemas.android.com/apk/res/android" android:drawable="@drawable/vector_drawable" > <target android:name="vector" android:animation="@anim/vector_anim" /> <target android:name="rotation" android:animation="@anim/vector_rotation" /> </animated-vector> |
4. 应用到控件中:
<TextView android:id="@+id/vector_anim" android:layout_width="200dp" android:layout_height="200dp" android:background="@drawable/vector_animation" android:textColor="#88000000" /> public void vector(){ // 点击TextVew时执行此方法 Drawable drawable = vectorView.getBackground(); if (drawable instanceof Animatable) { ((Animatable) drawable).start(); } } |
1.9.6. 转场动画Activity transitions
https://developer.android.com/training/material/animations.html#Transitions
n 分类
Explode, Fade, Slide, 共享元素
n 转场动画实现步骤(Explode, Fade, Slide):
1. 设置窗口属性,开启此功能
protected void onCreate(Bundle savedInstanceState) { // 第一步: 支持转场切换,注意要放在setContentView()之前 getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS); } |
2. 构造Transition对象,设置进入退出动画
Transition transition = new Explode(); // Fade, Slide transition.setDuration(1000); getWindow().setEnterTransition(transition); // 启动时的动画 getWindow().setExitTransition(transition); // 进入其它界面时的动画 getWindow().setReenterTransition(transition); // 从其它界面返回时的动画 getWindow().setReturnTransition(transition); // 退出时的动画 |
3. 开启新的Activity
@TargetApi(Build.VERSION_CODES.LOLLIPOP) private void startAcitivityWithBundle(Class cls) { Intent intent = new Intent(activity, cls); ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(activity); startActivity(intent, options.toBundle()); } |
4. 在新的Activiy中也添加进窗口的动画效果,同步骤1、2
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS); Transition transition = new Explode(); transition.setDuration(1000); getWindow().setEnterTransition(transition); getWindow().setExitTransition(transition); getWindow().setReenterTransition(transition); getWindow().setReturnTransition(transition); } |
5. 在新的Activity中,使用finishAfterTransition()替代finish()来结束Acitivity
public void onFinish(View view) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { finishAfterTransition(); } else { finish(); } } |
n 带共享元素的转场动画实现(ActivityA启动ActivityB):
第一步:与上面第一步相同
getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS); |
第二步:禁用Activity窗口的切换动画
getWindow().setEnterTransition(null); getWindow().setExitTransition(null); getWindow().setReturnTransition(null); getWindow().setReenterTransition(null); |
第三步:在共享元素中配置transitionName属性
ActivityA中的共享元素配置: <ImageView android:transitionName="share_01"/> <TextView android:transitionName="share_02"/> ActivityB中的共享元素配置: <ImageView android:transitionName="share_01"/> <TextView android:transitionName="share_02"/> |
第四步:
@TargetApi(Build.VERSION_CODES.LOLLIPOP) private void startAcitivityWithBundle(Class cls) { Intent intent = new Intent(this, ShareActivity.class); ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation( this, ivImage, "share_01"); // ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(this, // new Pair(ivImage, "share_01"), // new Pair(tvText, "share_02")); startActivity(intent, options.toBundle()); } |
第五步:在新的ActivityB中,使用finishAfterTransition()替代finish()来结束Acitivity
public void onFinish(View view) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { finishAfterTransition(); } else { finish(); } } |
1.10. 新控件:RecyclerView CardView SwipeRefreshLayout
(1)RecyclerView 可回收视图
l 可以用来实现ListView、GridView、瀑布流,通过复用有限数量的View,来提高滚动时的性能。
l 三种布局管理器:
LinearLayoutManager 列表布局
GridLayoutManager 网格布局
StaggeredGridLayoutManager 瀑布流布局
l 适配器:需要继承RecyclerView.Adapter
l ViewHolder对象:需要继承RecyclerView.ViewHolder
l 没有类似ListView的setOnItemClickListener方法,
可以通过给item根节点设置点击事件的方式实现;
l Item设置点击事件后,按下时才显示水波纹效果;
(2)CardView 卡片视图
用来实现阴影和圆角效果,是FrameLayout的子类。
cardElevation:设置阴影 (代码实现:setCardElevation)
cardCornerRadius:设置圆角(代码实现:setRadius)
carBackgroundColor: cardview的背景
(3)SwipeRefreshLayout 下拉刷新控件
<android.support.v4.widget.SwipeRefreshLayout android:id="@+id/swipe_fresh_layout" android:layout_width="match_parent" android:layout_height="wrap_content"> <android.support.v7.widget.RecyclerView android:id="@+id/recycler_view" android:layout_width="match_parent" android:layout_height="match_parent"/> </android.support.v4.widget.SwipeRefreshLayout> |
// 设置下拉刷新控件显示的颜色 swipeRefreshLayout.setColorSchemeResources(...); // 参数1: 下拉时是否缩放 // 参数2: 下拉时开始显示的位置 // 参数2: 下拉手势结束后,下拉刷新控件显示的位置 swipeRefreshLayout.setProgressViewOffset(false, 10, 50); // 显示下拉刷新控件 swipeRefreshLayout.setRefreshing(true); // 设置下拉监听事件 swipeRefreshLayout.setOnRefreshListener(...); |
1.11. 兼容性
l 兼容性
(1)兼容低版本: 调色板(Palette),RecyclerView,CardView
(2)部分兼容低版本: 主题样式
(3)不兼容的功能:
阴影,轮廓,裁剪,染色,矢量图,全新动画
l 定义不同的资源文件夹,存放不同的文件
res/layout-v21/ item_news.xml android:translationZ="10dp"
res/layout item_news.xml
res/drawable selector_item.xml普通的选择器
res/drawable-v21 selector_item.xml 水波纹定义
res/values
res/values-v21
l 使用以上无法兼容低版本的功能时,要进行版本的判断:
@TargetApi(Build.VERSION_CODES.LOLLIPOP) public void onFinish(View view) { finishAfterTransition(); } |
public void onFinish(View view) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { finishAfterTransition(); } else { finish(); } } |