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;
            }
        });

 

  

  

posted @ 2017-05-03 18:01  Lammy  阅读(2706)  评论(0编辑  收藏  举报