Android嵌套滚动NestedScroll的实现了解一下

 更新时间:2018年06月05日 14:29:42   作者:林嘉伟  
嵌套滚动已经算一个比较常见的特效了,这篇文章主要介绍了Android嵌套滚动NestedScroll的实现了解一下,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

其实嵌套滚动已经算一个比较常见的特效了,下面这个动图就是嵌套滚动的一个例子:

看到这个动效,大家可能都知道可以用CoordinatorLayout去实现.其实CoordinatorLayout是基于NestedScroll机制去实现的,而我们直接通过NestedScroll机制也能很方便的实现这个动效.

原理

NestedScroll的其实很简单.

一般的触摸消息的分发都是从外向内的,由外层的ViewGroup的dispatchTouchEvent方法调用到内层的View的dispatchTouchEvent方法.

而NestedScroll提供了一个反向的机制,内层的view在接收到ACTION_MOVE的时候,将滚动消息先传回给外层的ViewGroup,看外层的ViewGroup是不是需要消耗一部分的移动,然后内层的View再去消耗剩下的移动.内层view可以消耗剩下的滚动的一部分,如果还没有消耗完,外层的view可以再选择把最后剩下的滚动消耗掉.

上面的描述可能有点绕,可以看下面的图来帮助理解:

 

具体实现

NestedScroll机制会涉及到四个类:

NestedScrollingChild, NestedScrollingChildHelper 和 NestedScrollingParent , NestedScrollingParentHelper

NestedScrollingChild和NestedScrollingParent是两个接口,我们先看看他们的声明:

public interface NestedScrollingChild {
  public void setNestedScrollingEnabled(boolean enabled);

  public boolean isNestedScrollingEnabled();

  public boolean startNestedScroll(int axes);

  public void stopNestedScroll();

  public boolean hasNestedScrollingParent();

  public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed,
      int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow);

  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);
}

public interface NestedScrollingParent {
  public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes);

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

  public void onStopNestedScroll(View target);

  public void onNestedScroll(View target, int dxConsumed, int dyConsumed,
      int dxUnconsumed, int dyUnconsumed);

  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();
}

这里真正重要的其实是NestedScrollingParent的几个方法,因为其他方法都能直接让NestedScrollingChildHelper或者NestedScrollingParentHelper去代理:

  1. onStartNestedScroll 是否接受嵌套滚动,只有它返回true,后面的其他方法才会被调用
  2. onNestedPreScroll 在内层view处理滚动事件前先被调用,可以让外层view先消耗部分滚动
  3. onNestedScroll 在内层view将剩下的滚动消耗完之后调用,可以在这里处理最后剩下的滚动
  4. onNestedPreFling 在内层view的Fling事件处理之前被调用
  5. onNestedFling 在内层view的Fling事件处理完之后调用

我们只要让子view和父view分别实现NestedScrollingChild和NestedScrollingParent接口,然后分别调用NestedScrollingChildHelper和NestedScrollingParentHelper的对应方法去代理一些具体功能,然后在NestedScrollingChild的onTouchEvent那里根据需求调用startNestedScroll/dispatchNestedPreScroll/stopNestedScroll就能实现嵌套滚动了:

//NestedScrollingChild
private NestedScrollingChildHelper mHelper = new NestedScrollingChildHelper(this);

public boolean startNestedScroll(int axes) {
 return mHelper.startNestedScroll(axes);
}
public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed,
      int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow) {
 return mHelper.dispatchNestedScroll(dxConsumed, dyConsumed,
       dxUnconsumed, dyUnconsumed, offsetInWindow);
}
...
//NestedScrollingParent
private NestedScrollingParentHelper mHelper = new NestedScrollingParentHelper(this);

public void onNestedScrollAccepted(View child, View target, int axes) {
 mHelper.onNestedScrollAccepted(child, target, axes);
}

public int getNestedScrollAxes() {
 return mHelper.getNestedScrollAxes();
}
...

但是如果你使用sdk21及以上的版本,NestedScroll机制已经直接集成到了View中了,你只需要直接重写View的对应方法就好

布局

我们先看布局文件

<me.linjw.nestedscrolldemo.NestedScrollParentView xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:orientation="vertical">

  <FrameLayout
    android:id="@+id/header"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <ImageView
      android:layout_width="match_parent"
      android:layout_height="200dp"
      android:src="@mipmap/ic_launcher" />
  </FrameLayout>

  <TextView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@color/colorAccent"
    android:text="Title"
    android:textAlignment="center"
    android:textSize="20dp" />

  <android.support.v7.widget.RecyclerView
    android:id="@+id/list"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />
</me.linjw.nestedscrolldemo.NestedScrollParentView>

