CoordinatorLayout 学习笔记

CoordinatorLayout 是 support:design 提供的一个重要布局,虽然由于产品设计的原因,我在正式项目中还没有机会使用到, 不过它提供了一些非常不错的效果,值得记录一下.

Android中处理事件分发还是挺麻烦的,需要从顶层开始,层层分发,拦截,响应.特别是涉及到多层嵌套的时候,需要判断什么时候拦截,拦截后怎么处理;什么时候放行...

CoordinatorLayout 可以用于调度协调子布局,实现联动效果.使用它,可以简化事件的处理.下面是 CoordinatorLayout 的一些常用方式.

CoordinatorLayout 和 FloatingActionButton

FloatingActionButton就是一个按钮,不过可以设置一些5毛特效.


<!-- 
      app:backgroundTint 默认填充色 
      app:rippleColor    点击时填充色 
      app:elevation      默认高度(高度越高,阴影越大)   
      app:pressedTranslationZ 点击时高度
      app:layout_anchor  依赖目标 
      app:layout_anchorGravity 相对依赖目标的位置 
 -->

Snackbar是从底部弹出的,当它显示的时候,如果遮住了按钮,体验就不太好,CoordinatorLayout 就可以解决这个问题,当使用 CoordinatorLayout 作为容器时,如果显示Snackbar,FloatingActionButton会有一个向上移动的效果

<?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"    
   android:fitsSystemWindows="true">    
  
   <android.support.v4.widget.NestedScrollView        
       android:id="@+id/scroll"        
       android:layout_width="match_parent"        
       android:layout_height="match_parent"        
       app:layout_behavior="@string/appbar_scrolling_view_behavior">        

       <TextView            
           android:layout_width="wrap_content"            
           android:layout_height="wrap_content"            
           android:layout_margin="@dimen/text_margin"            
           android:text="@string/large_text" />    

   </android.support.v4.widget.NestedScrollView>    
   <android.support.design.widget.FloatingActionButton        
      android:id="@+id/fab"        
      android:layout_width="wrap_content"        
      android:layout_height="wrap_content"        
      app:layout_behavior="com.heihei.hehe.coordinatorlayout.behavior.FloatButtonBehavior"        
      app:layout_anchor="@id/scroll"        
      app:layout_anchorGravity="bottom|end"         
      app:backgroundTint="#fff000"        
      app:rippleColor="@color/colorAccent"        
      app:elevation="6dp"        
      app:pressedTranslationZ="12dp"        
      android:layout_margin="@dimen/fab_margin"        
      app:srcCompat="@android:drawable/ic_dialog_email" />

</android.support.design.widget.CoordinatorLayout>

关于FloatingActionButton,还可以实现一些更好的特效,不过需要涉及到 CoordinatorLayout 的原理,放在靠后一些的地方.

CoordinatorLayout 和 AppBarLayout

CoordinatorLayout 和 AppBarLayout一起使用,可以实现的效果有很多

Toolbar的快速返回
<?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"    
   android:fitsSystemWindows="true">    

   <android.support.design.widget.AppBarLayout    
       android:id="@+id/app_bar"    
       android:layout_width="match_parent"    
       android:layout_height="wrap_content"    
       android:theme="@style/AppTheme.AppBarOverlay">    
      
      <android.support.v7.widget.Toolbar        
          android:id="@+id/toolbar"        
          app:layout_scrollFlags="scroll|enterAlways"        
          android:layout_width="match_parent"        
          android:layout_height="?attr/actionBarSize"        
          app:navigationIcon="?attr/homeAsUpIndicator"        
          app:popupTheme="@style/AppTheme.PopupOverlay" />

   </android.support.design.widget.AppBarLayout>
  
   <android.support.v4.widget.NestedScrollView        
       android:id="@+id/scroll"        
       android:layout_width="match_parent"        
       android:layout_height="match_parent"        
       app:layout_behavior="@string/appbar_scrolling_view_behavior">        

       <TextView            
           android:layout_width="wrap_content"            
           android:layout_height="wrap_content"            
           android:layout_margin="@dimen/text_margin"            
           android:text="@string/large_text" />    

   </android.support.v4.widget.NestedScrollView>   

