CoordinatorLayout的使用,以及FloatingActionButton、snakebar的简单用法
一、CoordinatorLayout基本介绍
CoordinatorLayout作为“super-powered FrameLayout”基本实现两个功能: (demo是实现 http://blog.csdn.net/huachao1001/article/details/51554608 中的)
1、作为顶层布局
2、调度协调子布局
通过实验发现它也可以嵌套在其他的布局文件内,但它只能协调它的子布局。且与snakebar连着使用时,来控制弹出消息的位置,这个在后面介绍snakebar时讲。
基本用法
1、引入相应的库
使用CoordinatorLayout可以实现一些炫酷的动态效果,使用它首先得在app的gradle中引入相应的库:
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.3.0'
compile 'com.android.support:design:23.3.0'
}
node: 按网上的大多资料是compile 'com.android.support:design:22.2.1' , 这里我们引入的上面的支持库版本一致,否则会报错。
2、将CoordinatorLayout内部子控件或者布局设定behavior行为,该控件设定了监听其它的子控件或者布局来执行相应动作。下面给出简单的一个布局:
<?xml version="1.0" encoding="utf-8"?> <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.example.user.coordinatorlayouttest.MainActivity"> <Button android:id="@+id/btn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="#FFCC00" android:text="Hello" app:layout_behavior="com.example.user.coordinatorlayouttest.MyBehavior" /> <TextView android:layout_width="100dp" android:layout_height="100dp" android:layout_marginLeft="30dp" android:layout_marginTop="30dp" android:background="#3366CC" android:text="dependency" android:id="@+id/nice_to_me_to_you"/> </android.support.design.widget.CoordinatorLayout>
其中button就设定了behaviour,其实就是来监听一些控件的动作来执行相应的动作。
3、实现behaviour:
package com.example.user.coordinatorlayouttest; import android.content.Context; import android.support.design.widget.CoordinatorLayout; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.view.View; import android.widget.Button; import android.widget.TextView; /** * Created by user on 2017/5/3. */ public class MyBehavior extends CoordinatorLayout.Behavior<Button> { private int width; public MyBehavior(Context context, AttributeSet attrs) { super(context, attrs); DisplayMetrics display = context.getResources().getDisplayMetrics(); width = display.widthPixels; } @Override public boolean layoutDependsOn(CoordinatorLayout parent, Button child, View dependency) { //如果dependency是TextView的实例,说明它就是我们所需要的Dependency,只有返回true时候,下面的方法才会执行 return dependency instanceof TextView; } //每次dependency位置发生变化,都会执行onDependentViewChanged方法 @Override public boolean onDependentViewChanged(CoordinatorLayout parent, Button btn, View dependency) { //根据dependency的位置,设置Button的位置 int top = dependency.getTop(); int left = dependency.getLeft(); int x = width - left - btn.getWidth(); int y = top; setPosition(btn, x, y); return true; } private void setPosition(View v, int x, int y) { CoordinatorLayout.MarginLayoutParams layoutParams = (CoordinatorLayout.MarginLayoutParams) v.getLayoutParams(); layoutParams.leftMargin = x; layoutParams.topMargin = y; v.setLayoutParams(layoutParams); } }
这里设定了子布局中TextView的对象是dependency,当TextView的位置发生变化时,button的位置也会相应发生变化(竖直方向移动一致,横向方向是相反的)
4、定义dependency的位移方式,(其实大多dependency是自定义的控件),这里设计点触控,总结一下:点触控时候位移量最好用getRawX函数来获取,用getX是获得控件内部坐标,而控件本身在移动,函数执行有一定频率会导致位移量有丢失且图片抖动,用下面的方式就不会了:
textView = (TextView) findViewById(R.id.nice_to_me_to_you); textView.setOnTouchListener(new View.OnTouchListener() { int toLeft,toTop,sx ,sy; @Override public boolean onTouch(View v, MotionEvent event) { int action = event.getAction(); switch (action) { case MotionEvent.ACTION_DOWN: sx = (int) event.getRawX(); sy = (int) event.getRawY(); toLeft = textView.getLeft(); toTop = textView.getTop(); break; case MotionEvent.ACTION_MOVE: int sx2 = (int) event.getRawX(); int sy2 = (int) event.getRawY(); CoordinatorLayout.MarginLayoutParams layoutParams = (CoordinatorLayout.MarginLayoutParams) v.getLayoutParams(); layoutParams.leftMargin = toLeft + sx2 - sx; layoutParams.topMargin =toTop + sy2 - sy; v.setLayoutParams(layoutParams); break; } return true; } });
综合上面,CoordinatorLayout的用法介绍完毕了,但CoordinatorLayout通常与一些控件结合起来用,以达到一些特殊的效果,如:FloatingActionButton、snakebar、tabLayout,toolbar 、下面简单介绍FloatingActionButton、snakebar控件的基本用法:关于后面2个在后面的博客中再说。
二、snakebar 基本用法
snakebar也是与CoordinatorLayout一样需要引入compile 'com.android.support:design:23.3.0' ,其特点如下:(参考 http://blog.csdn.net/u013320868/article/details/51906896)
1)SnackBars 提供了一个轻量级的反馈操作,他们在屏幕的底部显示一条简短的信息,如果是较大的设备就显示在左下角。SnackBar出现在屏幕中所有其他元素的上方,同一时间只能显示一条SnackBar。
2)在超时或者用户在屏幕上完成了交互的时候SnackBar会自动消失,特别是在召唤了新的表层(意思是SnackBar本来是最外层的,然后在SnackBar上又新添加了一层)或者Activity的时候。SnackBar能在屏幕上侧滑。
3)SnackBar能包含一个action使用setAction方法
4)你可以通过它的CallBack来得知Snackbar是显示还是隐藏
与Toast进行比较,SnackBar有优势:
1.SnackBar可以自动消失,也可以手动取消(侧滑取消,但是需要在特殊的布局中,后面会仔细说)
2.SnackBar可以通过setAction()来与用户进行交互
3.通过CallBack我们可以获取SnackBar的状态
下面介绍一下
node:snakeBar通常与CoordinatorLayout连用,出现在CoordinatorLayout布局的下发,弹出一条消息,且可以设置一个action事件,但有时候没有CoordinatorLayout或者不在CoordinatorLayout布局内。下面的结论是通过实验得到的:
1、当布局中有CoordinatorLayout,不管引入snakeBar的控件是CoordinatorLayout 子控件还是CoordinatorLayout的子布局的子控件,它都会出现在CoordinatorLayout布局的最下面。
2、当引入snakebar的控件不在CoordinatorLayout内部时候,(即使有CoordinatorLayout,但引入snakebar控件不在其内)则snakebar会出现在最上层的framLayout的底部,即屏幕的底部。
下面给出个简单的例子:
布局文件:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <android.support.design.widget.CoordinatorLayout android:layout_width="match_parent" android:layout_height="200dp" android:id="@+id/f1" android:background="@android:color/darker_gray" > <android.support.design.widget.FloatingActionButton android:id="@+id/fab" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="end|bottom" android:layout_margin="16dp" android:background="@android:color/holo_red_light" /> </android.support.design.widget.CoordinatorLayout> </RelativeLayout>
然后在activity的onCreate()中写入下面方法后,当点击悬浮按钮的时候就会在200dp的位置出现snakebar,上面说到的引入snakebar按钮就是fab:
findViewById(R.id.fab).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Snackbar snackBar =Snackbar.make(view,"it is snackbar!",Snackbar.LENGTH_SHORT); //设置SnackBar背景颜色 snackBar.getView().setBackgroundColor(Color.RED); //设置按钮文字颜色 snackBar.setActionTextColor(Color.WHITE); //设置点击事件 snackBar.setAction("点击", new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(MainActivity.this,"It is Toast!",Toast.LENGTH_SHORT).show(); } }); //设置回调 snackBar.setCallback(new Snackbar.Callback() { @Override public void onDismissed(Snackbar snackbar, int event) { super.onDismissed(snackbar, event); Toast.makeText(MainActivity.this, "Snackbar dismiss", Toast.LENGTH_SHORT).show(); } @Override public void onShown(Snackbar snackbar) { super.onShown(snackbar); Toast.makeText(MainActivity.this, "Snackbar show", Toast.LENGTH_SHORT).show(); } }).show(); } });
可以看出,我们可以setAction来设定点击时间,而Snakebar.LEMGTH_SHORT是显示的时间,它有三种方式:
Snackbar.LENGTH_SHORT// 短时间显示,然后自动取消
Snackbar.LENGTH_LONG// 长时间显示,然后自动取消
Snackbar.LENGTH_INDEFINITE// 不消失显示,除非手动取消
且我们可以设定回调函数,当snakeBar显示和消失时候完成一些事情
3、FloatingActionButton
关于floatingActionButton介绍网上很多,它继承自ImageView,关于它的介绍主要集中在于设置背景色和阴影的设置上:
1、它也是来自上面所说的支持库,在build.grade文件中写上
compile 'com.android.support:design:23.2.0'
2、关于它的几个重要属性的介绍
1、app:borderWidth=""------------------边框宽度,通常设置为0 ,用于解决Android 5.X设备上阴影无法正常显示的问题
2、app:backgroundTint=""---------------按钮的背景颜色,不设置,默认使用theme中colorAccent的颜色 (默认颜色为主题中的colorAccent的颜色,关于如何自定义主题,以后的文章再讲)
3、app:rippleColor=""--------------------点击的边缘阴影颜色
4、app:elevation=""----------------------边缘阴影的宽度
5、app:pressedTranslationZ="16dp"-----点击按钮时,按钮边缘阴影的宽度,通常设置比elevation的数值大
3 可以看出属性是app开头的,因此在布局文件前面加上:
xmlns:app="http://schemas.android.com/apk/res-auto"
4、通过网上的设置background然后写一个drawable文件,写一个背景的选择器,在按下时候变色,如下:
<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_pressed="false" android:drawable="@color/floatBack1"></item> <item android:state_pressed="true" android:drawable="@color/floatBackPressed"></item> </selector>
发现将该drawable给background属性无效,给backgroundTint后变色了但不是我们设定的颜色,通过查看源码,发现setbackground源码如下:
@Override public void setBackgroundDrawable(Drawable background) { Log.i(LOG_TAG, "Setting a custom background is not supported."); } @Override public void setBackgroundResource(int resid) { Log.i(LOG_TAG, "Setting a custom background is not supported."); } @Override public void setBackgroundColor(int color) { Log.i(LOG_TAG, "Setting a custom background is not supported."); }
它本身没有实现这些函数,因此设置background属性是无效的,它的一些属性都是靠实现了ShadowViewDelegate接口的类完成的,而属性设置中的backgournd无效,智能通过backgroundTint来设定背景,而backgroundTint使用drawable里的文件颜色是改变了但不是我们设定的,(原因不是很清楚)因此在代码中来设定背景了:
比如下面我们设定当点击悬浮图标时候背景色跟着变化,点击完毕时候恢复原来颜色时,主要是设置了按键的OntouchListener
floatingActionButton = (FloatingActionButton) findViewById(R.id.fab); floatingActionButton.setBackgroundTintList(ColorStateList.valueOf(Color.parseColor("#ffff0000")));//设定默认色 floatingActionButton.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_MOVE: floatingActionButton.setBackgroundTintList(ColorStateList.valueOf(Color.parseColor("#ffffff00")));//手指按下时候变色 break; case MotionEvent.ACTION_UP: floatingActionButton.setBackgroundTintList(ColorStateList.valueOf(Color.parseColor("#ffff0000")));//手指离开时候恢复 break; } return true; } });