Android中的NestedScrollingParent和NestedScrollingChild分析

在分析SwipeRefreshLayout源码的时候发现该类实现了NestedScrollingParent和NestedScrollingChild两个接口,甚是好奇,于是结合了网上的资料,然后根据我个人的理解写下本章.

这个两个接口是为了更好解决事件冲突的.

在这里 nested scrolling 就翻译为嵌套滚动吧.

但是这和以前用过的dispatchTouchEvent,onInterceptTouchEvent,onTouchEvent和requestDisallowInterceptTouchEvent有什么区别呢?其实我感觉NestedScrollingParent和NestedScrollingChild是辅助解决事件冲突出现的.在之前的事件拦截中,就算子View的ACTION_MOVE的事件返回false,父布局也是获取不了该事件的.

NestedScrollingChild接口的定义如下:

public interface NestedScrollingChild {
    // 参数enabled:true表示view使用嵌套滚动,false表示禁用.
    public void setNestedScrollingEnabled(boolean enabled);

    public boolean isNestedScrollingEnabled();

    // 参数axes:表示滚动的方向如:ViewCompat.SCROLL_AXIS_VERTICAL(垂直方向滚动)和
    // ViewCompat.SCROLL_AXIS_HORIZONTAL(水平方向滚动)
    // 返回值:true表示本次滚动支持嵌套滚动,false不支持
    public boolean startNestedScroll(int axes);

    public void stopNestedScroll();

    public boolean hasNestedScrollingParent();

    // 参数dxConsumed: 表示view消费了x方向的距离长度
    // 参数dyConsumed: 表示view消费了y方向的距离长度
    // 参数dxUnconsumed: 表示滚动产生的x滚动距离还剩下多少没有消费
    // 参数dyUnconsumed: 表示滚动产生的y滚动距离还剩下多少没有消费
    // 参数offsetInWindow: 表示剩下的距离dxUnconsumed和dyUnconsumed使得view在父布局中的位置偏移了多少
    public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed,
            int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow);

    // 参数dx: 表示view本次x方向的滚动的总距离长度
    // 参数dy: 表示view本次y方向的滚动的总距离长度
    // 参数consumed: 表示父布局消费的距离,consumed[0]表示x方向,consumed[1]表示y方向
    // 参数offsetInWindow: 表示剩下的距离dxUnconsumed和dyUnconsumed使得view在父布局中的位置偏移了多少
    public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow);

    // 这个是滑动的就不详细分析了
    public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed);

    public boolean dispatchNestedPreFling(float velocityX, float velocityY);
}

1,setNestedScrollingEnabled 实现该结构的View要调用setNestedScrollingEnabled(true)才可以使用嵌套滚动.

2,isNestedScrollingEnabled判断当前view能否使用嵌套滚动.

3,startNestedScroll和stopNestedScroll.是配对使用的.startNestedScroll表示view开始滚动了,一般是在ACTION_DOWN中调用,如果返回true则表示父布局支持嵌套滚动.在事件结束比如ACTION_UP或者ACTION_CANCLE中调用stopNestedScroll,告诉父布局滚动结束.

4,dispatchNestedScroll,把view消费滚动距离之后,把剩下的滑动距离再次传给父布局.

5,dispatchNestedPreScroll,在view消费滚动距离之前把总得滑动距离传给父布局.

6,dispatchNestedFling和dispatchNestedPreFling就是view传递滑动的信息给父布局的.

NestedScrollingParent接口的定义如下:

public interface NestedScrollingParent {

      // 参数child:ViewParent包含触发嵌套滚动的view的对象
      // 参数target:触发嵌套滚动的view  (在这里如果不涉及多层嵌套的话,child和target)是相同的
      // 参数nestedScrollAxes:就是嵌套滚动的滚动方向了.
    public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes);

    public void onNestedScrollAccepted(View child, View target, int nestedScrollAxes);

    public void onStopNestedScroll(View target);

    // 参数target:同上
    // 参数dxConsumed:表示target已经消费的x方向的距离
    // 参数dyConsumed:表示target已经消费的x方向的距离
    // 参数dxUnconsumed:表示x方向剩下的滑动距离
    // 参数dyUnconsumed:表示y方向剩下的滑动距离
    public void onNestedScroll(View target, int dxConsumed, int dyConsumed,
            int dxUnconsumed, int dyUnconsumed);

    // 参数dx:表示target本次滚动产生的x方向的滚动总距离
    // 参数dy:表示target本次滚动产生的y方向的滚动总距离
    // 参数consumed:表示父布局要消费的滚动距离,consumed[0]和consumed[1]分别表示父布局在x和y方向上消费的距离.
    public void onNestedPreScroll(View target, int dx, int dy, int[] consumed);

    public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed);

    public boolean onNestedPreFling(View target, float velocityX, float velocityY);

    public int getNestedScrollAxes();
}

