MD - 简单控件使用以及自定义Behavior

1.概述

  Material Design从Android5.0开始引入的,是一种全新的设计语言(翻译为“原材料设计”),其实是谷歌提倡的一种设计风格、理念、原则。最近发现现在很多 app 包括最早的知乎没有使用了,今天再来回顾一下控件的简单使用,效果非常简单,先上张图。

    效果展示
                在这里插入图片描述

2.控件介绍

  2.1 CoordinatorLayout:用来协调子控件,结合AppBarLayout,FloatingActionButton等使用
  2.2 SwipeRefreshLayout:提供的下拉刷新的效果,里面只能包一个子控件可以包任意控件RecyclerView,ScrollView ,ListView。
  2.3 ToolBar:加强版的ActionBar
  2.4 TabLayout:导航书签,可以结合ViewPager使用
		app:tabIndicatorColor            导航书签下方指示线的颜色
    	app:tabIndicatorHeight           指示线的高度
    	app:tabSelectedTextColor         导航书签选中时的字体颜色
     	app:tabMode="fixed"              显示模式
              		1:fixed             全部展示
                    2. scrollable        滑动展示
  2.5 CollapsingToolBarLayout:折叠布局,结合ToolBar使用
		android:minHeight					 最小高度
		app:layout_scrollFlags				 模式
					scroll 				 	 想滚动就必须设置这个。
					enterAlways				 实现quick return效果, 当向下移动时,立即显示View(比如Toolbar)。
					exitUntilCollapsed		 向上滚动时收缩View,但可以固定Toolbar一直在上面。
					enterAlwaysCollapsed	 当你的View已经设置minHeight属性又使用此标志时,你的View只能以最小高度进入,只有当滚动视图到达顶部时才扩大到完整高度。
		app:contentScrim					 当完全CollapsingToolbarLayout折叠(收缩)后的背景颜色。
		app:expandedTitleGravity			 Toolbar的title显示位置
		app:expandedTitleMargin				 设置显示位置设置Margin值
		app:layout_collapseParallaxMultiplier视差因子   0-1之间
		app:layout_collapseMode              视差模式
		            pin:·					 固定模式
		            parallax: 				 折叠效果
  2.6 SnackBar:MD风格的Toast,底部弹出
  2.7 TextInputLayout:自带错误提示的文本框

  等等,不一一介绍了,今天只是简单的使用,想了解,可以去官网查看

3.效果实现

  3.1 CoordinatorLayout 和 Behavior 介绍

    CoordinatorLayout是什么? 看一下官方的介绍:Interaction behavior plugin for child views of (子视图的交互行为插件)
    Behavior 实现了用户的一个或者多个交互行为,它们可能包括拖拽、滑动、快滑或者其他一些手势。Behavior 是一个顶层抽象类,其他的一些具体行为的 Behavior 都是继承自这个类。

	<?xml version="1.0" encoding="utf-8"?>
	<LinearLayout 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:orientation="vertical"
	    tools:context=".one_18.BehaviorActivity">
	
	    <androidx.coordinatorlayout.widget.CoordinatorLayout
	        android:layout_width="match_parent"
	        android:layout_height="match_parent">
	
	    <com.google.android.material.appbar.AppBarLayout
	        android:layout_width="match_parent"
	        android:layout_height="wrap_content">
	
	        <ImageView
	            android:layout_width="match_parent"
	            android:layout_height="200dp"
	            android:background="@color/blue"
	            app:layout_scrollFlags="scroll|enterAlways|snap"/>
	
	<!--        <androidx.appcompat.widget.Toolbar-->
	<!--            android:id="@+id/tool_bar"-->
	<!--            android:layout_width="match_parent"-->
	<!--            android:layout_height="wrap_content"-->
	<!--            app:title="知乎首页"-->
	<!--            app:titleTextColor="#ffffff"-->
	<!--            app:layout_scrollFlags="scroll|enterAlways|snap">-->
	<!--        </androidx.appcompat.widget.Toolbar>-->
	    </com.google.android.material.appbar.AppBarLayout>
	
	<androidx.recyclerview.widget.RecyclerView
	    android:id="@+id/rv"
	    android:layout_width="match_parent"
	    android:layout_height="match_parent"
	    app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
	    
	    <com.google.android.material.floatingactionbutton.FloatingActionButton
	        android:id="@+id/fab"
	        android:layout_width="wrap_content"
	        android:layout_height="wrap_content"
	        android:layout_gravity="bottom|end"
	        android:layout_marginBottom="70dp"
	        android:layout_marginRight="16dp"
	        android:layout_marginEnd="16dp"
	        app:layout_behavior=".one_18.TranslationBehavior"
	        android:src="@mipmap/ic_launcher">
	    </com.google.android.material.floatingactionbutton.FloatingActionButton>
	
	
	    <LinearLayout
	        android:layout_width="match_parent"
	        android:layout_height="50dp"
	        android:orientation="horizontal"
	        android:background="@android:color/white"
	        app:layout_behavior="@string/bottom_sheet_behavior">
	
	        <ImageView
	            android:layout_width="0dp"
	            android:layout_height="match_parent"
	            android:layout_weight="1"
	            android:src="@mipmap/ic_launcher"/>
	        <ImageView
	            android:layout_width="0dp"
	            android:layout_height="match_parent"
	            android:layout_weight="1"
	            android:src="@mipmap/ic_launcher"/>
	        <ImageView
	            android:layout_width="0dp"
	            android:layout_height="match_parent"
	            android:layout_weight="1"
	            android:src="@mipmap/ic_launcher"/>
	        <ImageView
	            android:layout_width="0dp"
	            android:layout_height="match_parent"
	            android:layout_weight="1"
	            android:src="@mipmap/ic_launcher"/>
	    </LinearLayout>
	
	</androidx.coordinatorlayout.widget.CoordinatorLayout>
	</LinearLayout>

