CoordinatorLayout使用(三):NestedScrollView & 嵌套滑动事件

上一篇,我们大体理解了 Behavior流程 和 事件流
具体代码可以见 https://github.com/2954722256/use_little_demo
对应 coordinator 的 Module


简单复习

前面大体大体了解了
Behavior 和 CoordinatorLayout 直接的关系和使用
自定义Behavior的通用流程
了解 绑定的方式, 事件流
上一篇唯一的例子,是事件流中 CoordinatorLayout关联事件

而其中, 事件流中,嵌套滑动事件 中
我们 只是了解了 NestedScrollingChildNestedScrollingParent 的理论关系

注意: 嵌套滑动事件 不需要指定DependOn, 所有NestedScrollingChild的滑动, Parent默认都可以获得

通过源码,可以提前了解知道

  • CoordinatorLayout 其实是一个 NestedScrollingParent

CoordinatorLayout
  • 传递滑动事件的,其实相当于 NestedScrollingChild
  • 最后真正消费事件的,其实是 Behavior的子类(自定义的,系统的)

NestedScrollView简单了解

有一个类,叫 NestedScrollView:

NestedScrollView

直接从源码看,可以知道, 它既是一个 NestedScrollingChild 也是一个 NestedScrollingParent
换句话说, 即可以 接收事件, 也可以 处理并且发送给Behavior子类绑定的View

参考下官网:
https://developer.android.com/reference/android/support/v4/widget/NestedScrollView.html
可以发现,其实就是一个ScrollView
并且可以在老版本,新版本的android下面使用。
默认是开启的。


嵌套滑动事件 简单实例

注意: 这里是用 事件流中 嵌套滑动事件 去处理的

我们可以用NestedScrollView做事件发送,给外面的Parent发事件, 再传递给Behavior子类绑定的View
简单的思路:

  • NestedScrollView直接会发送事件
  • CoordinatorLayout也就是外面parent的会自动接收
  • 我们只需要写一个Behavior子类来消费即可

我们先看一下 嵌套滑动事件 方式的 Behavior

自定义简单的Behavior DodoBehavior1scroll

package com.aohuan.dodo.coordinator.utils;

import android.content.Context;
import android.support.design.widget.CoordinatorLayout;
import android.support.v4.view.ViewCompat;
import android.support.v4.widget.NestedScrollView;
import android.util.AttributeSet;
import android.view.View;

/**
 * Created by dodo  2390183798 on 2016/10/31.
 * 参考:  http://blog.csdn.net/qibin0506/article/details/50290421
 *
 * 对应的一起滑动的
 *      原理也简单, 是上下滑动, 就设置对应的y值为 Main View的y值
 *
 */
public class DodoBehavior1scroll extends CoordinatorLayout.Behavior<View> {

    public DodoBehavior1scroll(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) != 0;
        return nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL;
//        return false;
    }

//    @Override
//    public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, View child, View target, int dx, int dy, int[] consumed) {
//        super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed);
//        int followScrolled = target.getScrollY();
//        child.setScrollY(followScrolled);
//    }

    @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);
        int followScrolled = target.getScrollY();
        child.setScrollY(followScrolled);
    }

    @Override
    public boolean onNestedFling(CoordinatorLayout coordinatorLayout, View child, View target, float velocityX, float velocityY, boolean consumed) {
        if(child instanceof NestedScrollView){
            ((NestedScrollView) child).fling((int)velocityY);
        }
        return true;
    }
}

很好理解, 也就是3个方法
(具体参数说明,看上一篇Behavior子类获得事件,对应View变化,这里不单独介绍了)

  • boolean onStartNestedScroll
    • 判断是否接收后续事件
    • 我们的例子由于是竖直方向的滑动监听(直接true包含横向也行,后面不会获取对应的值)
  • void onNestedScroll
    • 对应滑动的时候,处理的事情
    • 当然,这里换成void onNestedPreScroll 效果是差不多的, 具体只是2个方法有先后顺序而已
  • boolean onNestedFling
    • 对应的滑动较快,也就是fling事件触发的时候调用
    • 这里不能换成 onNestedPreFling,替换后,会有卡顿,暂时不纠结为什么

