Material-Design
介绍Material design的相关控件在开发中使用
##概述 对于开发人员,Android21新增了许多新控件和新特性,这些控件和特性都是基于Material Design的设计理念的,这一篇就来讲讲Material Design给Android开发带来的变化。
##主题和布局 Material提供了下面三种主题
@android:style/Theme.Material (dark version)
@android:style/Theme.Material.Light (light version)
@android:style/Theme.Material.Light.DarkActionBar
使用Material的主题必须Api在21及以上;如果要适配21以下的设备,可以使用兼容包: Theme.AppCompat.Light Theme.AppCompat.Light.NoActionBar 继承Material主题时,可以通过下面的属性来自定义调色板:
<style name="AppTheme" parent="android:Theme.Material">
<item name="colorPrimary"></item>
<item name="colorPrimaryDark"></item>
<item name="colorAccent"></item>
<item name="textColorPrimary"></item>
<item name="windowBackground"></item>
<item name="navigationBarColor"></item>
</style>
设置StatusBar和系统NavigationBar的颜色
Andriod4.4 Holo主题
使用主题:继承主题Theme.Holo.Light.NoActionBar.TranslucentDecor ;不继承这个也可以,设置下面属性即可:
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
设置 fitsSystemWindows 属性为true来进行布局调整
使用SystemTintBar开源库设置颜色(其实就是自己画个View上去)
Android5.0及以上
使用主题:继承主题Theme.Material.Light,且SDK >= 21
设置属性或者调用方法:
colorPrimaryDark、navigationBarColor
getWindow().setStatusBarColor(Color.RED);
getWindow().setNavigationBarColor(Color.RED);
使用兼容包 Theme.AppCompat.Light 和上面一样
Theme Individual Views 个人主题View
在layout的xml定义中,可以使用android:theme来指定该View所适用的主题,指定后,它将改变当前View及其子View的theme。
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="个人主题"
android:theme="@style/xxx" />
Ps: 搞清沉浸式状态栏和透明状态栏
##视图和阴影 上一篇讲到阴影的产生是因为高度差,阴影的大小是由相对高度差决定的。
在以前UI只是二维时,View的位置参数有以下公式 X = Left + translationX Y = Top = translationY; Material新增的第三维度同样遵循这个公式 Z = elevation + translationZ
在初始化View的时候,elevation被复制,之后在Z轴上做属性动画,改变translationZ,两者之和是View的绝对高度。
###设置Elevation
在layout.xml中设置 android:elevation="2dp"
在代码中设置 mButton.setElevation(4f);
###Palette取色器 这个类挺有意思,在Android的版本发展中,UI越来越成为Google的发展中心,这次的Android5.X创新的使用了Palette来提取颜色,从而让主题能够动态适应当前页面的色调,使得整个app的颜色基本和谐统一。
Android内置了几种提取颜色的种类:
Vibrant(充满活力的)
Vibrant dark(充满活力的黑)
Vibrant light(充满活力的白)
Muted(柔和的)
Muted dark(柔和的黑)
Muted light(柔和的白)
使用:
gradle
compile 'com.android.support:palette-v7:21.0.+'
Api
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
etContentView(R.layout.activity_main);
setPalette();
}
private void setPalette() {
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher);
//创建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());
}
});
}
3.效果
4.分析
生成Palette对象的方法有好多
Palette palette = Palette.from(bitmap).generate();
Palette palette = Palette.generate(bitmap);
Palette palette = Palette.generate(bitmap, 24);
这几个都是同步的获取,如果bitmap很小可以这样使用,如果bitmap很大,就需要使用异步获取。
Palette.generateAsync(bitmap, new Palette.PaletteAsyncListener() {
@Override
public void onGenerated(Palette palette) {
}
});
然而最新版本都deprecated了,建议使用Builder来构建Palette,Builder方式也支持同步和异步两种方式。
同步:
Palette palette = new Palette.Builder(bitmap).generate();
异步:
new Palette.Builder(bitmap).generate(new Palette.PaletteAsyncListener() {
@Override
public void onGenerated(Palette palette) {
}
});
Ps:重要的一点 上面有一个方法传了24,这个值代表的是取样的最大颜色数,默认是16,建议范围8-32. 数值越大,取样耗时越长,但是取样代表性越强,所以根据你的图片的大小和需求,自己确定数值的大小。
取样风格 Palette.Swatch vibrant = palette.getDarkVibrantSwatch();
Palette提供了6种取样风格:
palette.getVibrantSwatch(); //充满活力的
palette.getDarkVibrantSwatch(); //充满活力的黑
palette.getLightVibrantSwatch(); //充满活力的白
palette.getMutedSwatch(); //柔和的
palette.getDarkMutedSwatch(); //柔和的黑
palette.getLightMutedSwatch(); //柔和的白
Ps:这里的黑、白是指深色系和浅色系
样本值 Demo里用到的vibrant.getRgb()是获取取样后的颜色RGB值 除了这个api,还有以下:
getPopulation():Swatch所代表的取样样本的像素个数 getRgb():Swatch取样的颜色RGB getHsl():Swatch取样的颜色HLS getBodyTextColor():在此取样颜色上,显示Body文本的推荐色 getTitleTextColor():在此取样颜色上,显示Title文本的推荐色
Tinting着色器 先看一个例子,Tinting的使用非常的简单,只要在XML中配置好tint和tintMode就可以了,对于配置组合效果,只需要大家实际操作一下,就能非常清晰的理解处理效果。
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
android:src="@mipmap/ic_launcher" />
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
android:src="@mipmap/ic_launcher"
android:tint="@android:color/holo_blue_bright" />
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
android:src="@mipmap/ic_launcher"
android:tint="@android:color/holo_blue_bright"
android:tintMode="add" />
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
android:src="@mipmap/ic_launcher"
android:tint="@android:color/holo_blue_bright"
android:tintMode="multiply" />
Ps:tint->着色颜色;tintMode->着色模式
所谓着色,就是在背景颜色上再画一层颜色,不同的着色模式,对两层颜色的处理不一样。 系统控件有很多用到了着色,比如EditText,在Material主题下,设置colorAccent,EditText在聚焦时会变成高亮色。
如果要用两张图片的方式就太浪费资源了,系统用的就是用TintManager为背景着色,着色模式是SRC_IN。 着色模式有以下几种:
文字解释: 1.PorterDuff.Mode.CLEAR:所绘制不会提交到画布上。 2.PorterDuff.Mode.SRC:显示上层绘制图片 3.PorterDuff.Mode.DST:显示下层绘制图片 4.PorterDuff.Mode.SRC_OVER:正常绘制显示,上下层绘制叠盖。 5.PorterDuff.Mode.DST_OVER:上下层都显示。下层居上显示。 6.PorterDuff.Mode.SRC_IN:取两层绘制交集。显示上层。 7.PorterDuff.Mode.DST_IN:取两层绘制交集。显示下层。 8.PorterDuff.Mode.SRC_OUT:取上层绘制非交集部分。 9.PorterDuff.Mode.DST_OUT:取下层绘制非交集部分。 10.PorterDuff.Mode.SRC_ATOP:取下层非交集部分与上层交集部分 11.PorterDuff.Mode.DST_ATOP:取上层非交集部分与下层交集部分 12.PorterDuff.Mode.XOR:异或:去除两图层交集部分 13.PorterDuff.Mode.DARKEN:取两图层全部区域,交集部分颜色加深 14.PorterDuff.Mode.LIGHTEN:取两图层全部,点亮交集部分颜色 15.PorterDuff.Mode.MULTIPLY:取两图层交集部分叠加后颜色 16.PorterDuff.Mode.SCREEN:取两图层全部区域,交集部分变为透明色
代码设置:
Drawable drawable = ContextCompat.getDrawable(this,R.mipmap.ic_launcher);
Drawable.ConstantState state = drawable.getConstantState();
Drawable drawable1 = DrawableCompat.wrap(state == null ? drawable : state.newDrawable()).mutate();
drawable1.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
DrawableCompat.setTint(drawable, ContextCompat.getColor(this, R.color.colorAccent));
//ivTint.setImageDrawable(drawable);
ivTint.setImageDrawable(drawable1);
比如我现在要对一个自定义组件实现对Tint的支持,其实只用继承下,加一些代码就好了,代码如下(几乎通用):
public class AppCompatFlowLayout extends FlowLayout implements TintableBackgroundView {
private static final int[] TINT_ATTRS = {
android.R.attr.background
};
private TintInfo mInternalBackgroundTint;
private TintInfo mBackgroundTint;
private TintManager mTintManager;
public AppCompatFlowLayout(Context context) {
this(context, null);
}
public AppCompatFlowLayout(Context context, AttributeSet attributeSet) {
this(context, attributeSet, 0);
}
public AppCompatFlowLayout(Context context, AttributeSet attributeSet, int defStyle) {
super(context, attributeSet, defStyle);
if (TintManager.SHOULD_BE_USED) {
TintTypedArray a = TintTypedArray.obtainStyledAttributes(getContext(), attributeSet,
TINT_ATTRS, defStyle, 0);
if (a.hasValue(0)) {
ColorStateList tint = a.getTintManager().getTintList(a.getResourceId(0, -1));
if (tint != null) {
setInternalBackgroundTint(tint);
}
}
mTintManager = a.getTintManager();
a.recycle();
}
}
private void applySupportBackgroundTint() {
if (getBackground() != null) {
if (mBackgroundTint != null) {
TintManager.tintViewBackground(this, mBackgroundTint);
} else if (mInternalBackgroundTint != null) {
TintManager.tintViewBackground(this, mInternalBackgroundTint);
}
}
}
@Override
protected void drawableStateChanged() {
super.drawableStateChanged();
applySupportBackgroundTint();
}
private void setInternalBackgroundTint(ColorStateList tint) {
if (tint != null) {
if (mInternalBackgroundTint == null) {
mInternalBackgroundTint = new TintInfo();
}
mInternalBackgroundTint.mTintList = tint;
mInternalBackgroundTint.mHasTintList = true;
} else {
mInternalBackgroundTint = null;
}
applySupportBackgroundTint();
}
@Override
public void setSupportBackgroundTintList(ColorStateList tint) {
if (mBackgroundTint == null) {
mBackgroundTint = new TintInfo();
}
mBackgroundTint.mTintList = tint;
mBackgroundTint.mHasTintList = true;
applySupportBackgroundTint();
}
@Nullable
@Override
public ColorStateList getSupportBackgroundTintList() {
return mBackgroundTint != null ? mBackgroundTint.mTintList : null;
}
@Override
public void setSupportBackgroundTintMode(PorterDuff.Mode tintMode) {
if (mBackgroundTint == null) {
mBackgroundTint = new TintInfo();
}
mBackgroundTint.mTintMode = tintMode;
mBackgroundTint.mHasTintMode = true;
applySupportBackgroundTint();
}
@Nullable
@Override
public PorterDuff.Mode getSupportBackgroundTintMode() {
return mBackgroundTint != null ? mBackgroundTint.mTintMode : null;
}
}
outLine和Clipping裁剪
Clipping可以让我们改变一个视图的外形,要使用Clipping,首先需要使用ViewOutlineProvider来修改outline作用给视图 下面这个例子,将一个正方形的textview通过Clipping裁剪成一个圆角正方形和一个圆。
<TextView
android:id="@+id/tv_rect"
android:layout_width="100dp"
android:layout_height="100dp"
android:elevation="1dp" />
<TextView
android:id="@+id/tv_circle"
android:layout_width="100dp"
android:layout_height="100dp"
android:elevation="1dp" />
@Bind(R.id.tv_rect)
TextView tv1;
@Bind(R.id.tv_circle)
TextView tv2;
ViewOutlineProvider vlp1 = new ViewOutlineProvider() {
@Override
public void getOutline(View view, Outline outline) {
outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), 30);
}
};
ViewOutlineProvider vlp2 = new ViewOutlineProvider() {
@Override
public void getOutline(View view, Outline outline) {
outline.setOval(0, 0, view.getWidth(), view.getHeight());
}
};
tv1.setOutlineProvider(vlp1);
tv2.setOutlineProvider(vlp2);
Ps:这个灰色边框是因为elevation>0而产生的阴影。
打开Outline.java文件,公开的方法并不算多
outline.canClip(); 是否支持裁剪,目前只有矩形、圆形、圆角矩形支持裁剪 outline.getAlpha(); 设置透明度outline.isEmpty(); 是否是空的。刚创建的时候是空的,调用了setEmpty后是空的。 outline.offset(0, 0); 设置x、y的偏移量 outline.setAlpha(1); 设置透明度 outline.setConvexPath(null); 设置用于构造一个outline的path outline.setEmpty(); 置空 outline.setOval(0, 0, 0, 0); 设置椭圆 outline.setOval(new Rect(0, 0, 0, 0)); 同上 outline.setRoundRect(0, 0, 0, 0, 10);设置圆角矩形 outline.setRoundRect(new Rect(0, 0, 0, 0), 10); 同上
以上都是在操作outline,有了outline就可以用它来裁剪View。 view.setClipToOutline(true)
还用以上面的例子来说: 一个TextView,设置背景颜色
<TextView
android:layout_width="100dp"
android:layout_height="100dp"
android:background="#d0d0d0"
android:text="OutLine"
android:gravity="center"
android:elevation="2dp"/>
设置一个outline tv.setOutlineProvider(outline);
你会发现并没有变化。 因为outline并没有应用到裁剪view的内容,需要调用tv.setClipToOutline(true) 这句话会用当前的outline裁剪view的内容,需要canClip返回true,之前提到,目前只有矩形、圆形、圆角矩形支持裁剪。 裁剪结果:
Ps:关于outline、clipping、background、alpha Outline可以影响投影的轮廓,但如果setClipToOutline(false),那它并不会裁剪view的内容,所以也不能改变background和forground的形状。 Outline只是用path描述的一种形状,本身并没有颜色,但是它却有alpha值,并且它的alpha值会影响阴影的透明度。 按照Material的原则,光照不透明的实体才会产生阴影,outline能影响阴影轮廓,却又没有改变view的实体形状,这就不好理解了。 setClipToOutline内部调用了native方法,没法知道具体的实现。
##UI控件 Android5.0基于Material Design,提供了很多新控件,这些控件无论在功能、体验和性能上都很出色,下面一一列举各个控件的使用方法。
###SnackBar### SnackBar通过在屏幕底部展示简洁的信息,为一个操作提供了一个轻量级的反馈,并且在Snackbar中还可以包含一个操作,在同一时间内,仅且只能显示一个 Snackbar,它的显示依赖于UI,不像Toast那样可以脱离应用显示。它的用法和Toast很相似,唯一不同的就是它的第一个参数不是传入Context而是传入它所依附的父视图,但是他比Toast更强大。 (Dialog > SnackBar > Toast) gradle compile 'com.android.support:design:23.1.1'
最简单的提示 Snackbar.make(view, "SnackBar Demo Text", Snackbar.LENGTH_LONG).show();
设置一个Action,自带点击事件
Snackbar.make(view, "Change Text", Snackbar.LENGTH_LONG)
.setAction("OK", new View.OnClickListener() {
@Override
public void onClick(View v) {
tv.setText("New Text");
}
}).show();
设置背景颜色
Snackbar sb=Snackbar.make(view, "SnackBar demo", Snackbar.LENGTH_LONG);
sb.setAction("OK", new onClickListener()());
sb.getView().setBackgroundColor(0xfff44336);
sb.show();
设置Action文字颜色 默认的Action文字颜色是ColorAccent,也可以通过api设置sb.setActionTextColor();
其他API
sb.dismiss();消失 sb.isShown();是否在显示 sb.isShownOrQueued();是否在显示或者在等待队列 sb.setText();设置文字
显示时长
LENGTH_LONG:长时间 LENGTH_SHORT:短时间 LENGTH_INDEFINITE:无限期,知道调用dismiss或者下一个SnackBar显示
注意1.SnackBar始终从底部弹出 SnackBar调用make时传进去一个View,它会顺着这个View去找父级,一直找到CoordinatorLayout或者FrameLayout,然后在它底部弹出。如果你的布局中没有包含这两个容器的一个,它就会一直找到Widnow的DecorView,效果就是在屏幕底部弹出。 看源码:
do {
if(view instanceof CoordinatorLayout) {
...
}
if(view instanceof FrameLayout) {
...
}
} while(view != null);
了解这特性有两个用处,解决异常Bug,实现特殊需求。 Demo:
<android.support.design.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="30dp">
<android.support.design.widget.FloatingActionButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/ic_launcher"
app:borderWidth="0dp"
app:fabSize="normal" />
</android.support.design.widget.CoordinatorLayout>
注意2.悬浮按钮被snackBar遮挡
<RelaticeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="30dp">
<android.support.design.widget.FloatingActionButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/ic_launcher"
app:borderWidth="0dp"
app:fabSize="normal" />
</RelaticeLayout>
这个问题一看很棘手,其实也很简单用一下Material design库里的CoordinatorLayout即可,Java代码完全不用动。
<android.support.design.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="30dp">
<android.support.design.widget.FloatingActionButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/ic_launcher"
app:borderWidth="0dp"
app:fabSize="normal" />
</android.support.design.widget.CoordinatorLayout>
稍微总结下:
1.比toast更加好,毕竟snackbar 可以响应点击事件 2.比Dialog轻量,可以替换很多Dialog的场景 3.snackbar 同一时间有且只有一个在显示 4.snackbar 上不要有图标 5.snackbar上action 只能有一个 6.如果有悬浮按钮FloatingActionButton的话,snackbar 在弹出的时候不要覆盖这个button 7.了解为什么SnackBar是从底部弹出的
###CardView### CardView曾经开始流行在Google+,后来越来越多的APP也引入了Card这样的布局方式,说到底,CardView也是一个容器布局,只是他提供了一种卡片的形式,Google所幸提供了一个CardView控件,方便大家使用这种布局,开发者可以设置大小和视图高度,圆角的角度等。
<android.support.v7.widget.CardView
android:id="@+id/cardview"
android:layout_width="match_parent"
android:layout_height="80dp"
android:layout_margin="16dp"
android:clickable="true"
android:foreground="?android:attr/selectableItemBackground"
app:cardBackgroundColor="@color/colorAccent"
app:cardCornerRadius="4dp"
app:cardElevation="6dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="TextView in CardView"
android:textStyle="bold"
android:textColor="@color/colorPrimaryDark"
android:textSize="26sp" />
</android.support.v7.widget.CardView>
其实不用CardView也能实现,但是CardView做了很好的封装,只需要设置几个属性,就可以实现很好的效果,并且也减少了drawable资源,而且即便你什么都不设置,它也会设置合适的默认值。
Ps:
在低版本中设置了 CardElevation 之后 CardView 会自动留出空间供阴影显示,而 Lollipop 之后则需要手动设置 Margin 边距来预留空间,导致我在设置 Margin 在 Android 5.x 机器上调试好后,在 Kitkat 机器调试时发现边距非常大,严重地浪费了屏幕控件。因此,我们需要自定义两个dimen作为CardView的Margin值。
/res/value /res/value-v21 android:layout_margin="@dimen/xxx_card_margin"
android:foreground="?attr/selectableItemBackground" 这个属性会在 Lollipop 上自动加上 Ripple 效果,在旧版本则是一个变深/变亮的效果。
让点击效果更加贴近 Material Design,要实现这个效果不难,我们只需要借助Lollipop的一个新属性android:stateListAnimator
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="true" android:state_pressed="true">
<objectAnimator
android:duration="@android:integer/config_shortAnimTime"
android:propertyName="translationZ"
android:valueTo="@dimen/touch_raise"
android:valueType="floatType" />
</item>
<item>
<objectAnimator
android:duration="@android:integer/config_shortAnimTime"
android:propertyName="translationZ"
android:valueTo="0dp"
android:valueType="floatType" />
</item>
</selector>
android:stateListAnimator="@anim/touch_raise"
###Toolbar### Toolbar是在 Android 5.0 开始推出的一个 Material Design 风格的导航控件 ,Google 非常推荐大家使用 Toolbar 来作为Android客户端的导航栏,以此来取代之前的 Actionbar 。与 Actionbar 相比, Toolbar 明显要灵活的多。它不像 Actionbar 一样,一定要固定在Activity的顶部,而是可以放到界面的任意位置。除此之外,在设计 Toolbar 的时候,Google也留给了开发者很多可定制修改的余地,这些可定制修改的属性在API文档中都有详细介绍,如:
设置导航栏图标;
设置App的logo;
支持设置标题和子标题;
支持添加一个或多个的自定义控件;
支持Action Menu;
使用 前面提到 Toolbar 是在 Android 5.0 才开始加上的,Google 为了将这一设计向下兼容,自然也少不了要推出兼容版的 Toolbar 。为此,我们需要在工程中引入 appcompat-v7 的兼容包,使用 android.support.v7.widget.Toolbar 进行开发。
MXL文件:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/color_0176da">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Clock" />
</android.support.v7.widget.Toolbar>
</LinearLayout>
MENU文件:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@id/action_search"
android:icon="@mipmap/ic_search"
android:title="@string/menu_search"
app:showAsAction="ifRoom" />
<item
android:id="@id/action_notification"
android:icon="@mipmap/ic_notifications"
android:title="@string/menu_notifications"
app:showAsAction="ifRoom" />
<item
android:id="@+id/action_item1"
android:title="@string/item_01"
app:showAsAction="never" />
<item
android:id="@+id/action_item2"
android:title="@string/item_02"
app:showAsAction="never" />
</menu>
Activity:
public class ToolBarActivity extends BaseActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_tool_bar);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
toolbar.setNavigationIcon(R.mipmap.ic_drawer_home);//设置导航栏图标
toolbar.setLogo(R.mipmap.ic_launcher);//设置app logo
toolbar.setTitle("Title");//设置主标题
toolbar.setSubtitle("Subtitle");//设置子标题
toolbar.setTitleTextColor(Color.rgb(250, 250, 250));//设置标题颜色
toolbar.setSubtitleTextColor(Color.rgb(250, 250, 250));//设置子标题颜色
toolbar.inflateMenu(R.menu.base_toolbar_menu);//设置右上角的填充菜单
toolbar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
return true;
}
});
}
}
设置更多Menu的icon: <item name="actionOverflowButtonStyle">@style/ActionButton.Overflow.White</item>
<style name="ActionButton.Overflow.White"
parent="android:style/Widget.Material.Light.ActionButton.Overflow">
<item name="android:src">@mipmap/ic_more</item>
</style>
设置返回menu的icon toolbar.setNavigationIcon(R.mipmap.ic_drawer_home);
通过着色改变颜色
Drawabledrawable=getResources().getDrawable(R.drawable.abc_ic_ab_back_mtrl_am_alpha);drawable.setColorFilter(Color.RED,PorterDuff.Mode.SRC_IN);toolbar.setNavigationIcon(R.mipmap.ic_drawer_home);
精卫填坑
坑1:在XML中设置Toolbar属性不生效
<android.support.v7.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/color_0176da"
android:logo="@mipmap/ic_launcher"
android:navigationIcon="@mipmap/ic_drawer_home"
android:subtitle="456"
android:title="123">
解决方法:在根布局中加入自定义属性的命名空间
xmlns:toolbar="http://schemas.android.com/apk/res-auto"
<android.support.v7.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/color_0176da"
toolbar:navigationIcon="@mipmap/ic_drawer_home"
toolbar:logo="@mipmap/ic_launcher"
toolbar:subtitle="456"
toolbar:title="123">
为什么会出现这种问题呢?我猜测是因为这个控件是兼容版的控件,用 android:xxx 设置无效是的这些属性是在兼容包中,不在默认的Android SDK中,所以我们需要额外的引入。至于为什么IDE不报错,估计就是bug了吧!
坑2:menu字体颜色不能改变 在style中设置:<item name="actionMenuTextColor">#ffffff</item> 并没有用 网上有人建议直接改全局的字体颜色 在style中设置:<item name="android:textColorPrimary">#ffffff</item> 但是这样其它所有文字的默认颜色都变了,慎重慎重!
坑3:ActionMode不能悬浮 解决方法:在style中设置悬浮
<item name="windowActionModeOverlay">true</item>
坑4:自定义的View位于 title 、 subtitle 和 actionmenu 之间,这意味着,如果 title 和 subtitle 都在,且 actionmenu选项 太多的时候,留给自定义View的空间就越小
坑5:必须设置为没有ActionBar 使用Toolbar必须要求当前Activity没有ActionBar,方法有两种。
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
RequestWindowFeature(Window.FEATURE_NO_TITLE);
###FloatingActionButton### FloatingActionButton从名字可以看出它是一个浮动的按钮,它是一个带有阴影的圆形按钮,可以通过fabSize来改变其大小,主要负责界面的基本操作,这个按钮总体来说还是比较简单的。
默认FloatingActionButton 的背景色是应用主题的 colorAccent(其实MD中的控件主题默认基本都是应用的这个主题),可以通过app:backgroundTint 属性或者setBackgroundTintList (ColorStateList tint)方法去改变背景颜色。
FloatingActionButton 的大小尺寸,可以用过app:fabSize 属性设置
android:src属性改变drawable
app:rippleColor设置点击button时候的颜色(水波纹效果)
app:borderWidth设置 button 的边框宽度
app:elevation设置普通状态阴影的深度(默认是 6dp)
app:pressedTranslationZ设置点击状态的阴影深度(默认是 12dp)
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab_search"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_margin="20dp"
android:clickable="true"
android:src="@android:drawable/ic_dialog_email"
app:borderWidth="1dp"
app:fabSize="normal"
app:rippleColor="#0000ff"
app:elevation="3dp"
app:pressedTranslationZ="6dp"/>
###NavigationView### NavigationView可以和DrawerLayout一起结合使用
代码:
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<android.support.design.widget.NavigationView
android:id="@+id/navigation_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
app:headerLayout="@layout/navigation_header"
app:menu="@menu/drawer" />
</android.support.v4.widget.DrawerLayout>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="192dp"
android:background="?attr/colorPrimaryDark"
android:gravity="center"
android:orientation="vertical"
android:padding="16dp"
android:theme="@style/ThemeOverlay.AppCompat.Dark">
<ImageView
android:layout_width="72dp"
android:layout_height="72dp"
android:src="@mipmap/profile"
app:border_color="@color/primary_light"
app:border_width="2dp" />
<TextView
android:layout_marginTop="10dp"
android:textSize="18sp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="APP开发者"/>
</LinearLayout>
和普通的menu比,就多了单选属性
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".MainActivity">
<group android:checkableBehavior="single">
<item
android:id="@+id/navigation_item_example"
android:icon="@drawable/ic_favorite"
android:title="@string/navigation_example" />
<item
android:id="@+id/navigation_item_blog"
android:icon="@drawable/ic_favorite"
android:title="@string/navigation_my_blog" />
<item
android:id="@+id/navigation_item_about"
android:icon="@drawable/ic_favorite"
android:title="@string/navigation_about" />
</group>
</menu>
navigationView.setNavigationItemSelectedListener(
new NavigationView.OnNavigationItemSelectedListener() {
@Override
public boolean onNavigationItemSelected(MenuItem menuItem) {
return true;
}
});
在Material Design中,Navigation drawer导航抽屉,被设计用于应用导航,提供了一种通用的导航方式,体现了设计的一致性。 而NavigationView的典型用途就是配合之前v4包的DrawerLayout,作为其中的Drawer部分,即导航菜单的本体部分。NavigationView是一个导航菜单框架,使用menu资源填充数据,使我们可以更简单高效的实现导航菜单。它提供了不错的默认样式、选中项高亮、分组单选、分组子标题、以及可选的Header。
###TextInputLayout### 使用过EditText的同学肯定知道,有一个叫hint的属性,它可以提示用户此处应该输入什么内容,然而当用户输入真实内容之后,hint的提示内容就消失了,用户的体验效果是十分不好的,TextInputLayout的出现解决了这个问题。 初始状态
点击输入内容
当用户在输入的时候hint的内容就会跑到输入内容的上边去,并且是以动画的形式,体验非常好。其中TextInputLayout中字体的颜色是style文件中的colorAccent。
看下代码:
<android.support.design.widget.TextInputLayout
android:id="@+id/til"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:errorEnabled="true">
<EditText
android:id="@+id/et"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Password"/>
</android.support.design.widget.TextInputLayout>
textLayout = (TextInputLayout)findViewById(R.id.til);
textLayout.setHint("Password");
非常简单的使用方法,只是在EditText外再套一层TextInputLayout。
错误提示 TextInputLayout还有一个挺不错的设计,就是错误提示,当用户输入内容不正确时,可以在底部弹出一个错误提示。要使用错误提示,需要先设置errorEnable为true。
textInputLayout.setErrorEnabled(true);
app:errorEnable=”true”
然后调用setError(“输入错误”);
设置样式
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="colorAccent">#3498db</item>
</style>
###TabLayout### 我们在使用viewpager的时候,经常会使用TabPageIndicator来与其配合。达到很漂亮的滑动效果。但是TabPageIndicator是第三方的,而且比较老了,当然了现在很多大神都已经开始自己写TabPageIndicator来满足需求,在2015年的google大会上,新的Android Support Design库就包含了TabLayout,它可以实现TabPageIndicator的效果,而且兼容性非常好,最低可以兼容到2.2,32个赞不能再少了。 XML代码
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical">
<android.support.design.widget.TabLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/titleBlue"
app:tabIndicatorColor="@color/white"
app:tabSelectedTextColor="@color/gray"
app:tabTextColor="@color/white" />
<android.support.v4.view.ViewPager
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>
java代码
TabLayout tabLayout;
tabLayout = (TabLayout)view.findViewById(R.id.tab_FindFragment_title);
tabLayout.setTabGravity(TabLayout.GRAVITY_FILL);
tabLayout.setTabMode(TabLayout.MODE_FIXED);
tabLayout.addTab(tabLayout.newTab().setText("Tab1"));
tabLayout.addTab(tabLayout.newTab().setText("Tab2"));
tabLayout.addTab(tabLayout.newTab().setText("Tab3"));
tabLayout.addTab(tabLayout.newTab().setText("Tab4"));
viewPager.stAdapter(adapter);
tabLayout.setViewPager(viewPager);
//tabLayout.setupWithViewPager(viewPager);
属性
tabLayout.setTabMode(TabLayout.MODE_FIXED);
**MODE_FIXED:**固定模式,每一个Tab等宽,不管多少都显示全部。 MODE_SCROLLABLE:滚动模式,适合多Tab,长短不一
tabLayout.setTabGravity(TabLayout.GRAVITY_FILL);
**GRAVITY_CENTER:**居中显示 **GRAVITY_FILL:**充满整个宽度
setIcon: tab.setIcon(R.drawable.ic_1); 要icon横向显示,可以使用setCustomView tab.setCustomView(getCustomView(index));
精卫填坑 tabLayout.setupWithViewPager(viewPager); 看源码,这个方法中调用了setTabsFromPagerAdapter(adapter);
public void setTabsFromPagerAdapter(@NonNull PagerAdapter adapter) {
removeAllTabs();
for (int i = 0, count = adapter.getCount(); i < count; i++) {
addTab(newTab().setText(adapter.getPageTitle(i)));
}
}
removeAllTabs()这个就是说把前面所有tablayout添加的view都删掉。也就是说在之前不管怎么处理view都被干掉。然后设置为PagerAdapter返回的title。
tabLayout.addTab(tab1);
tabLayout.addTab(tab2);
tabLayout.addTab(tab3);
tabLayout.addTab(tab4);
tabLayout.setupWithViewPager(viewPager);
tabLayout.setupWithViewPager(viewPager);
tabLayout.addTab(tab1);
tabLayout.addTab(tab2);
tabLayout.addTab(tab3);
tabLayout.addTab(tab4);
通过Adapter返回title
@Override
public CharSequence getPageTitle(int position) {
return “xxx”;
}
###Notification### Notification作为一个时间触发性的交互提示接口,让我们获得消息的时候,在状态栏,锁屏界面显示相应的消息
Google在Android5.X中,又进一步的改进了通知栏,优化了Notification,在5.X设备上一个标准的通知是这样的,长按会显示消息的来源。
基本的Notification 顶部显示横条
折叠式Notification 下拉时显示扩展模式,高度变高
悬挂式Notification 在顶部悬浮显示,提示性更强
显示等级的Notification VISIBILITY_PUBLIC 只有在没有锁屏时会显示通知 VISIBILITY_PRIVATE 任何情况都会显示通知VISIBILITY_SECRET 在安全锁和没有锁屏的情况下显示通知
RecyclerView 这篇文章已经写的很好了,不需要再做任何总结了。
##动画 ###触摸反馈### 触摸反馈是很有必要的,是对用于操作的反馈确认,通常就是颜色的改变,border的改变。 常规的做法是用两个drawable切换(图片或者xml),Material提供了一种更加优雅、更加符合Material风格的方式,就是水波纹效果ripple。
//波纹有边界
android:background="?android:attr/selectableItemBackground"
//波纹无边界
android:background="?android:attr/selectableItemBackgroundBorderless"
自定义Ripple
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="@android:color/holo_red_dark">
<item>
<shape android:shape="rectangle">
<solid android:color="@color/colorPrimary" />
</shape>
</item>
</ripple>
揭露效果:
<ImageView
android:id="@+id/rect"
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_marginTop="30dp"
android:src="@mipmap/ic_launcher" />
rect = (ImageView) findViewById(R.id.rect);
Animator animator2 = ViewAnimationUtils.createCircularReveal(rect, 0, 0, 0, (float) Math.hypot(rect.getWidth(), rect.getHeight()));
animator2.setDuration(2000);
animator2.start();
###Activity过渡动画### Android的activity跳转动画只是很生硬的切换,即使通过 overridePendingTransition( int inId, int outId)这个方法来给Activity增加一些切换动画,效果也只是差强人意,而在Android5.X中,Google对动画效果进行了更深一步的诠释,为Activity的转场效果设计了更加丰富的动画效果
Android5.X提供了三种Transition类型 进入:一个进入的过渡动画决定Activity中的所有视图怎么进入屏幕 退出:一个退出的过渡动画决定Activity中的所有视图怎么退出屏幕 共享元素:一个共享元素过渡动画决定两个Activity之间的过渡,怎么共享他们的视图
进人和退出效果包括 explode(分解)一一从屏幕中间进或出,移动视图 slide(滑动)——从屏幕边缘进或出,移动视图 fade(淡出) 一一通过改变屏幕上视图的不透明度达到添加或者移除视图
共享元素包括 changeBounds——改变目标视图的布局边界 changeCliBounds——裁剪目标视图边界 changeTransfrom——改变目标视图的缩放比例和旋转角度 changeImageTransfrom——改变目标图片的大小和缩放比例 可以发现,在Android5.X上,动画效果的种类变得更加丰富了
首先来看看普通的三种Activity过渡动画, 要使用这些动画非常简单,例如从ActivityA转到ActivityB,只需要在ActivityA中将基本的startActivity(intent)方法改为如下代码即可 startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(MainActivity.this).toBundle()); 而在AchvityB中,只需要设置下如下所示代码 getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS); 或者在样式文件中设置如下所示代码 <item name="android:windowContentTransitions">true</item>
那么接下来就可以设置进人/退出ActivityB的具体的动画效果了, 代码如下所示
getWindow().setEnterTransition(new Explode()); getWindow().setEnterTransition(new Slide()); getWindow().setEnterTransition(new Fade()); 要想在程序中使用共享元素的动画效果也很简单,首先需要在他的activity1布局中设置共享元素,增加元素代码`android:transitionName="XXX"` 同时在activity2中,也增加一个相应的共享元素属性,如果只要一个共享元素,那么在activity1中可以这样写 startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(this, view, "share").toBundle()); ``` 使用的参数就是前面普通动画的基础上增加了共享的的view和前面取的名字,如果由多个共享元素,那么我们可以通过Pair.create()来创建多个共享元素。
```
startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(this, Pair.create(view,"share"),Pair.create(fab,"fab")).toBundle()); ```
View state changes Animation(视图状态改变动画) StaetListAnimator作为视图改变时的动画效果,通常会使用Seletor来进行设置,但是以前我们设置Seletor的时候,通常是修改他的背景来达到反馈的效果,但是再现在Android5.X中有更高端的方法,可以使用动画来实现。
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true">
<set>
<objectAnimator
android:duration="2000"
android:property="rotationX"
android:valueTo="360"
android:valuyeType="floatType" />
</set>
</item>
<item android:state_pressed="false">
<set>
<objectAnimator
android:duration="2000"
android:property="rotationX"
android:valueTo="0"
android:valuyeType="floatType" />
</set>
</item>
</selector>
<Button
android:layout_width="200dp"
android:layout_height="200dp"
android:background="@drawable/animatorstate" />
曲线运动 Material design中的动画依靠曲线,这个曲线适用于时间插值器和控件运动模式。 PathInterpolator类是一个基于贝塞尔曲线(Bézier curve)或路径(Path)对象上的新的插值器。 在materialdesign规范中,系统提供了三个基本的曲线:
@interpolator/fast_out_linear_in.xml @interpolator/fast_out_slow_in.xml @interpolator/linear_out_slow_in.xml
你可以传递一个PathInterpolator对象给Animator.setInterpolator()方法。
ObjectAnimator类有了新的构造方法,使你能够一次能同时使用两个或多个属性去绘制动画的路径。例如,下面的动画使用一个Path对象进行视图X和Y属性的动画绘制:
```
ObjectAnimator mAnimator;
mAnimator = ObjectAnimator.ofFloat(view, View.X, View.Y, path);
... mAnimator.start(); ```