关于Material Design 的各种详细的介绍,极客学院的wiki写的非常清楚了:
http://wiki.jikexueyuan.com/project/material-design/
那么我们现在就开始撸代码
1.控制项目全局样式
1.1 无论是使用AS还是ADT,都要引入appcompat-v7,AS使用下面这一句代码引入,解决Android碎片化开发的问题,碎片化开发,就是在不同版本上的UI,看起来不会有太大的区别。
implementation 'com.android.support:appcompat-v7:26.1.0'
implementation 'com.android.support:design:26.1.0'
1.2 写自己的全局样式:
<!--
Base application theme, dependent on API level. This theme is replaced
by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
-->
<style name="AppBaseTheme" parent="Theme.AppCompat.Light">
<!--
Theme customizations available in newer API levels can go in
res/values-vXX/styles.xml, while customizations related to
backward-compatibility can go here.
-->
</style>
<!-- Application theme. -->
<style name="AppTheme" parent="AppBaseTheme">
<!-- All customizations that are NOT specific to a particular API-level can go here. -->
<item name="android:textColor">@color/mytextcolor</item>
<item name="colorPrimary">@color/colorPrimary_pink</item>
<item name="colorPrimaryDark">@color/colorPrimary_pinkDark</item>
<item name="android:windowBackground">@color/background</item>
<item name="colorAccent">@color/accent_material_dark</item>
<!-- 设置虚拟导航栏背景颜色 5.x新特性-->
<item name="android:navigationBarColor">@color/colorPrimary_pink</item>
</style>
AppTheme相当于AppBaseTheme的子类,在AppTheme中设置各种颜色,样式的配置。其中:
colorPrimary:主色,
colorPrimaryDark:主色--深色,一般可以用于状态栏颜色、底部导航栏
colorAccent:代表各个控件的基调颜色--CheckBox、RadioButton、ProgressBar等
android:textColor:当前所有的文本颜色
2. v7包中有很多控件,都是为兼容而生的,在各个版本中运行的外观并没有太大的区别。
2.1 AlertDialog:
android.app包中的AlertDialog:在不同的版本上显示样式是不一样的,在4.1下显示是这样的:
8.0下显示是这样的:
而android.support.v7.app包下的AlertDialog,在4.1和8.0下显示基本相同。
可以看到,基本上是一样的。
2.2 下拉刷新 SwipeRefreshLayout
srl.setColorSchemeColors(Color.YELLOW,Color.RED,Color.GREEN);//设置进度条的颜色
srl.setProgressBackgroundColorSchemeColor(Color.WHITE);//设置背景颜色
srl.setDistanceToTriggerSync(100);//下拉多少距离开始刷新
srl.setSize(SwipeRefreshLayout.DEFAULT);//设置进度条的大小
srl.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
//下拉完毕
srl.setRefreshing(false);//消失
}
});
2.3 PopWindow,ListPopupWindow,PopupMenu
ListPopupWindow:
final ListPopupWindow listPopupWindow = new ListPopupWindow(this);
listPopupWindow.setAdapter(adapter);
listPopupWindow.setHeight(LinearLayout.LayoutParams.WRAP_CONTENT);
listPopupWindow.setWidth(LinearLayout.LayoutParams.MATCH_PARENT);
listPopupWindow.setAnchorView(view);//设置锚点,弹出的位置相对于View
listPopupWindow.show();
listPopupWindow.getListView().setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Toast.makeText(getApplicationContext(),"点击了"+position,Toast.LENGTH_SHORT).show();
listPopupWindow.dismiss();
}
});
PopupMenu:
android.support.v7.widget.PopupMenu popupMenu
= new android.support.v7.widget.PopupMenu(this,view);
popupMenu.getMenuInflater().inflate(R.menu.menu,popupMenu.getMenu());
popupMenu.show();
2.4 LinearLayoutCompat :在该控件中添加属性:
app:divider="@drawable/abc_list_divider_mtrl_alpha"
app:showDividers="beginning|middle"
可以给LinearLayout中的控件都加上divider
<android.support.v7.widget.LinearLayoutCompat
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
app:divider="@drawable/abc_list_divider_mtrl_alpha"
app:showDividers="beginning|middle"
>
<!--子控件-->
</android.support.v7.widget.LinearLayoutCompat>
其他的控件,如Button,TextView,EditText等,V7包中的都是这样的包名:
android.support.v7.widget.AppCompatButton android.support.v7.widget.AppCompatTextView等。
查看源码:分析LinearLayoutCompat是怎么给其中的每个控件添加分割线的?
1.在xml文件中app:设置分割线,那么就从自定义属性入手,发现源码中有这样一句:
setDividerDrawable(a.getDrawable(R.styleable.LinearLayoutCompat_divider));
其中
a.getDrawable(R.styleable.LinearLayoutCompat_divider)
这句代码的意思是获取用户输入的drawable,那么setDividerDrawable()就是设置分割线了。
3. RecyclerView
RecyclerView内容很多,现在暂时没有时间去整理,以后再整理
RecyclerView也是v7包中的控件,因为使用的很多,所以专门拿出来说,RecyclerView最低可兼容到3.0。引入方法:
implementation 'com.android.support:recyclerview-v7:26.1.0
用法:Adapter 继承自 RecyclerView.Adapter<RecyclerView.ViewHolder>
设置adapter和LayoutManager给RecyclerView。
简单封装
绘制间隔线:
1在getItemOffset()中绘制矩形 在该矩形中绘制间隔线
2 拿到系统的属性的方法
4.侧滑
在Material Design出来之前,一般都是使用SlidingMenu进行侧滑,MD出来之后,Google收录了很多民间项目,并改编,放入了support包中,供开发者使用,DrawerLayout就是其中之一。
4.1 DrawerLayout :
1.来自support-v4包中的weidget包
2.继承自ViewGroup,可以看成是一个帧布局
3.里面包含两块内容,一个是内容布局,一个是侧滑布局,侧滑布局是在内容布局的上面显示,因此是写在内容布局的下面,并且需要设置android:layout_gravity="start" 如果设置成end 则是右侧滑动菜单
DrawerLayout和ToolBar结合使用:
1.在布局中添加ToolBar
2.在代码中用ToolBar替换ActionBar:setSupportActionBar(toolbar);
3.使用ActionBarDrawerToggle把DrawerLayout和ToolBar联系起来
toolbar = findViewById(R.id.toolbar);
//用toolbar替换actionBar
setSupportActionBar(toolbar);
drawerlayout = findViewById(R.id.drawerlayout);
//最后两个参数是两个资源文件的名称,int类型 并无实际意义,只要填上即可
ActionBarDrawerToggle drawerToggle = new ActionBarDrawerToggle(this,
drawerlayout,toolbar,R.string.drawer_open,R.string.drawer_close);
//状态同步
drawerToggle.syncState();
//给DrawerLayout设置监听
drawerlayout.setDrawerListener(drawerToggle);
4 如果要自定义DrawerLayout打开关上的动画,只需要重写DrawerListener即可
drawerlayout.addDrawerListener(new DrawerLayout.DrawerListener() {
/**
* Called when a drawer's position changes.
* @param drawerView The child view that was moved
* @param slideOffset The new offset of this drawer within its range, from 0-1
*/
@Override
public void onDrawerSlide(View drawerView, float slideOffset) {
//drawerView :侧滑布局
//slideOffset :滑动时的偏移量
}
/**
* Called when a drawer has settled in a completely open state.
* The drawer is interactive at this point.
*
* @param drawerView Drawer view that is now open
*/
@Override
public void onDrawerOpened(View drawerView) {
//打开时调用
}
/**
* Called when a drawer has settled in a completely closed state.
*
* @param drawerView Drawer view that is now closed
*/
@Override
public void onDrawerClosed(View drawerView) {
//关闭时调用
}
/**
* Called when the drawer motion state changes. The new state will
* be one of {@link #STATE_IDLE}, {@link #STATE_DRAGGING} or {@link #STATE_SETTLING}.
*
* @param newState The new drawer motion state
*
* STATE_IDLE:空闲 Indicates that any drawers are in an idle,
* settled state. No animation is in progress.
* STATE_DRAGGING:正在拖拽 Indicates that a drawer is currently
* being dragged by the user.
* STATE_SETTLING:拖拽完成 Indicates that a drawer is in
* the process of settling to a final position.
*
*/
@Override
public void onDrawerStateChanged(int newState) {
//拖拽状态改变时调用
}
});
//方法内容为空,也可以点击ToolbarNavigation 打开关闭DrawerLayout
drawerToggle.setToolbarNavigationClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
}
});
4.2 NavigationView
1.是对DrawerLayout更完善的封装
2.是谷歌在侧滑的MaterialDesign的一种规范,用来规范侧滑的基本样式。
NavigationView就是封装好的侧滑区域的布局,它有几个属性
app:headerLayout 和 app:menu 是头布局和头布局底下的菜单布局
在使用NavigationView时也需要将其android:layout_gravity 设置成start
插播:自定义Toast
Toast result = new Toast(this);
LayoutInflater inflate = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View v = inflate.inflate(自定义Toast的layout, null);
TextView tv = (TextView)v.findViewById(com.android.internal.R.id.message);
tv.setText("我是自定义吐司");
result.setView(v);
result.setDuration(Toast.LENGTH_SHORT);
result.show();
5.SnackBar
一种新的吐司,介于Toast和Dialog之间的产物:即做到提示用户,又不会打断用户操作,并且还能与用户交互。
使用:
//无穷时间 Snackbar.LENGTH_INDEFINITE view :为了找到父控件,获取上下文
Snackbar snackbar = Snackbar.make(view,"SnackBar",Snackbar.LENGTH_INDEFINITE);
snackbar.setAction("确定", new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(DrawerlayoutActivity.this,"点击了",Toast.LENGTH_SHORT).show();
}
});
snackbar.setActionTextColor(Color.WHITE);
snackbar.addCallback(new Snackbar.Callback(){
@Override
public void onShown(Snackbar sb) {
super.onShown(sb);
}
@Override
public void onDismissed(Snackbar transientBottomBar, int event) {
super.onDismissed(transientBottomBar, event);
}
});
snackbar.show();
6.TextInputLayout
MD风格的带有提示的EditText
1.使用:
<android.support.design.widget.TextInputLayout
android:id="@+id/text_input"
app:errorEnabled="true"
app:errorTextAppearance="@android:color/holo_red_dark"
app:hintTextAppearance="@android:color/darker_gray"
app:counterTextAppearance="@android:color/holo_green_dark"
app:counterOverflowTextAppearance="@android:color/holo_orange_dark"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="请输入"/>
</android.support.design.widget.TextInputLayout>
其中TextInputLayout属性:
app:errorEnabled="true" 是否开启错误提示
app:errorTextAppearance="@android:color/holo_red_dark" 自定义错误提示字体的颜色和大小等 app:hintTextAppearance="@android:color/darker_gray" 自定义hint字体的样式
app:counterTextAppearance="@android:color/holo_green_dark" 自定义计数字体的样式
app:counterOverflowTextAppearance="@android:color/holo_orange_dark"自定义字数超过最大值时 字体的样式
app:counterEnabled="true" 是否开启计数
app:counterMaxLength="20" 能输入的最大值
app:hintAnimationEnabled="true" hint移动到EditText上方时的动画是否开启
app:hintEnabled="false" 是否允许hint移动到EditText上方
- 自定义EditText错误监听
text_input.getEditText().addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
String string = text_input.getEditText().getText().toString();
if(!TextUtils.isEmpty(string)){
if(string.length() <= 6 ){
text_input.setErrorEnabled(false);
}else{
text_input.setErrorEnabled(true);
text_input.setError("长度不可大于6位");
}
}
}
});
7.Toolbar
顶部导航:显示标题、返回键、快捷操作、菜单等。Toolbar不一定要放在顶部,也可以放底部。
使用时要注意,把主题设置成NoActionBar.
在ToolBar上设置菜单
//重写 oncreateOptionsMenu
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main,menu);
return true;
}
app:popupTheme 弹出菜单样式
菜单中设置:
orderInCategory:设置菜单项的排列顺序,必须设置大于等于0的整数值。数值小的排列在前,如果值相等,则按照xml中的顺序展现。
showAsAction
该属性有五个值,可以混合使用。
always
总是显示在Toolbar上。
ifRoom
如果Toolbar上还有空间,则显示,否则会隐藏在溢出列表中。
never
永远不会显示在Toolbar上,只会在溢出列表中出现。
withText
文字和图标一起显示。
collapseActionView
声明了这个操作视窗应该被折叠到一个按钮中,当用户选择这个按钮时,这个操作视窗展开。一般要配合ifRoom一起使用才会有效。
8 SearchView
1.在xml中使用,在menu中设置
<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:orderInCategory="100"
app:showAsAction="always"
app:actionViewClass="android.support.v7.widget.SearchView"
android:title="搜索"
/>
<item
android:id="@+id/action_share"
android:orderInCategory="100"
app:showAsAction="ifRoom"
android:title="分享"
/>
</menu>
在代码中对searchView进行操作
//重写 oncreateOptionsMenu
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main,menu);
//1.找到SearchView
MenuItem item = menu.findItem(R.id.action_search);
SearchView searchView = (SearchView) MenuItemCompat.getActionView(item);
//2. 使用官方api设置searchView
//设置一出来就直接呈现搜索框---SearchView
searchView.setIconified(false);
//进来就呈现搜索框并且不能被隐藏
searchView.setIconifiedByDefault(false);
//设置提交按钮是否可用(可见)
searchView.setSubmitButtonEnabled(true);
//监听焦点改变
searchView.setOnQueryTextFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
// TODO Auto-generated method stub
}
});
//searchView的关闭监听
searchView.setOnCloseListener(new SearchView.OnCloseListener() {
@Override
public boolean onClose() {
// TODO Auto-generated method stub
return false;
}
});
searchView.setOnSearchClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(DrawerlayoutActivity.this, "提交", 0).show();
}
});
//监听文本变化,调用查询
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String text) {
//提交文本
Toast.makeText(DrawerlayoutActivity.this, "提交文本:"+text, 0).show();
return false;
}
@Override
public boolean onQueryTextChange(String text) {
// 文本改变的时候回调
System.out.println("文本变化~~~~~"+text);
return false;
}
});
//3.自定义一些需要的样式 查看源码v7包中的abc_search_view.xml
// 找到id 通过findViewById找到控件,进行自定义
//自定义go_btn按钮
ImageView icon = (ImageView) searchView.findViewById(R.id.search_go_btn);
icon.setImageResource(R.drawable.ic_event);
icon.setVisibility(View.VISIBLE);
//自定义EditText
SearchView.SearchAutoComplete et = searchView.findViewById(R.id.search_src_text);
et.setHint("输入商品名或首字母");
et.setHintTextColor(Color.WHITE);
return true;
}
9.Palette 调色板
使用之前添加:
implementation 'com.android.support:palette-v7:27.1.1'
分析已有图片的色彩特性:主色调、鲜艳的颜色、柔和颜色等等
用法:
BitmapDrawable drawable = (BitmapDrawable) iv.getDrawable();
Bitmap bitmap = drawable.getBitmap();
//得到bitmap里面的的一些色彩信息---通过Palette类分析出来的
//异步任务---可能分析的图片会比较大或者颜色分布比较复杂,会耗时比较久,防止卡死主线程。
Palette.from(bitmap).generate(new PaletteAsyncListener() {
@Override
public void onGenerated(Palette palette) {
//暗、柔和的颜色
int darkMutedColor = palette.getDarkMutedColor(Color.BLUE);//如果分析不出来,则返回默认颜色
//亮、柔和
int lightMutedColor = palette.getLightMutedColor(Color.BLUE);
//暗、鲜艳
int darkVibrantColor = palette.getDarkVibrantColor(Color.BLUE);
//亮、鲜艳
int lightVibrantColor = palette.getLightVibrantColor(Color.BLUE);
//柔和
int mutedColor = palette.getMutedColor(Color.BLUE);
//鲜艳
int vibrantColor = palette.getVibrantColor(Color.BLUE);
//获取某种特性颜色的样品
// Swatch lightVibrantSwatch = palette.getLightVibrantSwatch();
Swatch lightVibrantSwatch = palette.getVibrantSwatch();
//谷歌推荐的:图片的整体的颜色rgb的混合值---主色调
int rgb = lightVibrantSwatch.getRgb();
//谷歌推荐:图片中间的文字颜色
int bodyTextColor = lightVibrantSwatch.getBodyTextColor();
//谷歌推荐:作为标题的颜色(有一定的和图片的对比度的颜色值)
int titleTextColor = lightVibrantSwatch.getTitleTextColor();
//颜色向量
float[] hsl = lightVibrantSwatch.getHsl();
//分析该颜色在图片中所占的像素多少值
int population = lightVibrantSwatch.getPopulation();
//使用分析出的颜色
tv_title.setBackgroundColor(getTranslucentColor(0.6f,rgb));
tv_title.setTextColor(titleTextColor);
tv1.setBackgroundColor(darkMutedColor);
tv1.setText("darkMutedColor");
tv2.setBackgroundColor(lightMutedColor);
tv2.setText("lightMutedColor");
tv3.setBackgroundColor(darkVibrantColor);
tv3.setText("darkVibrantColor");
tv4.setBackgroundColor(lightVibrantColor);
tv4.setText("lightVibrantColor");
tv5.setBackgroundColor(mutedColor);
tv5.setText("mutedColor");
tv6.setBackgroundColor(vibrantColor);
tv6.setText("vibrantColor");
}
});
}
protected int getTranslucentColor(float percent, int rgb) {
// 10101011110001111
int blue = Color.blue(rgb);
int green = Color.green(rgb);
int red = Color.red(rgb);
int alpha = Color.alpha(rgb);
// int blue = rgb & 0xff;
// int green = rgb>>8 & 0xff;
// int red = rgb>>16 & 0xff;
// int alpha = rgb>>>24;
alpha = Math.round(alpha*percent);
return Color.argb(alpha, red, green, blue);
}
10.TabLayout
1.TabLayout和ViewPager一起用:
tablayout.setupWithViewPager(mViewPager);
tablayout.setTabMode(TabLayout.MODE_FIXED);
在xml文件中:
app:tabMode 与 app:tabGravity 一起使用 可以改变标签的位置
tabMode :fixed 适应屏幕填充,不能滑动 SCROLLABLE:标签可以滑动
tabGravity:fill tab平均填充整个宽度 center tab居中显示
2.自定义Tab
for(int i = 0;i< tablayout.getTabCount();i++){
TabLayout.Tab tab = tablayout.getTabAt(i);
View view = View.inflate(this,layoutId,null);
TextView tv_title = view.findViewById(R.id.tv_title);
tv_title.setText(titles[I]);
tab.setCustomView(view);//自定义Tab
}
11 沉浸式设计
官方的沉浸式: 就是让整个App充满了整个屏幕,没有显示状态栏,没有显示底部导航栏,而现在我们常说的沉浸式就是状态栏和ToolBar颜色一致。
沉浸式开发, Api必须 > 4.4 Api低于4.4的官方系统无法做到沉浸式,但有些品牌的系统做了处理,Api < 4.4也可以做到沉浸式 所以在做沉浸式开发时,需要判断手机品牌和系统。
做沉浸式兼容时,需要判断当前SDK版本,4.4 和5.0是两个分割线
Android 4.4 之前,Android 的状态栏是黑色背景,无法修改。
Android 4.4 推出了透明状态栏的效果。
Android 5.0 提供了方法可以直接修改状态栏的颜色。
沉浸式ToolBar
1.Android5.0之后,
系统已经实现了沉浸式设计,想要修改状态栏的颜色可以有几种办法:
1)
直接修改主题中的colorPrimaryDark ,但这种方式会使整个App的主题都是这个颜色
2)
修改<item name="android:navigationBarColor">@color/colorPrimary</item>
这个属性需要写在style-21文件中
3)
代码中修改:
getWindow().setStatusBarColor(getResources().getColor(R.color.cardview_dark_background));
2.Android4.4 设置沉浸式状态栏
方法1:
在主题中设置:<item name="android:windowTranslucentStatus">true</item>
方法2:
1)设置状态栏透明
2)在toolbar中添加属性 android:fitsSystemWindows="true"
但是这种办法在布局中有scrollView时会有bug,布局中没有scrollView时可以使用。
如果非要使用的话,需要在最外层的布局中添加属性
android:fitsSystemWindows="true"
android:clipToPadding="true"
android:background="@color/colorPrimary"
把最外层布局的背景设置成和toolbar相同的颜色,再把状态栏和ToolBar之外的部分设置成背景色。
方法3:
1)设置状态栏透明
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
2)获取状态栏高度,把toolbar用一个布局包裹,布局的高度为toolbar.height+status.height 或者直接修改ToolBar的高度
给Toolbar设置 android:fitsSystemWindows="true"
第2步还可以设置ToolBar的paddingTop:
toolbar.setPadding(toolbar.getPaddingLeft(),toolbar.getPaddingTop()+getStatusBarHeight(this), toolbar.getPaddingRight(),toolbar.getPaddingBottom());
沉浸式NavigationBar 底部虚拟导航
1 Android5.0之后
1) 属性解决
在主题中设置
<item name="android:navigationBarColor">@color/colorPrimary</item>
在style-21上设置
2)代码解决
在setContentView之前设置
getWindow().setNavigationBarColor(color);
2 Android 4.4 - Android5.0
方法1
1.虚拟导航栏设置为透明
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
2.反射拿到底部导航栏的高度:
/**获取底部导航栏的高度*/
private int getNavigationBarHeight(Context context) {
return getSystemComponentDimen(this, "navigation_bar_height");
}
/**
* 获取状态栏的高度
* @param context
* @return
*/
private int getStatusBarHeight(Context context) {
// 反射手机运行的类:android.R.dimen.status_bar_height.
return getSystemComponentDimen(this, "status_bar_height");
}
public static int getSystemComponentDimen(Context context, String dimenName){
// 反射手机运行的类:android.R.dimen.status_bar_height.
int statusHeight = -1;
try {
Class<?> clazz = Class.forName("com.android.internal.R$dimen");
Object object = clazz.newInstance();
String heightStr = clazz.getField(dimenName).get(object).toString();
int height = Integer.parseInt(heightStr);
//dp--->px
statusHeight = context.getResources().getDimensionPixelSize(height);
} catch (Exception e) {
e.printStackTrace();
}
return statusHeight;
}
3.使用一个View放在底部,设置高度为1dp,如果能够获取到navigationBar的高度,就把高度设置给View view的颜色设置成想要设置成的颜色即可。
12.CardView
兼容性开发,准备两套布局,一套放在layout文件夹下,一套放在layout-v21文件夹下。5.0以上不需要设置contentPadding属性 而 4.x需要设置contentPadding属性
5.0以上
1.阴影一般为16dp
2.点击cardView有水波纹效果:android:foreground="?attr/selectableItemBackground"
android:clickable="true"
3.按下z轴位移效果
自定义动画 z轴平移动画
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:state_pressed="true">
<objectAnimator
android:duration="@android:integer/config_shortAnimTime"
android:propertyName="translationZ"
android:valueTo="15dp"
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="@drawable/z"
13.FloatingActionButton 悬浮动作按钮
兼容性开发:fab的宽高是58dp
1.需要写两个layout/layout-v21
layout-v21:添加layout_margin="16dp"
layout: 添加layout_margin="0dp"
2.水波纹效果
5.0以上才有水波纹的效果:
app:rippleColor="#f00"//水波纹的颜色
android:clickable="true"//注意 要设置该属性才会有水波纹效果
3.按下Z轴移动动画
app:pressedTranslationZ="12dp"//z轴动画
其他:
app:srcCompat="@android:drawable/ic_dialog_email"//设置src
android:backgroundTint="@color/colorAccent"//设置FAB
背景色
app:fabSize="mini" fab的大小
4.锚:anchor
app:layout_anchor=”@id/appbar”
app:layout_anchorGravity=”end|bottom|right”
app:layout_anchor设置这个属性可以让FloatingActionButton以某一个控件为基准调整位置,我们这里设置这个控件就是appbar
app:layout_anchorGravity
设置基于appbar控件的位置,我们这里设置了end|bottom|right
这样两行代码就可以将FloatingActionButton设置在appbar的右下角,并且行为会和appbar的滚动行为协作。
传统方式滑动RecyclerView实现ToolBar和Fab的显示和隐藏
14. CoordinatorLayout
继承自ViewGroup。
通过协调并调度里面的子控件或者布局来实现触摸(一般是指滑动)产生一些相关的动画效果。
可以通过设置view的Behavior来实现触摸的动画调度。
点击Fab弹出SnackBar,SnackBar弹出后会遮挡住Fab,需要把最外层的布局更改为CoordinatorLayout即可。
使用CoordinatorLayout 和 beHavior来实现随RecyclerView的滑动ToolBar和fab显示和隐藏效果。
原理:fab需要监听RecyclerView的滑动,给fab设置behavior,在behavior中有几个回调方法,当RecyclerView滑动时,就会回调那几个方法,把滑动状态告诉fab,我们一般所写的behavior都是继承自CoordinatorLayout.Behavior,而CoordinatorLayout位于最外层,当触摸事件发生时,CoordinatorLayout负责事件分发和传递,CoordinatorLayout可以通过behavior把事件传递给fab,同时作用到fab。
简单的说,CoordinatorLayout的作用就是监听滑动控件的滑动通过Behavior反馈到其他子控件并执行一些动画。滑动控件是指:
RecyclerView/NestedScrollView/ViewPager 而ListView/ScrollView不可以。
1# fab的显示和隐藏。
xml中 编写xml时要注意:
1.最外层控件使用CoordinatorLayout
2.fab要设置app:layout_behavior=“com.kimliu.materialdesign.behavior.FabBehavior”
属性值为自定义behavior
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
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.support.v7.widget.RecyclerView
android:id="@+id/rv"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:clipChildren="false"
android:paddingTop="?attr/actionBarSize"/>
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@android:color/white"
app:title="ToolBar"
/>
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:layout_width="58dp"
android:layout_height="58dp"
android:layout_margin="16dp"
app:layout_behavior="com.kimliu.materialdesign.behavior.FabBehavior"
android:layout_gravity="bottom|end"
android:src="@android:drawable/ic_delete"/>
</android.support.design.widget.CoordinatorLayout>
代码:只需要写一个自定义behavior即可
public class FabBehavior extends FloatingActionButton.Behavior {
public FabBehavior() {
}
public FabBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}
private boolean visible = true;
@Override
public boolean onStartNestedScroll(@NonNull CoordinatorLayout coordinatorLayout,
@NonNull FloatingActionButton child,
@NonNull View directTargetChild,
@NonNull View target, int axes, int type) {
return
super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, axes, type);
}
@Override
public boolean onStartNestedScroll(@NonNull CoordinatorLayout coordinatorLayout,
@NonNull FloatingActionButton child,
@NonNull View directTargetChild,
@NonNull View target, int axes) {
return axes == ViewCompat.SCROLL_AXIS_VERTICAL || super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, axes);
}
@Override
public void onNestedScroll(@NonNull CoordinatorLayout coordinatorLayout,
@NonNull FloatingActionButton child,
@NonNull View target, int dxConsumed,
int dyConsumed, int dxUnconsumed,
int dyUnconsumed, int type) {
super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, type);
// 当观察的view滑动的时候回调的
//根据情况执行动画
if(dyConsumed>0&&visible){
//向上滑动 且可见 隐藏
visible = false;//不可见
onHide(child);
}else if(dyConsumed<0){
//向下滑动
visible = true;
onShow(child);
}
}
public void onHide(FloatingActionButton fab) {
// 隐藏动画--属性动画
// toolbar.animate().translationY(-toolbar.getHeight()).setInterpolator(new AccelerateInterpolator(3));
ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) fab.getLayoutParams();
fab.animate().translationY(fab.getHeight()+layoutParams.bottomMargin).setInterpolator(new AccelerateInterpolator(3));
// ViewCompat.animate(fab).scaleX(0f).scaleY(0f).start();
}
public void onShow(FloatingActionButton fab) {
// 显示动画--属性动画
// toolbar.animate().translationY(0).setInterpolator(new DecelerateInterpolator(3));
ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) fab.getLayoutParams();
fab.animate().translationY(0).setInterpolator(new DecelerateInterpolator(3));
// ViewCompat.animate(fab).scaleX(1f).scaleY(1f).start();
}
}
2 Toolbar的滑动和隐藏
只需要在xml中设置,关键点:
1.ToolBar需要包裹在AppBarLayout中
2.AppBarLayout中的控件都有一个属性layout_scrollFlags
这个属性有5个值,分别是:
scroll:所有想滚动出屏幕的view都需要设置这个flag, 没有设置这个flag的view将被固定在屏幕顶部。
enterAlways:这个flag让任意向下的滚动都会导致该view变为可见,启用快速“返回模式”。
enterAlwaysCollapsed:假设你定义了一个最小高度(minHeight)同时enterAlways也定义了,那么view将在到达这个最小高度的时候开始显示,并且从这个时候开始慢慢展开,当滚动到顶部的时候展开完。
exitUntilCollapsed:当你定义了一个minHeight,此布局将在滚动到达这个最小高度的时候折叠。
snap:当一个滚动事件结束,如果视图是部分可见的,那么它将被滚动到收缩或展开。例如,如果视图只有底部25%显示,它将折叠。相反,如果它的底部75%可见,那么它将完全展开。
3.RecyclerView 要设置一个behavior app:layout_behavior="@string/appbar_scrolling_view_behavior"
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
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.support.v7.widget.RecyclerView
android:id="@+id/rv"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
/>
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize">
<android.support.v7.widget.Toolbar
app:layout_scrollFlags="scroll|enterAlways"
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@android:color/white"
app:title="ToolBar"
/>
</android.support.design.widget.AppBarLayout>
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:layout_width="58dp"
android:layout_height="58dp"
android:layout_margin="16dp"
app:layout_behavior="com.kimliu.materialdesign.behavior.FabBehavior"
android:layout_gravity="bottom|end"
android:src="@android:drawable/ic_delete"/>
</android.support.design.widget.CoordinatorLayout>
3.AppBarLayout和NestedScrollView结合使用
要在NestedScrollView上添加属性:
app:layout_behavior="@string/appbar_scrolling_view_behavior"
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.ricky.materialdesign.fab.animation.MainActivity" >
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior" >
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<android.support.v7.widget.CardView
android:layout_width="300dp"
android:layout_height="200dp"
android:layout_margin="0dp"
app:cardCornerRadius="20dp"
app:cardElevation="10dp"
app:contentPadding="5dp" >
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scaleType="centerCrop"
android:src="@drawable/tulips2" />
</android.support.v7.widget.CardView>
<android.support.v7.widget.CardView
android:layout_width="300dp"
android:layout_height="200dp"
android:layout_margin="0dp"
app:cardCornerRadius="20dp"
app:cardElevation="10dp"
app:contentPadding="5dp" >
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scaleType="centerCrop"
android:src="@drawable/tulips2" />
</android.support.v7.widget.CardView>
<android.support.v7.widget.CardView
android:layout_width="300dp"
android:layout_height="200dp"
android:layout_margin="0dp"
app:cardCornerRadius="20dp"
app:cardElevation="10dp"
app:contentPadding="5dp" >
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scaleType="centerCrop"
android:src="@drawable/tulips2" />
</android.support.v7.widget.CardView>
<android.support.v7.widget.CardView
android:layout_width="300dp"
android:layout_height="200dp"
android:layout_margin="0dp"
app:cardCornerRadius="20dp"
app:cardElevation="10dp"
app:contentPadding="5dp" >
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scaleType="centerCrop"
android:src="@drawable/tulips2" />
</android.support.v7.widget.CardView>
<android.support.v7.widget.CardView
android:layout_width="300dp"
android:layout_height="200dp"
android:layout_margin="0dp"
app:cardCornerRadius="20dp"
app:cardElevation="10dp"
app:contentPadding="5dp" >
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scaleType="centerCrop"
android:src="@drawable/tulips2" />
</android.support.v7.widget.CardView>
<android.support.v7.widget.CardView
android:layout_width="300dp"
android:layout_height="200dp"
android:layout_margin="0dp"
app:cardCornerRadius="20dp"
app:cardElevation="10dp"
app:contentPadding="5dp" >
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scaleType="centerCrop"
android:src="@drawable/tulips2" />
</android.support.v7.widget.CardView>
<android.support.v7.widget.CardView
android:layout_width="300dp"
android:layout_height="200dp"
android:layout_margin="0dp"
app:cardCornerRadius="20dp"
app:cardElevation="10dp"
app:contentPadding="5dp" >
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scaleType="centerCrop"
android:src="@drawable/tulips2" />
</android.support.v7.widget.CardView>
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize" >
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_scrollFlags="scroll|enterAlways"
android:background="?attr/colorPrimary" />
</android.support.design.widget.AppBarLayout>
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:layout_width="58dp"
android:layout_height="58dp"
android:layout_gravity="bottom|end"
android:layout_margin="16dp"
app:layout_behavior="com.ricky.materialdesign.fab.animation.FabBehavior"
android:onClick="rotate"
android:src="@drawable/ic_favorite_outline_white_24dp" />
</android.support.design.widget.CoordinatorLayout>
4.AppBarLayout和ViewPager结合使用
在ViewPager上设置属性:app:layout_behavior="@string/appbar_scrolling_view_behavior"
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent" xmlns:app1="http://schemas.android.com/apk/res/com.ricky.materialdesign.tablayout">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<android.support.v7.widget.Toolbar
app1:layout_scrollFlags="scroll|enterAlways"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
>
</android.support.v7.widget.Toolbar>
<android.support.design.widget.TabLayout
app1:layout_scrollFlags="scroll|enterAlways"
android:id="@+id/tablayout"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
app1:tabBackground="@color/material_deep_teal_200"
app:tabGravity="fill"
app:tabIndicatorColor="@color/colorPrimary_pink"
app:tabMode="scrollable"
app:tabSelectedTextColor="@color/colorPrimary_pinkDark"
app:tabTextColor="@color/colorPrimary_pink" />
</android.support.design.widget.AppBarLayout>
<android.support.v4.view.ViewPager
android:id="@+id/vp"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
</android.support.design.widget.CoordinatorLayout>
5.ToolBar的折叠与隐藏 CollapsingToolbarLayout的使用
CollapsingToolbarLayout需要包裹在AppBarLayout中使用。
注意:
1.既然是需要折叠toolbar的高度,那么CollapsingToolbarLayout的高度应设置为match_parent
2. AppBarLayout需要设置固定高度,要实现折叠效果,这个高度需要比toolbar的高度高
3.要实现折叠效果,需要给CollapsingToolbarLayout设置属性: app:layout_scrollFlags="scroll|enterAlways"
属性:
1.在CollapsingToolbarLayout中的子控件都会有这个属性:
app:layout_collapseMode="" 它有三个值:none/pin/parallax
none:没有任何效果,往上滑动的时候toolbar会首先被固定并推出去。
parallax:在滑动的时候这个View 会呈现 出 视觉特差效果
layout_collapseParallaxMultiplier:值为0-1之间 与parallax结合使用,
pin:当这个View到达 CollapsingToolbarLayout的底部的时候,这个View 将会被放置,即代替整个CollapsingToolbarLayout
2.contentScrim :当CollapsingToolbarLayout完全折叠后的背景颜色。
通常设置为:app:contentScrim=”?attr/colorPrimary”,这样当CollapsingToolbarLayout完全折叠后就会显示主题颜色。
3.expandedTitleMarginStart :布局张开的时候title与左边的距离
4.app:collapsedTitleGravity="center_horizonta"折叠后标题的gravity
- layout_scrollFlags: 设置滚动表现:
1、 Scroll, 表示手指向上滑动的时候,CollapsingToolbarLayout也会向上滚出屏幕并且消失,这个属性必须要有。
2、 exitUntilCollapsed: 表示这个layout会一直滚动离开屏幕范围,直到它收折成它的最小高度.
(如果设置了该属性,toolbar最后会停留在最高处,如果没有设置,toolbar最终会滚出屏幕)
3、enterAlways: 一旦手指向下滑动这个view就可见。
4、enterAlwaysCollapsed: 这个flag定义的是已经消失之后何时再次显示。假设你定义了一个最小高度(minHeight)同时enterAlways也定义了, 那么view将在到达 这个最小高度的时候开始显示,并且从这个时候开始慢慢展开,当滚动到顶部的时候展开完。
即当你的视图设置了minHeight属性的时候,那么视图只能以最小高度进入,只有当滚动视图到达顶部时才扩大到完整高度。
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout 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.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="250dp">
<android.support.design.widget.CollapsingToolbarLayout
app:layout_scrollFlags="scroll|exitUntilCollapsed|snap"
android:layout_width="match_parent"
app:statusBarScrim="@android:color/white"
app:contentScrim="@android:color/transparent"
android:layout_height="200dp">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@drawable/mao"
android:scaleType="fitXY"
app:layout_collapseMode="parallax"/>
<android.support.v7.widget.Toolbar
android:layout_width="match_parent"
app:title="nnn"
app:layout_collapseMode="pin"
app:navigationIcon="@drawable/ic_record"
app:titleTextColor="@android:color/white"
android:layout_height="?attr/actionBarSize">
</android.support.v7.widget.Toolbar>
</android.support.design.widget.CollapsingToolbarLayout>
<TextView
android:layout_width="match_parent"
android:layout_height="50dp"
android:text="TabLayout"
android:textSize="20sp"
android:textColor="@color/colorPrimary"
android:gravity="center"
/>
</android.support.design.widget.AppBarLayout>
<android.support.v4.widget.NestedScrollView
app:layout_behavior="@string/appbar_scrolling_view_behavior"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:text="NestedScrollView"
android:textSize="20sp"
android:textColor="@color/colorPrimary"
android:gravity="center"/>
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
</android.support.design.widget.CoordinatorLayout>
15.自定义Behavior
Behavior:抽象类,监听者,包裹在其中的所有子控件或者容器产生联动效果。
Behavior可以做到两种情况:
1.某个View需要监听另一个View的状态(比如:位置,大小,显示状态)
(需要重写layoutDependsOn/onDependentViewChanged)
demo: 当点击TextView时,另一个TextView跟着一起动
xml:
<?xml version="1.0" encoding="utf-8"?>
<!--1.最外层 CoordinatorLayout-->
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.kimliu.materialdesign.customBehavior.Main4Activity">
<TextView
android:layout_width="80dp"
android:layout_height="80dp"
android:background="@android:color/holo_blue_dark"
android:onClick="down"
android:layout_gravity="left|top"
android:text="被观察者----dependecy"/>
<!--2.在观察者上设置behavior-->
<TextView
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_gravity="right|top"
app:layout_behavior="com.kimliu.materialdesign.customBehavior.CustomBehavior"
android:background="@android:color/holo_orange_dark"
android:text="观察者"/>
</android.support.design.widget.CoordinatorLayout>
java:
public class CustomBehavior extends CoordinatorLayout.Behavior {
public CustomBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}
/**
*用来决定需要监听那些控件或容器的状态
* @param parent 父容器
* @param child 子控件 监听别人的view 如果有多个,每个都要配置behavior 然后用id判断是谁
* @param dependency 被观察者 被监听的view
* @return 两种条件:1.知道监听的是哪个控件 2. 知道这个控件是什么状态改变 如果这两个条件都知道 return true
* 如果返回true 会调 onDependentViewChanged()
*/
@Override
public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
return dependency instanceof TextView || super.layoutDependsOn(parent, child, dependency);
}
/**
*当被监听的view发生改变时回调,可以在其中做一些联动动画
* @param parent
* @param child
* @param dependency
* @return
*/
@Override
public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
int offset = dependency.getTop() - child.getTop();
ViewCompat.offsetLeftAndRight(child,-offset);
//child垂直方向平移offset
ViewCompat.offsetTopAndBottom(child,offset);
return super.onDependentViewChanged(parent, child, dependency);
}
}
2.某个View需要监听CoordinatorLayout里面所有控件的滑动状态。
(需要重写方法:onStartNestedScroll / onNestedScroll / onNestedPreScroll )
能被CoordinatorLayout捕获到滑动状态的控件有: NestedScrollView / RecyclerView / ViewPager
跟1相似,只是重写的方法不同而已,就不再赘述。