这里layout,也很简单
就CoordinatorLayout中,包含 2个 NestedScrollView , 一个Behavior

activity_main4.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.design.widget.CoordinatorLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <!--app:layout_behavior="com.aohuan.dodo.coordinator.utils.DodoBehavior1scroll"-->
        <android.support.v4.widget.NestedScrollView
            android:id="@+id/ns0"
            android:layout_width="150dp"
            android:layout_height="match_parent"
            android:layout_gravity="top|right"
            android:background="#666666">

            <!--<com.aohuan.dodo.coordinator.view.NoScrollListView-->
                <!--android:id="@+id/list0"-->
                <!--android:layout_width="150dp"-->
                <!--android:layout_height="match_parent"-->
                <!--android:layout_gravity="top|right" />-->
            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                style="@style/TextAppearance.AppCompat.Display3"
                android:text="A\nB\nC\nD\nE\nF\nG\nH\nI\nJ\nK\nL\nM\nN\nO\nP\nQ\nR\nS\nT\nU\nV\nW\nX\nY\nZ"/>
        </android.support.v4.widget.NestedScrollView>


        <android.support.v4.widget.NestedScrollView
            android:id="@+id/ns1"
            android:layout_width="150dp"
            android:layout_height="match_parent"
            android:layout_gravity="top|left"
            android:background="#888888"
            app:layout_behavior="com.aohuan.dodo.coordinator.utils.DodoBehavior1scroll">

            <!--<com.aohuan.dodo.coordinator.view.NoScrollListView-->
                <!--android:id="@+id/list1"-->
                <!--android:layout_width="150dp"-->
                <!--android:layout_height="match_parent"-->
                <!--android:layout_gravity="top|left" />-->
            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                style="@style/TextAppearance.AppCompat.Display3"
                android:text="A\nB\nC\nD\nE\nF\nG\nH\nI\nJ\nK\nL\nM\nN\nO\nP\nQ\nR\nS\nT\nU\nV\nW\nX\nY\nZ"/>

        </android.support.v4.widget.NestedScrollView>
    </android.support.design.widget.CoordinatorLayout>
</RelativeLayout>

如果是TextView,Activity就不用填充数据了,这里就不贴对应的代码了

看一下效果


效果

我们可以得到,

  • 右边 滑动,左边随着滑动
  • 左边单独滑动,右边不动

和前面提到的逻辑是一样的, 因为Parent会传递给左边


再添加一个NestedScrollView

我们知道 NestedScrollView 可以发送事件给外面的Parent,
也就是CoordinatorLayout
那如果我们再添加一个 NestedScrollView
那应该都可以发送滑动事件