最外层是我们自定义的NestedScrollParentView,其实它是一个LinearLayout,内部竖直排列了三个子view:

  1. 一个由FrameLayout包裹的ImageView
  2. 一个TextView
  3. 一个RecyclerView

代码

为了简便起见,我们先直接用sdk22的版本用重写View方法的方式去实现它.

NestedScrollParentView中有两个方法比较重要,嵌套滚动基本上就是由这两个方法实现的:

@Override
 public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
   return true;
 }

 @Override
 public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {
   super.onNestedPreScroll(target, dx, dy, consumed);

   boolean headerScrollUp = dy > 0 && getScrollY() < mHeaderHeight;
   boolean headerScrollDown = dy < 0 && getScrollY() > 0 && !target.canScrollVertically(-1);
   if (headerScrollUp || headerScrollDown) {
     scrollBy(0, dy);
     consumed[1] = dy;
   }
 }

onStartNestedScroll 这个方法如果返回true的话代表接受由内层传来的滚动消息,我们直接返回true就好,否则后面的消息都接受不到

onNestedPreScroll 这个方法用于消耗内层view的一部分滚动.我们需要将消耗掉的滚动存到counsumed中让consumed知道.例如我们这里在顶部的FrameLayout需要移动的情况下会消耗掉所有的dy,这样内层的view(即RecyclerView)就不会滚动了.

这里的mHeaderHeight保存的是顶部的FrameLayout的高度:

@Override
  protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);
    mHeaderHeight = mHeader.getMeasuredHeight();
  }

到这里基本上就实现了动图的效果,是不是很简单?

完整代码可以参考 https://github.com/bluesky466/NestedScrollDemo/tree/sdk22

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

相关文章

  • Flutter StreamBuilder组件实现局部刷新示例讲解

    Flutter StreamBuilder组件实现局部刷新示例讲解

    日常使用最多的局部刷新为Provider状态管理 Selector,今天分享flutter框架自带的StreamBuilder组件,该组件可做到局部刷新,使用简单且轻便
    2022-11-11
  • Android 6.0权限请求相关及权限分组方法

    Android 6.0权限请求相关及权限分组方法

    今天小编就为大家分享一篇Android 6.0权限请求相关及权限分组方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-08-08
  • Android中使用socket使底层和framework通信的实现方法

    Android中使用socket使底层和framework通信的实现方法

    native和framework的通信是通过jni,但是这一般只是framework调用native,native如果有消息要怎样通知上层 呢?android中GSP模块提供一种解决思路,但是实现有些复杂,这里介绍一种使用socket通信的方法可以使native和framework自由通信,感兴趣的朋友一起看看吧
    2016-11-11
  • Android实现登录注册页面(上)

    Android实现登录注册页面(上)

    这篇文章主要为大家详细介绍了Android实现登录注册页面,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-04-04
  • Android学习笔记之蓝牙功能

    Android学习笔记之蓝牙功能

    这篇文章主要为大家详细介绍了Android学习笔记之蓝牙功能,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-09-09
  • 使用Flutter开发一个图片UI组件的代码示例

    使用Flutter开发一个图片UI组件的代码示例

    在移动应用开发中,图片展示是一个常见的需求,为了满足不同场景的图片展示需求,我们可以开发一个灵活配置的图片UI组件,本文将介绍如何使用Flutter开发一个图片UI组件,并提供了丰富的配置选项,需要的朋友可以参考下
    2023-09-09
  • android全屏去掉title栏的多种实现方法

    android全屏去掉title栏的多种实现方法

    android全屏去掉title栏包括以下几个部分:实现应用中的所有activity都全屏/实现单个activity全屏/实现单个activity去掉title栏/自定义标题内容/自定义标题布局等等感兴趣的可参考下啊
    2013-02-02
  • 关于Android SDCard存储的问题

    关于Android SDCard存储的问题

    本篇文章小编为大家介绍,关于Android SDCard存储的问题。需要的朋友参考下
    2013-04-04
  • 关注Ionic底部导航按钮tabs在android情况下浮在上面的处理

    关注Ionic底部导航按钮tabs在android情况下浮在上面的处理

    Ionic是一款流行的移动端开发框架,但是刚入门的同学会发现,Ionic在iOS和Android的底部tabs显示不一样。在安卓情况下底部tabs会浮上去,下面给大家介绍下实现代码,一起看看吧
    2016-12-12
  • Android开发实现的文本折叠点击展开功能示例

    Android开发实现的文本折叠点击展开功能示例

    这篇文章主要介绍了Android开发实现的文本折叠点击展开功能,涉及Android界面布局与属性控制相关操作技巧,需要的朋友可以参考下
    2019-03-03

最新评论