</android.support.design.widget.CoordinatorLayout>

内容上划时,隐藏toolbar,下划时,显示toolbar,实现这个效果的关键在于

  1. 给滚动视图添加属性
    // 只有添加了该属性,才能让CoordinatorLayout 响应子视图的滚动事件
    // 注: 滚动视图必须要实现 NestedScrollingChild
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
  2. toolbar放在 AppBarLayout 里面(暂时可以认为,只有AppBarLayout 里面的控件,才能响应滑动)
  3. 个toolbar添加属性
    app:layout_scrollFlags="scroll|..."

关于 app:layout_scrollFlags 还可以设置一些其它的值,实现的效果也有区别

scroll   
    这个值必须有,没有这个值,控件会固定在屏幕上,不响应任何事件
snap    
    设置这个值后,toolbar不会停止在中间状态,结束状态要么完全显示,要么完全隐藏
enterAlways 
    向上滚动,隐藏该控件;向下的滚动,显示该控件
enterAlwaysCollapsed
    向上滚动,隐藏该控件;
    向下滑动:
        a.没有设置 minHeight,当滚动视图到达顶部,再显示该控件 
        b.设置 minHeight,先已最小高度出现,等滚动视图到达顶部,再显示该控件(同时需要设置 enterAlways  才生效)
 exitUntilCollapsed
    滚动视图向上滚动时,该控件会折叠在顶部,这个在后面再具体说明

toolbar 搭配 tablayout 使用

<?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"    
   android:fitsSystemWindows="true">    

   <android.support.design.widget.AppBarLayout    
       android:id="@+id/app_bar"    
       android:layout_width="match_parent"    
       android:layout_height="wrap_content"    
       android:theme="@style/AppTheme.AppBarOverlay">    
      
      <android.support.v7.widget.Toolbar        
          android:id="@+id/toolbar"        
          app:layout_scrollFlags="scroll|enterAlways"        
          android:layout_width="match_parent"        
          android:layout_height="?attr/actionBarSize"        
          app:navigationIcon="?attr/homeAsUpIndicator"        
          app:popupTheme="@style/AppTheme.PopupOverlay" />

      <android.support.design.widget.TabLayout    
          android:id="@+id/tabs"    
          android:layout_width="match_parent"    
          android:layout_height="wrap_content"    
          app:tabMode="scrollable"    
          app:tabIndicatorHeight="3dp"     
          app:tabTextColor="@color/color_ffffff"    
          app:tabSelectedTextColor="@color/colorAccent"    
          app:tabIndicatorColor="@color/colorAccent"/>

   </android.support.design.widget.AppBarLayout>
  
   <android.support.v4.widget.NestedScrollView        
       android:id="@+id/scroll"        
       android:layout_width="match_parent"        
       android:layout_height="match_parent"        
       app:layout_behavior="@string/appbar_scrolling_view_behavior">        

       <TextView            
           android:layout_width="wrap_content"            
           android:layout_height="wrap_content"            
           android:layout_margin="@dimen/text_margin"            
           android:text="@string/large_text" />    

   </android.support.v4.widget.NestedScrollView>   

</android.support.design.widget.CoordinatorLayout>

顺便解释下TabLayout的一些属性

  app:tabMode                模式,有两个值:scrollable(可滚动); fixed(固定的)
  app:tabIndicatorHeight     指示滑块的高度
  app:tabTextColor           文字的颜色
  app:tabSelectedTextColor   文字选中状态的颜色
  app:tabIndicatorColor      指示滑块的颜色

app:layout_scrollFlags="scroll|enterAlwaysCollapsed|enterAlways"

app:layout_scrollFlags="scroll|exitUntilCollapsed"

exitUntilCollapsed一般结合CollapsingToolbarLayout使用