1,onStartNestedScroll.当子view的调用NestedScrollingChild的方法startNestedScroll时,会调用该方法.

2,onNestedScrollAccepted.如果onStartNestedScroll方法返回的是true的话,那么紧接着就会调用该方法.它是让嵌套滚动在开始滚动之前,让布局容器(viewGroup)或者它的父类执行一些配置的初始化的.下面是原文:

(It offers an opportunity for the view and its superclasses to perform initial configuration for the nested scroll.)

3,onStopNestedScroll停止滚动了,当子view调用stopNestedScroll时会调用该方法.

4,onNestedScroll,当子view调用dispatchNestedScroll方法时,会调用该方法.

5,onNestedPreScroll,当子view调用dispatchNestedPreScroll方法是,会调用该方法.

6,dispatchNestedFling和dispatchNestedPreFling对应的就是滑动了.

下面给出基本的例子,首先是父布局的代码:

package com.yluo.testnestscrolling;

import android.annotation.SuppressLint;
import android.content.Context;
import android.support.v4.view.NestedScrollingParent;
import android.support.v4.view.NestedScrollingParentHelper;
import android.support.v4.view.ViewCompat;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.widget.FrameLayout;

@SuppressLint("NewApi") public class NestScrollingLayout extends FrameLayout implements NestedScrollingParent{

    private static final String TAG = "NestScrollingLayout";

    private  NestedScrollingParentHelper mParentHelper;

    public NestScrollingLayout(Context context, AttributeSet attrs,
            int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

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

    public NestScrollingLayout(Context context) {
        super(context);
        init();

    }

    @SuppressLint("NewApi") private void init() {
         mParentHelper = new NestedScrollingParentHelper(this);
    }
    @Override
    public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {

        Log.d(TAG, "child==target:" + (child == target));

        Log.d(TAG, "----父布局onStartNestedScroll----------------");

         return (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0;
    }
    @SuppressLint("NewApi") @Override
    public void onNestedScrollAccepted(View child, View target, int nestedScrollAxes) {

        Log.d(TAG, "----父布局onNestedScrollAccepted---------------");

        mParentHelper.onNestedScrollAccepted(child, target, nestedScrollAxes);
    }

    @Override
    public void onStopNestedScroll(View target) {
        Log.d(TAG, "----父布局onStopNestedScroll----------------");
        mParentHelper.onStopNestedScroll(target);
    }

    @Override
    public void onNestedScroll(View target, int dxConsumed, int dyConsumed,
            int dxUnconsumed, int dyUnconsumed) {
        Log.d(TAG, "----父布局onNestedScroll----------------");
    }

    @Override
    public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {
        scrollBy(0, -dy);

        consumed[0] = 0;
        consumed[1] = 10; // 把消费的距离放进去
        Log.d(TAG, "----父布局onNestedPreScroll----------------");
    }

    @Override
    public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) {
        Log.d(TAG, "----父布局onNestedFling----------------");
        return true;
    }
    @Override
    public boolean onNestedPreFling(View target, float velocityX, float velocityY)  {
        Log.d(TAG, "----父布局onNestedPreFling----------------");
        return true;
    }
    @Override
    public int getNestedScrollAxes() {
        Log.d(TAG, "----父布局getNestedScrollAxes----------------");
         return mParentHelper.getNestedScrollAxes();
    }
}

接着是子View的代码:

package com.yluo.testnestscrolling;

import android.content.Context;
import android.support.v4.view.NestedScrollingChild;
import android.support.v4.view.NestedScrollingChildHelper;
import android.support.v4.view.ViewCompat;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

public class NestScrollingView extends View implements NestedScrollingChild{

    private static final String TAG = "NestScrollingView";

    private NestedScrollingChildHelper mChildHelper;

    private int[] mConsumed = new int[2];

    private int[] mOffset = new int[2];