<?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:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.v4.widget.NestedScrollView
        android:id="@+id/ns0"
        android:layout_width="100dp"
        android:layout_height="match_parent"
        android:layout_gravity="top|right"
        android:background="#666666">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            style="@style/TextAppearance.AppCompat.Display3"
           android:text="A\nB\nC\nD\nE\nF\nG\nH\nI\nJ\nK\nL\nM\nN\nO\nP\nQ\nR\nS\nT\nU\nV\nW\nX\nY\nZ\nB\nC\nD\nE\nF\nG\nH\nI\nJ\nK\nL\nM\nN\nO\nP\nQ\nR\nS\nT\nU\nV\nW\nX\nY\nZ"/>
    </android.support.v4.widget.NestedScrollView>

    <android.support.v4.widget.NestedScrollView
        android:id="@+id/ns1"
        android:layout_width="100dp"
        android:layout_height="match_parent"
        android:layout_gravity="top|center_horizontal"
        android:background="#009900">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            style="@style/TextAppearance.AppCompat.Display3"
            android:text="A\nB\nC\nD\nE\nF\nG\nH\nI\nJ\nK\nL\nM\nN\nO\nP\nQ\nR\nS\nT\nU\nV\nW\nX\nY\nZ\nB\nC\nD\nE\nF\nG\nH\nI\nJ\nK\nL\nM\nN\nO\nP\nQ\nR\nS\nT\nU\nV\nW\nX\nY\nZ"/>

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

    <android.support.v4.widget.NestedScrollView
        android:id="@+id/ns2"
        android:layout_width="100dp"
        android:layout_height="match_parent"
        android:layout_gravity="top|left"
        android:background="#888888"
        app:layout_behavior="com.aohuan.dodo.coordinator.utils.DodoBehavior1scroll">
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            style="@style/TextAppearance.AppCompat.Display3"
android:text="A\nB\nC\nD\nE\nF\nG\nH\nI\nJ\nK\nL\nM\nN\nO\nP\nQ\nR\nS\nT\nU\nV\nW\nX\nY\nZ\nB\nC\nD\nE\nF\nG\nH\nI\nJ\nK\nL\nM\nN\nO\nP\nQ\nR\nS\nT\nU\nV\nW\nX\nY\nZ"/>

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

其他一样,只是给最左边的添加了Behavior

我们来看看效果

效果

我们如果给左边2个都添加上对应的Behavior
也就是里面任意一个滑动,左边2个都会跟着移动
这里就不贴代码了
我们来看看效果

效果

简单的变动

我们经常可以看见一些滑动后,慢慢出现一个Button按钮
应该是用的系统的,或者自己写的
按这个思路,简单写一个demo
大体也就是滑动

  • 到一定距离以后,显示按钮
  • 再一定距离以后,隐藏按钮

大体layout

<?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:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.v4.widget.NestedScrollView
        android:id="@+id/ns0"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="top|right"
        android:background="#666666">
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            style="@style/TextAppearance.AppCompat.Display3"
            android:text="A\nB\nC\nD\nE\nF\nG\nH\nI\nJ\nK\nL\nM\nN\nO\nP\nQ\nR\nS\nT\nU\nV\nW\nX\nY\nZ\nB\nC\nD\nE\nF\nG\nH\nI\nJ\nK\nL\nM\nN\nO\nP\nQ\nR\nS\nT\nU\nV\nW\nX\nY\nZ"/>

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

    <Button
        android:id="@+id/btn"
        android:visibility="gone"
        android:layout_width="wrap_content"
        android:layout_height="100dp"
        android:background="#888888"
        android:layout_gravity="bottom|center_horizontal"
        android:text="  Dodo Follow  "
        app:layout_behavior="com.aohuan.dodo.coordinator.utils.DodoMoveBigerBehavior"
        />
</android.support.design.widget.CoordinatorLayout>

也就只有一个 发送滑动事件的NestedScrolling
外面一样是 NestedScrolling 的 Parent
再有一个绑定Behavior的按钮, 接收和消费 滑动事件

对应的Behavior

package com.aohuan.dodo.coordinator.utils;

import android.animation.ObjectAnimator;
import android.content.Context;
import android.support.design.widget.CoordinatorLayout;
import android.support.v4.view.ViewCompat;
import android.support.v4.widget.NestedScrollView;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.animation.Animation;

import com.aohuan.dodo.coordinator.view.DodoMoveView;

/**
 * Created by dodo on 2016/11/1.
 * qq: 2390183798
 *
 *
 * 根据MainView竖直方向的滑动, 设置绑定View的宽度
 *      原理也简单, 只要是竖直滑动, 动态设置宽, 添加是否可见,以及简单动画,即可
 */
public class DodoMoveBigerBehavior extends CoordinatorLayout.Behavior<View> {