<?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"    
   android:fitsSystemWindows="true">    

   <android.support.design.widget.AppBarLayout    
       android:id="@+id/app_bar"    
       android:layout_width="match_parent"    
       android:layout_height="250dp"
       android:fitsSystemWindows="true"  
       android:theme="@style/AppTheme.AppBarOverlay">    
      
      <android.support.design.widget.CollapsingToolbarLayout    
          android:id="@+id/toolbar_layout"    
          android:layout_width="match_parent"    
          android:layout_height="match_parent"   
          android:minHeight="?attr/actionBarSize"    
          android:fitsSystemWindows="true"    
          app:contentScrim="?attr/colorPrimary"    
          app:statusBarScrim="?attr/colorAccent"    
          app:title="title"   
          app:collapsedTitleGravity="left"    
          app:expandedTitleGravity="center_horizontal|bottom"    
          app:layout_scrollFlags="scroll|exitUntilCollapsed">    

          <ImageView        
             android:src="@mipmap/icon_bg_mine"        
             android:layout_width="match_parent"        
             android:layout_height="match_parent"        
             android:scaleType="centerCrop"        
             app:layout_collapseMode="parallax"/>    

          <android.support.v7.widget.Toolbar        
             android:id="@+id/toolbar"        
             app:layout_collapseMode="pin"        
             android:layout_width="match_parent"        
             android:layout_height="?attr/actionBarSize"        
             app:navigationIcon="?attr/homeAsUpIndicator"        
             app:popupTheme="@style/AppTheme.PopupOverlay" />

      </android.support.design.widget.CollapsingToolbarLayout>

   </android.support.design.widget.AppBarLayout>
  
   <android.support.v4.widget.NestedScrollView        
       android:id="@+id/scroll"        
       android:layout_width="match_parent"        
       android:layout_height="match_parent"        
       app:layout_behavior="@string/appbar_scrolling_view_behavior">        

       <TextView            
           android:layout_width="wrap_content"            
           android:layout_height="wrap_content"            
           android:layout_margin="@dimen/text_margin"            
           android:text="@string/large_text" />    

   </android.support.v4.widget.NestedScrollView>   

</android.support.design.widget.CoordinatorLayout>

上面的例子中,CollapsingToolbarLayout在滚动视图上滑时,会逐渐收缩折叠在屏幕上方(折叠高度受最小高度影响). 同时 CollapsingToolbarLayout 的子视图可以设置折叠模式

  // 折叠模式 
  app:layout_collapseMode      
  有两个值:
       parallax ->  视差模式,就是上面的图片的变化效果
       pin      ->  固定模式,在折叠的时候最后固定在顶端

  // 视差效果
  app:layout_collapseParallaxMultiplier   
  范围[0.0,1.0],值越大视差越大

CollapsingToolbarLayout 中使用到的几个属性也解释一下:

  //折叠后的背景色  -> setContentScrim(Drawable)
  app:contentScrim="?attr/colorPrimary"   
  // 必须设置透明状态栏才有效  -> setStatusBarScrim(Drawable)     
  app:statusBarScrim="?attr/colorAccent"    
  // 标题  
  app:title="title"
  // 折叠后的标题位置
  app:collapsedTitleGravity="left"
  // 打开时的标题位置
  app:expandedTitleGravity="center_horizontal|bottom"

PS: AppbarLayout 的展开和关闭是可以通过代码控制的

 appbarLayout.setExpanded(true,false);
CoordinatorLayout 和 BottomSheet

通过 CoordinatorLayout 也可以实现底部弹窗的效果,并且效果更好

  <android.support.v4.widget.NestedScrollView    
     android:id="@+id/scroll"    android:layout_width="match_parent"    
     android:layout_height="300dp"    
     app:behavior_peekHeight="0dp"    
     app:behavior_hideable="true"    
     app:layout_behavior="@string/bottom_sheet_behavior">    

    <TextView        
       android:layout_width="wrap_content"        
       android:layout_height="wrap_content"        
       android:layout_margin="@dimen/text_margin"        
       android:text="@string/large_text" />

  </android.support.v4.widget.NestedScrollView>

可以看到,在 CoordinatorLayout 中,只要给某个视图指定属性

   app:layout_behavior="@string/bottom_sheet_behavior"

就可以将该视图变为底部菜单的形式,同时可以通过其它几个属性改变菜单的规则

  // 关闭时的高度
  app:behavior_peekHeight="0dp"    
  // 是否可以完全隐藏,如果指定为 false,那么将最少已上面的高度显示
  app:behavior_hideable="true"   