    public NestScrollingView(Context context, AttributeSet attrs,
            int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

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

    public NestScrollingView(Context context) {
        super(context);
        init();
    }

    private void init() {
        mChildHelper = new NestedScrollingChildHelper(this);
        setNestedScrollingEnabled(true);
    }

    @Override
    public void setNestedScrollingEnabled(boolean enabled) {
        mChildHelper.setNestedScrollingEnabled(enabled);
    }
    @Override
    public boolean isNestedScrollingEnabled() {
        return mChildHelper.isNestedScrollingEnabled();
    }
    @Override
    public boolean startNestedScroll(int axes) {
        Log.d(TAG, "-----------子View开始滚动---------------");
        return mChildHelper.startNestedScroll(axes);
    }
    @Override
    public void stopNestedScroll() {
        Log.d(TAG, "-----------子View停止滚动---------------");
        mChildHelper.stopNestedScroll();
    }
    @Override
    public boolean hasNestedScrollingParent() {
        return mChildHelper.hasNestedScrollingParent();
    }
    @Override
     public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed,
                int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow) {
        Log.d(TAG, "-----------子View把剩余的滚动距离传给父布局---------------");

        return mChildHelper.dispatchNestedScroll(dxConsumed,dyConsumed,
                dxUnconsumed,dyUnconsumed,offsetInWindow);
    }
    @Override
     public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {
        Log.d(TAG, "-----------子View把总的滚动距离传给父布局---------------");
        return mChildHelper.dispatchNestedPreScroll(dx,dy,
                consumed,offsetInWindow);
    }

    @Override
    public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed){
        return mChildHelper.dispatchNestedFling(velocityX,velocityY,
                consumed);
    }
    @Override
    public boolean dispatchNestedPreFling(float velocityX, float velocityY){
        return mChildHelper.dispatchNestedPreFling(velocityX,velocityY);
    }
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            // 按下事件调用startNestedScroll
            startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL);
            break;
        case MotionEvent.ACTION_MOVE:
            // 移动事件调用startNestedScroll
            dispatchNestedPreScroll(0,20,mConsumed,mOffset);
            // 输出一下偏移
            Log.d(TAG, "offset--x:" + mOffset[0] + ",offset--y:" + mOffset[1]);
            dispatchNestedScroll(50,50,50,50,mOffset);

            break;
        case MotionEvent.ACTION_UP:
        // 弹起事件调用startNestedScroll
            stopNestedScroll();
            break;
        default:
            break;
        }
        return true;
    }
}

布局文件如下:

<com.yluo.testnestscrolling.NestScrollingLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
 >

    <com.yluo.testnestscrolling.NestScrollingView
        android:layout_marginTop="100dp"
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:background="@android:color/holo_green_dark">

    </com.yluo.testnestscrolling.NestScrollingView>

</com.yluo.testnestscrolling.NestScrollingLayout>

输出结果如下:

--------------子View开始滚动------------------
----父布局onStartNestedScroll----------------
 ----父布局onNestedScrollAccepted---------------
-----------子View把总的滚动距离传给父布局--------
----父布局onNestedPreScroll----------------
---offset--x:0,offset--y:20
-----------子View把剩余的滚动距离传给父布局-------
 ----父布局onNestedScroll----------------
 -----------子View停止滚动---------------
 ----父布局onStopNestedScroll----------------

输入的顺序跟之前我们分析的是一样的.好了NestedScrollingParent和NestedScrollingChild的分析就到这里了.

至于这两个接口的具体应用例子现在网上有很多,包括SDK也有.后面我在写Android自定义控件的时候也会讲解这两个接口的用法的.

好了就先到这里啦.

这是本文的测试例子的下载地址:

http://download.csdn.net/detail/x87648510/9573102

时间: 2024-10-30 03:36:55

Android中的NestedScrollingParent和NestedScrollingChild分析的相关文章

Android中相机和相册使用分析