看一下Behaviror的方法作用

 /**
     * 表示是否给应用了Behavior 的View 指定一个依赖的布局,通常,当依赖的View 布局发生变化时
     * 不管被被依赖View 的顺序怎样,被依赖的View也会重新布局
     * @param parent
     * @param child 绑定behavior 的View
     * @param dependency   依赖的view
     * @return 如果child 是依赖的指定的View 返回true,否则返回false
     */
    @Override
    public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
        return super.layoutDependsOn(parent, child, dependency);
    }

    /**
     * 当被依赖的View 状态(如:位置、大小)发生变化时,这个方法被调用
     * @param parent
     * @param child
     * @param dependency
     * @return
     */
    @Override
    public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
        return super.onDependentViewChanged(parent, child, dependency);
    }

    /**
     *  当coordinatorLayout 的子View试图开始嵌套滑动的时候被调用。当返回值为true的时候表明
     *  coordinatorLayout 充当nested scroll parent 处理这次滑动,需要注意的是只有当返回值为true
     *  的时候,Behavior 才能收到后面的一些nested scroll 事件回调(如:onNestedPreScroll、onNestedScroll等)
     *  这个方法有个重要的参数nestedScrollAxes,表明处理的滑动的方向。
     *
     * @param coordinatorLayout 和Behavior 绑定的View的父CoordinatorLayout
     * @param child  和Behavior 绑定的View
     * @param directTargetChild
     * @param target
     * @param nestedScrollAxes 嵌套滑动 应用的滑动方向,看 {@link ViewCompat#SCROLL_AXIS_HORIZONTAL},
     *                         {@link ViewCompat#SCROLL_AXIS_VERTICAL}
     * @return
     */
    @Override
    public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, View child, View directTargetChild, View target, int nestedScrollAxes) {
        return super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, nestedScrollAxes);
    }

    /**
     * 嵌套滚动发生之前被调用
     * 在nested scroll child 消费掉自己的滚动距离之前,嵌套滚动每次被nested scroll child
     * 更新都会调用onNestedPreScroll。注意有个重要的参数consumed,可以修改这个数组表示你消费
     * 了多少距离。假设用户滑动了100px,child 做了90px 的位移,你需要把 consumed[1]的值改成90,
     * 这样coordinatorLayout就能知道只处理剩下的10px的滚动。
     * @param coordinatorLayout
     * @param child
     * @param target
     * @param dx  用户水平方向的滚动距离
     * @param dy  用户竖直方向的滚动距离
     * @param consumed
     */
    @Override
    public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, View child, View target, int dx, int dy, int[] consumed) {
        super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed);
    }

    /**
     * 进行嵌套滚动时被调用
     * @param coordinatorLayout
     * @param child
     * @param target
     * @param dxConsumed target 已经消费的x方向的距离
     * @param dyConsumed target 已经消费的y方向的距离
     * @param dxUnconsumed x 方向剩下的滚动距离
     * @param dyUnconsumed y 方向剩下的滚动距离
     */
    @Override
    public void onNestedScroll(CoordinatorLayout coordinatorLayout, View child, View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
        super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed);
    }

    /**
     *  嵌套滚动结束时被调用,这是一个清除滚动状态等的好时机。
     * @param coordinatorLayout
     * @param child
     * @param target
     */
    @Override
    public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, View child, View target) {
        super.onStopNestedScroll(coordinatorLayout, child, target);
    }

    /**
     * onStartNestedScroll返回true才会触发这个方法,接受滚动处理后回调,可以在这个
     * 方法里做一些准备工作,如一些状态的重置等。
     * @param coordinatorLayout
     * @param child
     * @param directTargetChild
     * @param target
     * @param nestedScrollAxes
     */
    @Override
    public void onNestedScrollAccepted(CoordinatorLayout coordinatorLayout, View child, View directTargetChild, View target, int nestedScrollAxes) {
        super.onNestedScrollAccepted(coordinatorLayout, child, directTargetChild, target, nestedScrollAxes);
    }

    /**
     * 用户松开手指并且会发生惯性动作之前调用,参数提供了速度信息,可以根据这些速度信息
     * 决定最终状态,比如滚动Header,是让Header处于展开状态还是折叠状态。返回true 表
     * 示消费了fling.
     *
     * @param coordinatorLayout
     * @param child
     * @param target
     * @param velocityX x 方向的速度
     * @param velocityY y 方向的速度
     * @return
     */
    @Override
    public boolean onNestedPreFling(CoordinatorLayout coordinatorLayout, View child, View target, float velocityX, float velocityY) {
        return super.onNestedPreFling(coordinatorLayout, child, target, velocityX, velocityY);
    }

    /**
     * 摆放子 View 的时候调用,可以重写这个方法对子View 进行重新布局
     */
    @Override
    public boolean onLayoutChild(CoordinatorLayout parent, View child, int layoutDirection) {
        return super.onLayoutChild(parent, child, layoutDirection);
    }
  3.2 自定义 Behavior 来实现 FloatingActionBar 的改变
	public class TranslationBehavior<T extends FloatingActionButton> extends CoordinatorLayout.Behavior<T> {
	    private static final String TAG = "TranslationBehavior";
	    
	    /**
	     * FloatingActionButton当前是否是显示状态
	     */
	    private boolean isOut = false;
	
	    public TranslationBehavior(Context context, AttributeSet attrs) {
	        super(context, attrs);
	    }
	
	    /**
	     *  注意:当coordinatorLayout 的子View试图开始嵌套滑动的时候被调用。当返回值为true的时候表明
	     *  coordinatorLayout 充当nested scroll parent 处理这次滑动,需要注意的是只有当返回值为true
	     *  的时候,Behavior 才能收到后面的一些nested scroll 事件回调(如:onNestedPreScroll、onNestedScroll等)
	     *  这个方法有个重要的参数nestedScrollAxes,表明处理的滑动的方向。
	     * @param coordinatorLayout 和Behavior 绑定的View的父CoordinatorLayout
	     * @param child  和Behavior 绑定的View
	     * @param directTargetChild
	     * @param target
	     * @param axes 嵌套滑动,滑动方向, {@link ViewCompat#SCROLL_AXIS_HORIZONTAL}
	     *                                 {@link ViewCompat#SCROLL_AXIS_VERTICAL}
	     * @return
	     */
	    @Override
	    public boolean onStartNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull T child, @NonNull View directTargetChild, @NonNull View target, int axes, int type) {
	        Log.e(TAG,"onStartNestedScroll");
	        return axes == ViewCompat.SCROLL_AXIS_VERTICAL;
	    }
	
	
	    /**
	     * 进行嵌套滚动时被调用
	     * @param coordinatorLayout
	     * @param child
	     * @param target
	     * @param dxConsumed    target 已经消费的x方向的距离
	     * @param dyConsumed    target 已经消费的y方向的距离  往上滑是正  往下滑是负
	     * @param dxUnconsumed  x 方向剩下的滚动距离
	     * @param dyUnconsumed  y 方向剩下的滚动距离
	     * @param type
	     */
	    @Override
	    public void onNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull T child, @NonNull View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int type) {
	        Log.e(TAG,"onNestedScroll");
	        super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, type);
	        Log.e(TAG,"dyConsumed:"+dyConsumed+"--------dyUnconsumed:"+dyUnconsumed);
	        //1.将我们的child(FloatingActionButton)进行隐藏
	        if (dyConsumed > 0){
	            if (!isOut){
	                //2.移动距离计算  bottomMargin + 自己的高度
	                int translationY = ((CoordinatorLayout.LayoutParams)child.getLayoutParams()).bottomMargin + child.getMeasuredHeight();
	                child.animate().translationY(translationY).setDuration(500).start();
	                //3.注意:这样设置后网上滑的时候会发现FloatingActionButton会反应慢,我们可以设置一个标志位
	                isOut = true;
	            }
	        }else {
	            if (isOut){
	                child.animate().translationY(0).setDuration(500).start();
	                isOut = false;
	            }
	        }
	    }
	}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值