美文网首页安卓开发Android带修改-安卓深入学习
一个神奇的控件——Android CoordinatorLayo

一个神奇的控件——Android CoordinatorLayo

作者: jacky123 | 来源:发表于2016-07-14 22:49 被阅读2751次

    介绍

    CoordinatorLayout是用来协调其子view们之间动作的一个父view,而Behavior就是用来给CoordinatorLayout的子view们实现交互的。


    SUM

    1. CollapsingToolbarLayout_伸缩折叠工具

    参考:看,这个工具栏能伸缩折叠——Android CollapsingToolbarLayout使用介绍

    a. CollapsingToolbarLayout折叠或展开时,FloatingActionButton跟随运动并且大小相应变化.CollapsingToolbarLayout是专门用来实现子布局内不同元素响应滚动细节的布局。
    b. AppBarLayout是一种支持响应滚动手势的app bar布局(比如工具栏滚出或滚入屏幕);与AppBarLayout组合的滚动布局(Recyclerview、NestedScrollView等)需要设置app:layout_behavior="@string/appbar_scrolling_view_behavior"(上面代码中NestedScrollView控件所设置的)。没有设置的话,AppBarLayout将不会响应滚动布局的滚动事件。。
    c. CollapsingToolbarLayout和ScrollView一起使用会有滑动bug,注意要使用NestedScrollView来替代ScrollView。

    Android studio中有一个Activity模板叫ScrollingActivity,它实现的就是简单的可折叠工具栏。



    ScrollingActivity的布局代码如下:

    <?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"
        tools:context="com.jack.jack_junit_demo.ScrollingActivity">
    
        <android.support.design.widget.AppBarLayout
            android:id="@+id/app_bar"
            android:fitsSystemWindows="true"
            android:layout_height="180dp"
            android:layout_width="match_parent"
            android:theme="@style/AppTheme.AppBarOverlay">
    
            <android.support.design.widget.CollapsingToolbarLayout
                android:id="@+id/toolbar_layout"
                android:fitsSystemWindows="true"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                app:layout_scrollFlags="scroll|exitUntilCollapsed"
                app:contentScrim="?attr/colorPrimary">
    
                <android.support.v7.widget.Toolbar
                    android:id="@+id/toolbar"
                    android:layout_height="?attr/actionBarSize"
                    android:layout_width="match_parent"
                    app:layout_collapseMode="pin"
                    app:popupTheme="@style/AppTheme.PopupOverlay" />
    
            </android.support.design.widget.CollapsingToolbarLayout>
        </android.support.design.widget.AppBarLayout>
        <!--可以include 抽取出来-->
        <android.support.v4.widget.NestedScrollView                    
                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"
            android:layout_margin="@dimen/fab_margin"
            app:layout_anchor="@id/app_bar"
            app:layout_anchorGravity="bottom|end"
            android:src="@android:drawable/ic_dialog_email" />
    
    </android.support.design.widget.CoordinatorLayout>
    
    
    2.AppBarLayout的子布局有5种滚动标识

    就是上面代码CollapsingToolbarLayout中配置的app:layout_scrollFlags属性:

    1. scroll:将此布局和滚动时间关联。这个标识要设置在其他标识之前,没有这个标识则布局不会滚动且其他标识设置无效。
    2. enterAlways:任何向下滚动操作都会使此布局可见。这个标识通常被称为“快速返回”模式。
    3. enterAlwaysCollapsed:假设你定义了一个最小高度(minHeight)同时enterAlways也定义了,那么view将在到达这个最小高度的时候开始显示,并且从这个时候开始慢慢展开,当滚动到顶部的时候展开完。
    4. exitUntilCollapsed:当你定义了一个minHeight,此布局将在滚动到达这个最小高度的时候折叠。
    5. snap:当一个滚动事件结束,如果视图是部分可见的,那么它将被滚动到收缩或展开。例如,如果视图只有底部25%显示,它将折叠。相反,如果它的底部75%可见,那么它将完全展开。
    3.CollapsingToolbarLayout的contentScrim、statusBarScrim 属性。
    • app:contentScrim设置折叠时工具栏布局的颜色
    • app:statusBarScrim设置折叠时状态栏的颜色。
      默认contentScrim是colorPrimary的色值,statusBarScrim是colorPrimaryDark的色值。这个后面会用到。
    4.CollapsingToolbarLayout子布局设置折叠模式,app:layout_collapseMode**
    • off:这个是默认属性,布局将正常显示,没有折叠的行为。
    • pin:CollapsingToolbarLayout折叠后,此布局将固定在顶部。
    • parallax:CollapsingToolbarLayout折叠时,此布局也会有视差折叠效果

    当CollapsingToolbarLayout的子布局设置了parallax模式时,我们还可以通过
    app:layout_collapseParallaxMultiplier
    设置视差滚动因子,值为:0~1。

    5.FloatingActionButton

    FloatingActionButton这个控件通过app:layout_anchor这个设置锚定在了AppBarLayout下方。FloatingActionButton源码中有一个Behavior方法,当AppBarLayout收缩时,FloatingActionButton就会跟着做出相应变化。关于CoordinatorLayout和Behavior,我下一篇文章会和大家一起学习。

    B站很早就开源了一个弹幕引擎,还起了个狂拽酷炫吊炸天的名字叫“烈焰弹幕使 ”(一看就是二次元程序猿们的作品→_→),源码在github上,项目名叫DanmakuFlameMaster


    2. 自定义CoordinatorLayout的Behavior

    自定义Behavior模仿知乎
    参考:http://www.jianshu.com/p/488283f74e69

    1.先看下布局

    <?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"
        android:id="@+id/behavior_demo_coordinatorLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <android.support.design.widget.AppBarLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:theme="@style/AppTheme.AppBarOverlay">
    
            <android.support.v7.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                app:layout_scrollFlags="scroll|enterAlways|snap"
                android:background="?attr/colorPrimary" />
        </android.support.design.widget.AppBarLayout>
    
        <android.support.v4.widget.SwipeRefreshLayout
            android:id="@+id/behavior_demo_swipe_refresh"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_behavior="@string/appbar_scrolling_view_behavior">
            <android.support.v7.widget.RecyclerView
                android:id="@+id/behavior_demo_recycler"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                />
        </android.support.v4.widget.SwipeRefreshLayout>
    
        <!--行为,依赖于自定义Beavior-->
        <android.support.design.widget.FloatingActionButton
            android:id="@+id/fab"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginRight="16dp"
            android:layout_marginBottom="72dp"
            android:src="@android:drawable/ic_dialog_email"
            app:layout_behavior="com.example.zcp.coordinatorlayoutdemo.behavior.MyFabBehavior"
            android:layout_gravity="bottom|right" />
    
        <!--行为,依赖于自定义Beavior-->
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:layout_gravity="bottom"
            android:background="@color/colorPrimary"
            android:gravity="center"
            app:layout_behavior="com.example.zcp.coordinatorlayoutdemo.behavior.MyBottomBarBehavior">
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:textColor="#ffffff"
                android:text="这是一个底栏"/>
        </LinearLayout>
    
    </android.support.design.widget.CoordinatorLayout>
    
    

    SwipeRefreshLayout、FloatingActionButton和当做底栏的LinearLayout上有一个app:layout_behavior配置。

    SwipeRefreshLayout配置的"@string/appbar_scrolling_view_behavior"是系统提供的,用来使滑动控件与AppBarLayout互动。

    FloatingActionButton和底栏上配置的是我们接下来要自定义的Behavior。

    先看FloatingActionButton的Behavior。

    public class MyFabBehavior extends CoordinatorLayout.Behavior<View> {
    
        private static final Interpolator INTERPOLATOR = new FastOutSlowInInterpolator();
    
        private float viewY;//控件距离coordinatorLayout底部距离
        private boolean isAnimate;//动画是否在进行
    
        public MyFabBehavior(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        @Override
        public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, View child, View directTargetChild, View target, int nestedScrollAxes) {
    
            if (child.getVisibility() == View.VISIBLE && viewY == 0) {
                //获取控件距离父布局(coordinatorLayout)底部距离
                viewY = coordinatorLayout.getHeight() - child.getY();
            }
    
            return (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0;//判断是否竖直滚动
        }
    
        @Override
        public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, View child, View target, int dx, int dy, int[] consumed) {
            //大于0是向上滚动 小于0是向下滚动
    
            if (dy >= 0 && !isAnimate && child.getVisibility() == View.VISIBLE) {
                hide(child);
            } else if (dy < 0 && !isAnimate && child.getVisibility() == View.GONE) {
                show(child);
            }
        }
    
        //隐藏时的动画
        private void hide(final View view) {
            ViewPropertyAnimator animator = view.animate().translationY(viewY).setInterpolator(INTERPOLATOR).setDuration(200);
    
            animator.setListener(new Animator.AnimatorListener() {
                @Override
                public void onAnimationStart(Animator animator) {
                    isAnimate = true;
                }
    
                @Override
                public void onAnimationEnd(Animator animator) {
                    view.setVisibility(View.GONE);
                    isAnimate = false;
                }
    
                @Override
                public void onAnimationCancel(Animator animator) {
                    show(view);
                }
    
                @Override
                public void onAnimationRepeat(Animator animator) {
    
                }
            });
            animator.start();
        }
    
        //显示时的动画
        private void show(final View view) {
            ViewPropertyAnimator animator = view.animate().translationY(0).setInterpolator(INTERPOLATOR).setDuration(200);
            animator.setListener(new Animator.AnimatorListener() {
                @Override
                public void onAnimationStart(Animator animator) {
                    view.setVisibility(View.VISIBLE);
                    isAnimate = true;
                }
    
                @Override
                public void onAnimationEnd(Animator animator) {
                    isAnimate = false;
                }
    
                @Override
                public void onAnimationCancel(Animator animator) {
                    hide(view);
                }
    
                @Override
                public void onAnimationRepeat(Animator animator) {
    
                }
            });
            animator.start();
        }
    }
    

    逻辑并不复杂,我们通过重写Behavior中关于嵌套滑动的两个回调完成了FloatingActionButton的隐藏和显示判断及操作。

    单独出场的底栏也可以利用上面一样的方法来设置隐藏或显示,我的底栏是和AppBarLayout一起出场,所以我就让底栏从属于AppBarLayout活动。代码如下:

    public class MyBottomBarBehavior extends CoordinatorLayout.Behavior<View> {
    
    
        public MyBottomBarBehavior(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        @Override
        public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
            //这个方法是说明这个子控件是依赖AppBarLayout的
            return dependency instanceof AppBarLayout;
        }
    
    
        @Override
        public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
    
            float translationY = Math.abs(dependency.getTop());//获取更随布局的顶部位置
            child.setTranslationY(translationY);
            return true;
        }
    
    }
    

    代码量比上个还少。我们还可以通过重写onMeasureChild()来使控件响应从属控件的大小变化。

    Behavior提供的很多,我这里用到的只是一部分,大家可以看看源码,根据具体需求去使用。


    3 .CollapsingToolbarLayout与TabLayout结合

    CollapsingToolbarLayout与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="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"
                app:titleEnabled="false"
                android:fitsSystemWindows="true"
                app:contentScrim="@color/colorPrimary"
                app:statusBarScrim="@android:color/transparent"
                app:layout_scrollFlags="scroll|exitUntilCollapsed|snap">
                <ImageView
                    android:id="@+id/imageview"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:scaleType="centerCrop"
                    android:adjustViewBounds="true"
                    app:layout_collapseMode="parallax"
                    app:layout_collapseParallaxMultiplier="0.7"
                    android:fitsSystemWindows="true"
                    android:src="@drawable/girl2"/>
                <View
                    android:layout_width="match_parent"
                    android:layout_height="40dp"
                    android:background="@drawable/gradient"
                    android:fitsSystemWindows="true" />
                <android.support.v7.widget.Toolbar
                    android:id="@+id/toolbar"
                    android:layout_width="match_parent"
                    android:layout_height="96dp"
                    android:minHeight="?attr/actionBarSize"
                    android:gravity="top"
                    app:layout_collapseMode="pin"
                    app:title="hello"
                    app:popupTheme="@style/AppTheme.PopupOverlay"
                    app:titleMarginTop="15dp"
                    />
                <android.support.design.widget.TabLayout
                    android:id="@+id/tablayout"
                    android:layout_width="match_parent"
                    android:layout_height="45dp"
                    android:layout_gravity="bottom" />
            </android.support.design.widget.CollapsingToolbarLayout>
        </android.support.design.widget.AppBarLayout>
    
    
        <android.support.v4.view.ViewPager
            android:id="@+id/viewpage"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:layout_behavior="@string/appbar_scrolling_view_behavior">
        </android.support.v4.view.ViewPager>
    
    </android.support.design.widget.CoordinatorLayout>
    

    2. AppBarLayout
    1. SwipeRefreshLayout配置的"@string/appbar_scrolling_view_behavior"是系统提供的,用来使滑动控件与AppBarLayout互动。

    参考资料

    一个神奇的控件——Android CoordinatorLayout与Behavior使用指南

    相关文章

      网友评论

      • 王秋荣:楼主 你好,就你最下面这个效果图,如何实现在滑动过程中下面Fragment中的文字总是垂直居中,也就是说保证下面布局高度随滑动改变。

      本文标题:一个神奇的控件——Android CoordinatorLayo

      本文链接:https://www.haomeiwen.com/subject/utqyjttx.html