Android中相机和相册使用分析 欢迎转载,但请尊重原创(文章来自不易,转载请标明转载出处,谢谢) 在手机应用程序中,使用自带的相机拍照以及相册选择喜欢的图片是最常见不过的用户需求,那么怎么合理使用相机和相册来选择照片是重要的,下面就以项目中实际需求为例进行说明,这里实现的功能如下: 1 使用相机和相册选择图片,并裁剪较小图片(常用于剪裁小图) 2 使用相机和相册选择图片,并裁剪较大图片(常用于裁剪大图) 具体的实现功能清楚了,那么就一一进行说明,具体如下(这里不会罗列怎么上传图片到服务端,只

Android中的消息处理实例与分析

Android中的消息处理实例与分析 摘要 本文介绍了Android中的消息处理机制,给出了Android消息处理中的几个重点类Handler.Message.MessageQueue.Looper.Runnable.Thread的详细介绍,提供了两个消息处理的实例代码,并深入分析了使用Android消息机制应该遵循的几个原则. 阅读本文的收获 在具有java基础的情况下,Android的学习比较轻松,很多人在没有深刻了解Android消息处理机制的背景下,已经能够开发出可用的app,很多人开始

Android中app卡顿原因分析示例

在知乎回答了一个“为什么微博的app在iPhone比Android上流畅”的问题.后面部分是一个典型的动画卡顿的性能分析过程,因此帖在这里.有编程问题可以在这里交流.知乎链接. ========================================================= 我来说下我所知道的事情.我不知道iOS为什么流畅,但我知道一些Android为什么不流畅的原因. 首先,就题主所说的问题,我用iPad和小米Pad对比了一下微博滑动滚屏这件事情(2014年8月10日目前微博

Android中的软件安全和逆向分析[二]—apk反破解技术与安全保护机制

在Android应用开发中,当我们开发完软件之后,我们不希望别人能够反编译破解我们的应用程序,不能修改我们的代码逻辑.实际上,在应用程序的安全机制考虑中,我们希望自己的应用程序安全性高,通过各种加密操作等来增大竞争对手的反编译破解成本.设想,竞争对手开发一个同样的应用程序需要10天,而破解我们的软件程序需要100天,那么势必会打消黑客程序员破解我们应用程序的念头.如何增加对手的破解成本,就需要考验我们应用程序的安全性有多高,加密技术有多强.一个优秀的应用程序,不仅能为用户带来利益,同时也能保护自

android中倒计时控件CountDownTimer分析

android中倒计时控件CountDownTimer分析 1 示例代码 new CountDownTimer(10000, 1000) { public void onTick(long millisUntilFinished) { LogUtil.i(TAG, "seconds remaining: " + millisUntilFinished / 1000); } public void onFinish() { LogUtil.i(TAG, "done!"

Android中线程间通信原理分析:Looper,MessageQueue,Handler

自问自答的两个问题 在我们去讨论Handler,Looper,MessageQueue的关系之前,我们需要先问两个问题: 1.这一套东西搞出来是为了解决什么问题呢? 2.如果让我们来解决这个问题该怎么做? 以上者两个问题,是我最近总结出来的,在我们学习了解一个新的技术之前,最好是先能回答这两个问题,这样你才能对你正在学习的东西有更深刻的认识. 第一个问题:google的程序员们搞出这一套东西是为了解决什么问题的?这个问题很显而易见,为了解决线程间通信的问题.我们都知道,Android的UI/Vi

Android中RelativeLayout和LinearLayout性能分析

先看一些现象吧:用eclipse或者Android studio,新建一个Activity自动生成的布局文件都是RelativeLayout,或许你会认为这是IDE的默认设置问题,其实不然,这是由 android-sdk\tools\templates\activities\BlankActivity\root\res\layout\activity_simple.xml.ftl 这个文件事先就定好了的,也就是说这是Google的选择,而非IDE的选择.那SDK为什么会默认给开发者新建一个默认的

Coredump介绍及如何在Android中开启和使用来分析Crash等问题

文章目录: Coredump简介及使用... 1 目录... 2 一.什么是Coredump. 3 二.Coredump产生的原因... 3 三.如何控制产生Coredump. 4 四.使用Coredump的准备... 4 五.开始使用Coredump. 5 一.什么是Coredump 有些C/C++程序或者通过JNI调用了C/C++的APK程序可以通过编译, 但在运行时会出现错误,比如常见的signal 11 (SIGSEGV),这样的程序都是可以通过编译的,而且这样的错误一般情况下不会像编译

android中的context源码分析

https://duanqz.github.io/2017-12-25-Android-Contexthttps://www.jianshu.com/p/5ff04d5fe218设计模式:https://www.jianshu.com/p/008e16b9392d?utm_source=oschina-apphttps://www.cnblogs.com/yfceshi/p/7236179.html 原文地址:https://blog.51cto.com/haidragon/2412415