- 12.1 Android5.X UI设计初步
- 12.1.1 材料的形态模拟
- 12.1.2 更加真实的动画
- 12.1.3 大色块的使用
- 12.2 Material Design主题
- 12.3 Palette
- 12.4 视图与阴影
- 12.5 Tinting和Clipping
- 12.5.1 Tinting(着色)
- 12.5.2 Clipping(裁剪)
- 12.6 列表与卡片
- 12.6.1 RecyclerView
- 12.6.2 CardView
- 12.7 Android过渡动画
- 12.8 Material Design动画效果
- 12.8.1 Ripple效果
- 12.8.2 Circular Reveal
- 12.8.3 View state changes Animation
- 12.9 Toolbar
- 12.10 Notification
- 12.10.1 基本的Notification
- 12.10.2 折叠式Notification
- 12.10.3 悬挂式Notification
- 12.10.4 显示等级的Notification
Android 9.0都出了半年多了,我还在学习Android 5.0的知识,真是醉了,加油吧!
2014年,发布Android5.X,全新设计的UI和更加优化的性能
Android 5.X UI设计初步
Android 5.X 新特性分析
- Android 5.X开始使用新的设计风格Material Design,翻译就是“材料设计”,不知道对不对
12.1.1 材料的形态模拟
- Google通过模拟自然界的形态变化、关线与阴影、纸与纸之间的空间层级关系,带来一种真实的空间感
- 那种按钮都会是有阴影效果的,就是感觉很新潮
12.1.2 更加真实的动画
- 好的动画效果可以更好的引导用户,给用户非常愉悦的体验
12.1.3 大色块的使用
- Material Design中采用大量高饱和、适中亮度的大色块来突出界面的主次
- Google的Design网站上,有整个Material Design的设计,网址:http://www.google.com/design/#resources
Material Design提供默认的三种主题
@android:style/Theme.Material (dark version) @android:style/Theme.Material.Light (light version) @android:style/Theme.Material.Light.DarkActionBar
同时也提出了Color Palette的概念,可以自己设定系统区域的颜色
可以通过使用自定义Style来创建自己的Color Palette颜色主题
Android5.X使用Palette来提取颜色,从而让主题能够动态适应当前页面的色调
Android内置了几种提取颜色的种类:
- Vibrant(充满活力的)
- Vibrant dark(充满活力的黑)
- Vibrant light(充满活力的白)
- Muted(柔和的)
- Muted dark(柔和的黑)
- Muted light(柔和的白)
使用Palette需要在Dependencies中添加依赖:
compile 'com.android.support:palette-v7:21.0.2'
这可以给Palette传进一个Bitmap对象,并调用它的Palette.generate()静态方法或者Palette.generateAsync()方法创建一个Palette
下面例子演示如何通过加载图片的柔和色调来改变状态栏和Actionbar的色调:
public class PaletteActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.palette); Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.test); // 创建Palette对象 Palette.generateAsync(bitmap, new Palette.PaletteAsyncListener() { @Override public void onGenerated(Palette palette) { // 通过Palette来获取对应的色调 Palette.Swatch vibrant = palette.getDarkVibrantSwatch(); // 将颜色设置给相应的组件 getSupportActionBar().setBackgroundDrawable( new ColorDrawable(vibrant.getRgb())); Window window = getWindow(); window.setStatusBarColor(vibrant.getRgb()); } }); } }
- Material Design最重要的特点就是拟物扁平化
12.4.1 阴影效果
Android 5.X在以往的X、Y属性上添加了—Z,对应垂直方向上的变化
- elevation是静态的成员,在xml中直接设置
- translationZ可以在代码中使用来实现动画效果
Z = elevation + translationZ;
- 在xml中设置
android:elevation="XXdp"
- 在代码中设置
view.setTranslationZ(xxx);
- 通常还可以和属性动画一起使用
if(flag){ view.animate().translationZ(100); flag = false; }else { view.animate().translationZ(0); flag = true; }
12.5.1 Tinting(着色)
- 使用非常简单,XML文件中配置好tint和tintMode就可以了,至于代码的效果大家自己实际操作一下就好了
12.5.2 Clipping(裁剪)
- Clipping可以让我们改变一个视图的外形
- 首先需要使用ViewOutlineProvider来修改outline,然后再通过setOutlinProvider将outline作用给视图
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); View v1 = findViewById(R.id.tv_rect); View v2 = findViewById(R.id.tv_circle); //获取Outline ViewOutlineProvider viewOutlineProvider1 = new ViewOutlineProvider() { @Override public void getOutline(View view, Outline outline) { //修改outline为特定形状 outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), 30); } }; //获取Outline ViewOutlineProvider viewOutlineProvider2 = new ViewOutlineProvider() { @Override public void getOutline(View view, Outline outline) { //修改outline为特定形状 outline.setOval(0, 0, view.getWidth(), view.getHeight()); } }; //重新设置形状 v1.setOutlineProvider(viewOutlineProvider1); v2.setOutlineProvider(viewOutlineProvider2); } }
12.6 列表与卡片
12.6.1 RecyclerView
- 这个在项目中已经用烂了,也没啥要说的了,而且这本书讲这块讲的不多
- 例子中点击事件加了一个增加高度的动画效果
- 点击事件必须自己定义回调接口:
public OnItemClickListener mOnItemClickListener; public void setOnItemClickListener(OnItemClickListener onItemClickListener) { mOnItemClickListener = onItemClickListener; } public interface OnItemClickListener { void onItemClick(View view, int position); }
12.6.2 CardView
- 这个没啥说的,就是多了圆角,阴影等等一些属性
在Activity之间的跳转中,通过overridePendingtransition(int inId,int outId)这个方法给Activity添加一些切换动画,效果也是差强人意
Android5.X提供了三种Transition类型的动画:
- 进入:一个进入的过渡动画决定Activity中的所有视图怎么进入屏幕
- 退出:一个退出的过渡动画决定Activity中的所有视图怎么退出屏幕
- 共享元素:一个共享元素过渡动画决定两个Activity之间的过渡,怎么共享他们的视图
其中,进人和退出效果包括:
- explode(分解)一一从屏幕中间进或出,移动视图
- slide(滑动)——从屏幕边缘进或出,移动视图
- fade(淡出) 一一通过改变屏幕上视图的不透明度达到添加或者移除视图
共享元素包括(简单的说ActivityA的指定的View不会消失,带着View跳转到ActivityB中):
- changeBounds——改变目标视图的布局边界
- changeClipBounds——裁剪目标视图边界
- changeTransfrom——改变目标视图的缩放比例和旋转角度
- changeImageTransfrom——改变目标图片的大小和缩放比例
这里比如ActivityA跳转到ActivityB,运用这三种过渡动画
// 在ActivityA中修改: startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(this).toBundle()); // 而在ActivityB,只需要设置以下代码: getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS); // 或者在ActivityB的样式文件设置如下代码:
- true
// 那么接下来可以设置进入ActivityB的动画效果: getWindow().setEnterTransition(new Explode()); getWindow().setEnterTransition(new Slide()); getWindow().setEnterTransition(new Fade()); // 或者是设置离开ActivityB的动画效果: getWindow().setExitTransition(new Explode()); getWindow().setExitTransition(new Slide()); getWindow().setExitTransition(new Fade());下面是共享元素的使用
// 首先在ActivityA中设置共享元素的属性: android:transitionName="XXX" // 同时在ActivityB中设置相同的共享元素的属性: android:transitionName="XXX" // 在代码中使用共享元素,首先是单个共享元素: startActivity(intent,ActivityOptions.makeSceneTransitionAnimation(this,view,"share").toBundle()); // 或者一个视图中有多个共享元素,通过Pair.create()来创建多个共享元素: startActivity(intent,ActivityOptions.makeSceneTransitionAnimation(this, Pair.create(view,"share"),Pair.create(fab,"fab")).toBundle());
一整个过渡动画的例子
布局就不贴了,ActivityA:
public class TransitionsA extends Activity { private Intent intent; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_transition); } // 设置不同动画效果 public void explode(View view) { intent = new Intent(this, TransitionsB.class); intent.putExtra("flag", 0); startActivity(intent,ActivityOptions.makeSceneTransitionAnimation(this).toBundle()); } // 设置不同动画效果 public void slide(View view) { intent = new Intent(this, TransitionsB.class); intent.putExtra("flag", 1); startActivity(intent,ActivityOptions.makeSceneTransitionAnimation(this).toBundle()); } // 设置不同动画效果 public void fade(View view) { intent = new Intent(this, TransitionsB.class); intent.putExtra("flag", 2); startActivity(intent,ActivityOptions.makeSceneTransitionAnimation(this).toBundle()); } // 设置不同动画效果 public void share(View view) { View fab = findViewById(R.id.fab_button); intent = new Intent(this, TransitionsB.class); intent.putExtra("flag", 3); // 创建单个共享元素 // startActivity(intent, // ActivityOptions.makeSceneTransitionAnimation( // this, view, "share").toBundle()); startActivity(intent,ActivityOptions.makeSceneTransitionAnimation(this, // 创建多个共享元素 Pair.create(view, "share"), Pair.create(fab, "fab")).toBundle()); } }
ActivityB:
public class TransitionsB extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS); int flag = getIntent().getExtras().getInt("flag"); // 设置不同的动画效果 switch (flag) { case 0: getWindow().setEnterTransition(new Explode()); break; case 1: getWindow().setEnterTransition(new Slide()); break; case 2: getWindow().setEnterTransition(new Fade()); getWindow().setExitTransition(new Fade()); break; case 3: break; } setContentView(R.layout.activity_transition_to); } }
- Android 5.X的UI设计更是使用了大量的动画效果
12.8.1 Ripple效果
- 顾名思义,水波纹效果
// 水波纹有边界 android:background="?android:attr/selectableItemBackground" // 水波纹无边界 android:background="?android:attr/selectableItemBackgroundBorderless"
- 同样的,也可以通过XML来声明一个ripple,然后在布局文件中使用
12.8.2 Circular Reveal
- 意为“循环揭示”,这个动画效果具体变现为一个View以圆形的形式展开,揭示出来,通过ViewAnimationUtils.createCircularReveal()来创建动画,代码如下:
public static Animator createCircularReveal(View view, int centerX, int centerY, float startRadius, float endRadius) { return new RevealAnimator(view,centerX,centerY,startRadius,endRadius); }
RevealAnimator的使用特别简单,主要就是几个关键的坐标点:
- centerX 动画开始的中心点X
- centerY 动画开始的中心点Y
- startRadius 动画开始半径
- endRadius 动画结束半径
接下来我们来看一下例子,了解下RevealAnimator的使用,首先用XML创建一个圆和方形:
public class CircularReveal extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_circular_reveal); final View oval = findViewById(R.id.oval); oval.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Animator animator = ViewAnimationUtils.createCircularReveal(oval, oval.getWidth() / 2, oval.getHeight() / 2, oval.getWidth(), 0); animator.setInterpolator(new AccelerateDecelerateInterpolator()); animator.setDuration(2000); animator.start(); } }); final View rect = findViewById(R.id.rect); rect.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Animator animator = ViewAnimationUtils.createCircularReveal(rect, 0, 0, 0, (float) Math.hypot(rect.getWidth(), rect.getHeight())); animator.setInterpolator(new AccelerateInterpolator()); animator.setDuration(2000); animator.start(); } }); } }
12.8.3 View state changes Animation
- 意为“视图状态改变动画”
StateListAnimator
视图动画效果,以前Selector用来修改背景达到点击的变化,现在Selector支持动画,在XML定义一个StateListAnimator,添加到Selector中:
在布局中直接使用:
Animator是要开启的,所以必须在主代码中使用AnimatorInflater.loadStateListAnimator()方法,并且通过View.setStateListAnimator()方法分配到视图中
animated-selector
同样是一个状态改变的Selector,使用这个动画效果需要准备一套图片,然后类似帧动画一样,一张一张的播
- ...一直到30
- ...一直到30
程序中直接使用
public class AnimatedSelectorActivity extends AppCompatActivity { private boolean mIsCheck; private static final int[] STATE_CHECKED = new int[]{ android.R.attr.state_checked}; private static final int[] STATE_UNCHECKED = new int[]{}; private ImageView mImageView; private Drawable mDrawable; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_animated_selector); mImageView = (ImageView) findViewById(R.id.image); mDrawable = getResources().getDrawable(R.drawable.fab_anim); mImageView.setImageDrawable(mDrawable); } public void anim(View view) { if (mIsCheck) { mImageView.setImageState(STATE_UNCHECKED, true); mIsCheck = false; } else { mImageView.setImageState(STATE_CHECKED, true); mIsCheck = true; } } }
Toolbar与ActionBar最大的区别就是Toolbar更加自由、可控,这也是Google逐渐使用Toolbar替代Actionbar的原因,使用Toolbar必须引入appcompat-v7包,并设置主题为NoActionBar,使用以下代码进行设置:
记得先将主题设置为NoActionBar主题,在布局中声明ToolBar
在Menu文件夹中创建一个Menu的XML文件
在代码中使用
public class ToolbarActivity extends AppCompatActivity { private ShareActionProvider mShareActionProvider; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_toolbar); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); toolbar.setLogo(R.mipmap.ic_launcher); toolbar.setTitle("主标题"); toolbar.setSubtitle("副标题"); setSupportActionBar(toolbar); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu_main, menu); return super.onCreateOptionsMenu(menu); } }
修改布局实现侧滑
代码中实现
public class ToolbarActivity extends AppCompatActivity { private ShareActionProvider mShareActionProvider; private DrawerLayout mDrawerLayout; private ActionBarDrawerToggle mDrawerToggle; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_toolbar); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); toolbar.setLogo(R.mipmap.ic_launcher); toolbar.setTitle("主标题"); toolbar.setSubtitle("副标题"); setSupportActionBar(toolbar); getSupportActionBar().setDisplayHomeAsUpEnabled(true); mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer); mDrawerToggle = new ActionBarDrawerToggle( this, mDrawerLayout, toolbar, R.string.abc_action_bar_home_description, R.string.abc_action_bar_home_description_format); mDrawerToggle.syncState(); mDrawerLayout.setDrawerListener(mDrawerToggle); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu_main, menu); /* ShareActionProvider配置 */ mShareActionProvider = (ShareActionProvider) MenuItemCompat.getActionProvider(menu .findItem(R.id.action_share)); Intent intent = new Intent(Intent.ACTION_SEND); intent.setType("text/*"); mShareActionProvider.setShareIntent(intent); return super.onCreateOptionsMenu(menu); } }
现在很多APP,微信、支付宝等头部都有很多的按钮,不知道是不是ToolBar实现的,但是都是类似的功能吧,看看需求是如何了,也有的项目是自己写一个常用的自定义控件TopBar
很明显,“通知”的意思
Android 5.X对Notification做了优化:
- 长按Notification可以显示消息来源
- 锁屏状态下,可以看到Notification的通知
12.10.1 基本的Notification
public class Notification01 extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_notification01); //第一步:初始化 Notification.Builder builder = new Notification.Builder(this); //第二步:构建点击之后的意图 Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.baidu.com")); //构造pendingdintent PendingIntent pendingIntent = PendingIntent.getActivities(this, 0, new Intent[]{intent}, 0); //第三步:设置通知栏的各种消息 builder.setSmallIcon(R.mipmap.ic_launcher); builder.setContentIntent(pendingIntent); builder.setAutoCancel(true); builder.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher)); builder.setContentText("Title"); builder.setContentText("内容"); builder.setSubText("text"); //第四步:通过NotificationManager来发出 NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); notificationManager.notify(0, builder.build()); } }
12.10.2 折叠式Notification
public class Notification02 extends AppCompatActivity { private static final int NOTIFICATION_ID_COLLAPSE = 0x01; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_notification02); Intent intent = new Intent(Intent.ACTION_VIEW,Uri.parse("http://www.sina.com")); PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, 0); Notification.Builder builder = new Notification.Builder(this); builder.setSmallIcon(R.mipmap.ic_launcher); builder.setContentIntent(pendingIntent); builder.setAutoCancel(true); builder.setLargeIcon(BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher)); // 通过RemoteViews来创建自定义的Notification视图 RemoteViews contentView = newRemoteViews(getPackageName(),R.layout.notification); contentView.setTextViewText(R.id.textView,"show me when collapsed"); Notification notification = builder.build(); notification.contentView = contentView; // 通过RemoteViews来创建自定义的Notification视图 RemoteViews expandedView =new RemoteViews(getPackageName(),R.layout.notification_expanded); notification.bigContentView = expandedView; NotificationManager nm = (NotificationManager)getSystemService(NOTIFICATION_SERVICE); nm.notify(NOTIFICATION_ID_COLLAPSE, notification); } }
12.10.3 悬挂式 Notification
- 比如说微信在后台来新消息了
public class Notification03 extends AppCompatActivity { private static final int NOTIFICATION_ID_HEADSUP = 0x01; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_notification03); Notification.Builder builder = new Notification.Builder(this) .setSmallIcon(R.mipmap.ic_launcher) .setPriority(Notification.PRIORITY_DEFAULT) .setCategory(Notification.CATEGORY_MESSAGE) .setContentTitle("Headsup Notification") .setContentText("I am a Headsup notification."); Intent push = new Intent(); push.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); push.setClass(this, MainActivity.class); PendingIntent pendingIntent = PendingIntent.getActivity( this, 0, push, PendingIntent.FLAG_CANCEL_CURRENT); builder.setContentText("Heads-Up Notification on Android 5.0") .setFullScreenIntent(pendingIntent, true); NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); nm.notify(NOTIFICATION_ID_HEADSUP, builder.build()); } }
12.10.4 显示等级的Notification
Notification分为三个等级:
- VISIBILITY_PRIVATE:表明只有当没有锁屏的时候会显示
- VISIBILITY_PUBLIC:表明在任何情况下都会显示
- VISIBILITY_SECRET:表明在pin、password等安全锁和没有锁屏的情况下显示
// 设置Notification的等级非常简单,只需在案例三四五增加一句: builder.setVisibility(Notification.VISIBILITY_PUBLIC); // 同时,在Android 5.X改动挺多的,比如如下两种: // 设置Notification背景颜色 builder.setColor(Color.RED); //设置Notification的category接口,用来确定Notification的显示位置 builder.setCategory(Notification.CATEGORY_MESSAGE);
其实这一章虽然内容多,但是都是一些比较简单的内容,看一看就应该差不多会了,即使忘了,再看一下就都能会用,想想现在Android 9.0都出了半年了,我还在学习Andorid 5.0的知识,这种感觉真是,不过不论何时,学习都不晚,加油吧!那么让我们在越来越多的实战中去巩固这些知识吧!