    public DodoMoveBigerBehavior(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 onNestedScroll(CoordinatorLayout coordinatorLayout, View child, View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
        super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed);
        int followScrolled = target.getScrollY();
            setBiger(child, followScrolled);
    }

    @Override
    public boolean onNestedFling(CoordinatorLayout coordinatorLayout, View child, View target, float velocityX, float velocityY, boolean consumed) {
        int followScrolled = target.getScrollY();
        setBiger(child, followScrolled);
        return true;
    }

    @Override
    public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, View child, View target) {
        super.onStopNestedScroll(coordinatorLayout, child, target);
        int followScrolled = target.getScrollY();
        setBiger(child, followScrolled);
    }

    @Override
    public boolean onNestedPreFling(CoordinatorLayout coordinatorLayout, View child, View target, float velocityX, float velocityY) {
        int followScrolled = target.getScrollY();
        setBiger(child, followScrolled);
        return super.onNestedPreFling(coordinatorLayout, child, target, velocityX, velocityY);
    }

    final int MinWidth = 300;
    final int MaxWidth = 450;

    private void setBiger(View v, int y) {
        CoordinatorLayout.MarginLayoutParams layoutParams = (CoordinatorLayout.MarginLayoutParams) v.getLayoutParams();
        layoutParams.width = 200 + y/10;

        if(layoutParams.width >= MinWidth && layoutParams.width <= MaxWidth){
            v.setVisibility(View.VISIBLE);
        }else{
            v.setVisibility(View.GONE);
        }

//        doJudge(v, layoutParams.width);

        v.setLayoutParams(layoutParams);
    }

    private int oldY = 0;
    private boolean isMinBiggerAni = false;
    private boolean isMaxBiggerAni = false;

    private void doJudge(View v, int y){
        Log.e("ani", "y : " +y + "  ==  oldY : " + oldY);
        if(oldY < y){
            if(y >= MinWidth && !isMinBiggerAni){
                isMinBiggerAni = !isMinBiggerAni;
                v.setVisibility(View.VISIBLE);
                doAnimat(v, true);
                Log.e("ani", "now");
            }
            if(y >= MaxWidth && !isMaxBiggerAni){
                isMaxBiggerAni = !isMaxBiggerAni;
                doAnimat(v, false);
                Log.e("ani", "now");
            }
        }

        if(oldY > y){
            if(y < MinWidth && isMinBiggerAni){
                isMinBiggerAni = !isMinBiggerAni;
                doAnimat(v, false);
                Log.e("ani", "now");
            }
            if(y < MaxWidth && isMaxBiggerAni){
                isMaxBiggerAni = !isMaxBiggerAni;
                doAnimat(v, true);
                v.setVisibility(View.VISIBLE);
                Log.e("ani", "now");
            }
        }
        oldY = y;
    }

    private void doAnimat(View v, boolean isBigger){
        ObjectAnimator fViewScaleXAnim = ObjectAnimator.ofFloat(v,"scaleX",isBigger?0f:1f, isBigger?1f:0f);
        fViewScaleXAnim.setDuration(500);
        fViewScaleXAnim.start();
    }
}

和前面的demo类似, 只是简单的修改

我们来看一下效果
这里变大,是为了让我们感觉对应的滑动变大的关联

直接感觉

再简单添加一个动画
看看效果

添加简单动画

这里只是为了理解 嵌套滑动事件
应该会有一些bug
自己就不继续了


简单回顾

这里几个demo,只是上一节理论的实例
NestedScrollView简单理解了 NestedScrolling的嵌套滑动事件
由于在文章链接里面
卌梓的文章
找到一张图,感觉说得很清楚,自己就不画图了,贴别人的
(对应的触摸事件 换成 嵌套滑动事件 即可 )

其他的内容,后续一起学习
具体代码,可以见
https://github.com/2954722256/use_little_demo
对应 coordinator 的 Module


下一篇我们可以了解
和Toolbar的简单使用

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

推荐阅读更多精彩内容