代码中控制

  // 初始化
  sheetBehavior = BottomSheetBehavior.from(findViewById(R.id.scroll));

  // 打开
  sheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
  //关闭
  sheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
  //隐藏
  sheetBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
 // 状态监听
 sheetBehavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {    
     @Override    
     public void onStateChanged(@NonNull View bottomSheet, int newState) {        
        // 状态改变时回调       
        if(newState == BottomSheetBehavior.STATE_EXPANDED){            
            Toast.makeText(bottomSheet.getContext(),"打开了",Toast.LENGTH_SHORT).show();       
        }else if(newState == BottomSheetBehavior.STATE_COLLAPSED){            
            Toast.makeText(bottomSheet.getContext(),"关闭了",Toast.LENGTH_SHORT).show();        
        }else if(newState == BottomSheetBehavior.STATE_HIDDEN){            
            Toast.makeText(bottomSheet.getContext(),"隐藏了",Toast.LENGTH_SHORT).show();       
        }    
      }    

      @Override    
      public void onSlide(@NonNull View bottomSheet, float slideOffset) {        
          // 拖动时回调    
      }
 });
BottomSheetDialog

BottomSheetDialog 也是一种从底部弹起的对话框,使用起来和 CoordinatorLayout 没什么关系,这里也一起介绍

    // 直接通过构造方法初始化
    BottomSheetDialog bottomSheetDialog = new BottomSheetDialog(v.getContext());
    //设置内容
    bottomSheetDialog.setContentView(R.layout.content_scrolling);
    //显示
    bottomSheetDialog.show();

常规的一些用法到这里差不多了. 那么为什么 CoordinatorLayout 可以实现这些效果? 因为在上面我们好像并没有写什么代码,就是简单的谢谢布局,设置一下属性就可以了.

其实CoordinatorLayout自己并不控制View,所有的控制权都在Behavior. Behavior是 CoordinatorLayout 的内部内,是个抽象类. 它的子视图通过实现Behavior,然后CoordinatorLayout就可以进行协同管理.

前面我们使用了AppBarLayout 和 FloatingActionButton ,可以去源码里简单的看一下

  @CoordinatorLayout.DefaultBehavior(AppBarLayout.Behavior.class)
  public class AppBarLayout extends LinearLayout {

  ...
  
  @CoordinatorLayout.DefaultBehavior(FloatingActionButton.Behavior.class)
  public class FloatingActionButton extends VisibilityAwareImageButton {

   ...

可以看到,这两个控件都使用了Behavior,使用 Behavior的方式也有多种:

  • 注解绑定Behavior,当我们使用自定义控件的时候,就可以像上面一样,直接通过注解指定 Behavior.
  • 在XML中绑定Behavior
    app:layout_behavior="Behavior的包名"
  • 代码绑定Behavior
    CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) view.getLayoutParams();
    params.setBehavior(behavior);

同时,也可以通过自定义Behavior实现一些特殊的效果

// 如果该Behavior只想给某种控件使用,可以通过泛型控制, 当然也可以不指定,那么任何控件都可以使用
public class MyBehavior<T> extends CoordinatorLayout.Behavior {    

  // 构造方法必须要写,因为Behavior最终都是通过反射此构造方法初始化    
  // 可以带有自定义属性    
  public MyBehavior(Context context, AttributeSet attrs) {        
     super(context, attrs);   
  }    

  // 视图依赖(想想观察者模式),在这里可以指定具体的对象,也可以指定一个范围    
  // 比如这里指定了,只观察 AppBarLayout 的变化    
  @Override    
  public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {        
     return dependency instanceof AppBarLayout;    
  }    

  /**     
   * 依赖的对象发生了变化(观察者 onNext...),可以在这里做出相应的处理,比如位置改变,大小变化等     
   * @param child      使用此 Behavior 的控件     
   * @param dependency 观察的控件     
   */    
   @Override    
   public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {        
      return true;    
   }    

 /**************************以下是滑动事件的相关方法(无需声明依赖,不受依赖影响)*************************/    

   /**     
    * (嵌套)滚动事件开始前     
    * 通过返回值表示要不要响应本次滑动,只有这里返回true,后面的响应方法才会执行    
    * 比如这里,表示只响应垂直方向的滑动     
    * @param child             自己     
    * @param directTargetChild 发起滑动事件的控件     
    * @param target     
    */    
    @Override    
    public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, View child, View directTargetChild, View target, int nestedScrollAxes) {        
        return nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL;    
     }   

     /**     
      * (嵌套)滚动事件开始后,滚动视图获得滚动事件前     
      * @param dy        垂直方向滑动增量    
      * @param consumed  长度为二 , 水平和垂直方向消耗掉的滚动     
      */    
     @Override    
     public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, View child, View target, int dx, int dy, int[] consumed) {        
         //dy大于0是向上滚动 小于0是向下滚动    
     }   

     /**     
      * 滚动视图获得(嵌套)滚动事件后     
      * @param dyConsumed     竖直方向上滑动被消耗了多少     
      * @param dyUnconsumed   未消耗的     
      */    
      @Override    
      public void onNestedScroll(CoordinatorLayout coordinatorLayout, View child, View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {       
          if (dyConsumed > 0 && dyUnconsumed == 0) {            
              // 上滑       
          }       
          if (dyConsumed == 0 && dyUnconsumed > 0) {           
             // 到边了, 还在上滑        
          }       
          if (dyConsumed < 0 && dyUnconsumed == 0) {           
             // 下滑       
          }        
          if (dyConsumed == 0 && dyUnconsumed < 0) {            
             // 到边了, 还在下滑        
          }    
       }    

       // (嵌套)滚动事件结束后    
       @Override    
       public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, View child, View target) {   
       }    

       // 快速滑动开始前    
       @Override    
       public boolean onNestedPreFling(CoordinatorLayout coordinatorLayout, View child, View target, float velocityX, float velocityY) {        return false;   
       }    

       // 快速滑动    
       @Override    
       public boolean onNestedFling(CoordinatorLayout coordinatorLayout, View child, View target, float velocityX, float velocityY, boolean consumed) {        return false;    
       }
 }

自定义Behavior的套路就在上面,注释很详细了,下面是几个具体的例子

1.FloatingActionButton 在向上滚动是隐藏,向下滚动时出现

public class FloatButtonBehavior extends FloatingActionButton.Behavior {    
 
  // 构造方法必须有,使用的时候,会通过反射调用该构造方法实例化, 如果没有,会保错    
  public FloatButtonBehavior(Context context, AttributeSet attrs) {        
      super();    
  }    

  @Override    
  public boolean onStartNestedScroll(final CoordinatorLayout coordinatorLayout, final FloatingActionButton child,  final View directTargetChild, final View target, final int nestedScrollAxes) {        
    // Ensure we react to vertical scrolling        
    return nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL || super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, nestedScrollAxes);    
  }    

  //上滑隐藏,下滑显示    
  @Override    
  public void onNestedScroll(CoordinatorLayout coordinatorLayout, FloatingActionButton child, View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {        
      super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed,dyUnconsumed);        
      if (dyConsumed > 0 && child.getVisibility() == View.VISIBLE) {            
            child.hide();        
      } else if (dyConsumed < 0 && child.getVisibility() != View.VISIBLE) {            
            child.show();        
      }    
   }
}

2.底部菜单的上滑隐藏

 public class BottomNavigationBehavior extends CoordinatorLayout.Behavior<View> {    

    private ObjectAnimator outAnimator,inAnimator;    

    public BottomNavigationBehavior(Context context, AttributeSet attrs) {        
        super(context, attrs);   
    }    

    // 垂直滑动    
    @Override    
    public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, View child, View directTargetChild, View target, int nestedScrollAxes) {        
       return nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL;    
    }    

    @Override    
    public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, View child, View target, int dx, int dy, int[] consumed) {        
        if(dy > 0){//上滑隐藏           
            if(outAnimator == null){                
                outAnimator = ObjectAnimator.ofFloat(child,"translationY",0,child.getHeight());                
                outAnimator.setDuration(200);            
            }            
            if(!outAnimator.isRunning() && child.getTranslationY() <= 0){                
                outAnimator.start();            
            }        
         }else if(dy < 0){//下滑显示            
           if(inAnimator == null){               
               inAnimator = ObjectAnimator.ofFloat(child,"translationY",child.getHeight(),0);                
               inAnimator.setDuration(200);            
            }            
            if(!inAnimator.isRunning() && child.getTranslationY() >= child.getHeight()){               
               inAnimator.start();            
            }        
          }    
     }
 }

上面的例子用到了 support:design:25.0.0 里的一个新控件 BottomNavigationView

使用方式如下:

  <!-- 
     app:itemBackground  按钮背景
     app:itemIconTint         图标颜色
     app:itemTextColor      文字颜色
     app:menu                   菜单
  -->
  <android.support.design.widget.BottomNavigationView    
      android:id="@+id/navigation"    
      android:layout_width="match_parent"    
      android:layout_height="wrap_content"    
      android:layout_gravity="bottom"    
      app:layout_behavior="com.heihei.hehe.coordinatorlayout.behavior.BottomNavigationBehavior"    
      app:itemBackground="@color/color_1ec859"    
      app:itemIconTint="@drawable/tab_text_color_selector"    
      app:itemTextColor="@drawable/tab_text_color_selector"    
      app:menu="@menu/menu_navigation"/>

menu:

 <?xml version="1.0" encoding="utf-8"?>
 <menu xmlns:android="http://schemas.android.com/apk/res/android">    
    <item        
      android:id="@+id/item1"        
      android:checked="true"        
      android:icon="@android:drawable/stat_notify_chat"        
      android:title="Message"/>    

   <item        
      android:id="@+id/item2"        
      android:icon="@android:drawable/stat_notify_error"        
      android:title="Call"/>    

   <item        
      android:id="@+id/item3"        
      android:icon="@android:drawable/stat_notify_more"        
      android:title="Contact"/>    
 
   <item        
      android:id="@+id/item4"        
      android:icon="@android:drawable/stat_notify_sync"        
      android:title="aaa"/>

 </menu>

代码中设置:

  navigationView = (BottomNavigationView) findViewById(R.id.navigation);
  navigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {    

     @Override    
     public boolean onNavigationItemSelected(@NonNull MenuItem item) {        
          switch (item.getItemId()){            
              case R.id.item1:                
                 navigationView.setItemBackgroundResource(R.color.color_1ec859);                
                 break;            
              case R.id.item2:                
                 navigationView.setItemBackgroundResource(R.color.color_3b93eb);                
                 break;            
              case R.id.item3:                
                 navigationView.setItemBackgroundResource(R.color.color_ffa973);                
                break;            
              case R.id.item4:                
                 navigationView.setItemBackgroundResource(R.color.color_ffbc00);                
               break;       
            }        
           Toast.makeText(BottomNavigationActivity.this, item.getTitle(), Toast.LENGTH_SHORT).show();        
           return false;    
      }
   });

3.头像动画

 public class HeaderImageBehavior extends CoordinatorLayout.Behavior {    

     private float distanceY;    

     public HeaderImageBehavior(Context context, AttributeSet attrs) {        
          super(context, attrs);        
          TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.HeadBehavior);        
          distanceY = a.getDimension(R.styleable.HeadBehavior_openHeight,dip2px(context,250))//750        -a.getDimension(R.styleable.HeadBehavior_closeHeight,dip2px(context,56));//168       
          a.recycle();   
      }    

      @Override    
      public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {        
         return dependency instanceof AppBarLayout;    
      }        

      @Override    
      public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {        
         float p = Math.abs(dependency.getY())*1f/distanceY;        child.setScaleX(1- p/2);       
        child.setScaleY(1- p/2);        
        child.setTranslationX(-child.getLeft()*p);        
        return true;    
      }    

      private int dip2px(Context context, float dpValue) {       
         final float scale = context.getResources().getDisplayMetrics().density;        
         return (int) (dpValue * scale + 0.5f);   
      }
   }

CoordinatorLayout 还可以实现更复杂的效果,能力有限,就整理到这里了

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,425评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,058评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,186评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,848评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,249评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,554评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,830评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,536评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,239评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,505评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,004评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,346评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,999评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,060评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,821评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,574评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,480评论 2 267

推荐阅读更多精彩内容