Android -- NestedScrolling滑动机制
1,如今NestedScrolling运用到很多地方了,要想好看一点的滑动变换,基本上就是使用这个来完成的,让我们来简单的了解一下。
2,NestedScrolling机制能够让父View和子View在滚动式进行配合,其基本流程如下:
- 当子view开始滚动之前,可以通知父View,让其先于自己进行滚动;
- 子View自己进行滚动;
- 子view滚动之后,还可以通知父view继续滚动。
//主要接口
NestedScrollingChild
NestedScrollingParent
//帮助类
NestedScrollingChildHelper
NestedScrollingParentHelper
//开始、停止嵌套滚动
public boolean startNestedScroll(int axes); public void stopNestedScroll();
//触摸滚动相关
public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow);
public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow);
//惯性滚动相关 public boolean dispatchNestedPreFling(float velocityX, float velocityY);
public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed);
//当开启、停止嵌套滚动时被调用
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 onNestedPreScroll(View target, int dx, int dy, int[] consumed);
public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed);
//当惯性嵌套滚动时被调用
public boolean onNestedPreFling(View target, float velocityX, float velocityY);
public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed);



- 调用child的startNestedScroll()来发起嵌套滑动流程(实质上是寻找能够配合child进行嵌套滚动的parent)。parent的onStartNestedScroll()会被调用,若此方法返回true,则OnNestScrollAccepted()也会被调用。
- chuld每次滚动前,可以先询问parent是否要滚动,即调用dispatchNestedScroll(),这时可以回调到parent的OnNestedPreScroll(),parent可以在这个回调中先于child滚动。
- dispatchNestedPreScroll()之后,child可以进行自己的滚动操作。
3,自定义NestedScrolling控件
先看一下效果
先看一下布局文件
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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.qianmo.mynestedscrolling.view.MyNestedScrollParent
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:orientation="vertical"> <ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:src="@mipmap/ic_launcher"/> <TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#f0f"
android:text="上面的图片会被隐藏,而这个文字不会被隐藏"/> <com.qianmo.mynestedscrolling.view.MyNestedScrollChild
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"> <TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="123\n456\n789\n111\n222\n333\n444\n555\n666\n777\n888\n999\n14\n12\n13\n44\n55\n66\n77\n88\n99\n11\n22\n33\n44\n55\n66\n77\n88\n99\n77\n88\n88\n8\n88\n88\n"
android:textColor="#f0f"
android:textSize="20sp"/>
</com.qianmo.mynestedscrolling.view.MyNestedScrollChild>
</com.qianmo.mynestedscrolling.view.MyNestedScrollParent>
</RelativeLayout>
布局文件只是简单的嵌套,MyNestedScrollParent继承Linearlayout,并实现NestedScrollingParent接口,MyNestedScrollChild同理,先来看看MyNestedScrollChild这个类吧。
MyNestedScrollChild.java
package com.qianmo.mynestedscrolling.view; import android.content.Context;
import android.os.Build;
import android.support.annotation.RequiresApi;
import android.support.v4.view.NestedScrollingChild;
import android.support.v4.view.NestedScrollingChildHelper;
import android.support.v4.view.ViewCompat;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.LinearLayout; /**
* Created by Administrator on 2017/2/14 0014.
* E-Mil:543441727@qq.com
*/ public class MyNestedScrollChild extends LinearLayout implements NestedScrollingChild {
private NestedScrollingChildHelper mNestedScrollingChildHelper;
private final int[] offset = new int[2]; //偏移量
private final int[] consumed = new int[2]; //消费
private int lastY;
private int showHeight; public MyNestedScrollChild(Context context) {
super(context);
} public MyNestedScrollChild(Context context, AttributeSet attrs) {
super(context, attrs);
} @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//第一次测量,因为布局文件中高度是wrap_content,因此测量模式为atmost,即高度不超过父控件的剩余空间
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
showHeight = getMeasuredHeight(); //第二次测量,对稿哦度没有任何限制,那么测量出来的就是完全展示内容所需要的高度
heightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
} @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
//按下
case MotionEvent.ACTION_DOWN:
lastY = (int) event.getRawY();
break;
//移动
case MotionEvent.ACTION_MOVE:
int y = (int) (event.getRawY());
int dy = y - lastY;
lastY = y;
if (startNestedScroll(ViewCompat.SCROLL_AXIS_HORIZONTAL)
&& dispatchNestedPreScroll(0, dy, consumed, offset)) //如果找到了支持嵌套滑动的父类,父类进行了一系列的滑动
{
//获取滑动距离
int remain = dy - consumed[1];
if (remain != 0) {
scrollBy(0, -remain);
} } else {
scrollBy(0, -dy);
}
break;
} return true;
} //限制滚动范围
@Override
public void scrollTo(int x, int y) {
int maxY = getMeasuredHeight() - showHeight;
if (y > maxY) {
y = maxY;
}
if (y < 0) {
y = 0;
}
super.scrollTo(x, y);
} //初始化helper对象
private NestedScrollingChildHelper getScrollingChildHelper() {
if (mNestedScrollingChildHelper == null) {
mNestedScrollingChildHelper = new NestedScrollingChildHelper(this);
mNestedScrollingChildHelper.setNestedScrollingEnabled(true);
}
return mNestedScrollingChildHelper;
} //实现一下接口
@Override
public void setNestedScrollingEnabled(boolean enabled) {
getScrollingChildHelper().setNestedScrollingEnabled(enabled);
} @Override
public boolean isNestedScrollingEnabled() {
return getScrollingChildHelper().isNestedScrollingEnabled();
} @Override
public boolean startNestedScroll(int axes) {
return getScrollingChildHelper().startNestedScroll(axes);
} @Override
public void stopNestedScroll() {
getScrollingChildHelper().stopNestedScroll();
} @Override
public boolean hasNestedScrollingParent() {
return getScrollingChildHelper().hasNestedScrollingParent();
} @Override
public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow) {
return getScrollingChildHelper().dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, offsetInWindow);
} @Override
public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {
return getScrollingChildHelper().dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow);
} @Override
public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
return getScrollingChildHelper().dispatchNestedFling(velocityX, velocityY, consumed);
} @Override
public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
return getScrollingChildHelper().dispatchNestedPreFling(velocityX, velocityY);
}
}
主要是在OnTouchEvent中先后调用了startNestedScroll()
和dispatchNestedPreScroll()
方法,在借助helper来完成NestedScrollingParent接口方法
MyNestedScrollParent.java
package com.qianmo.mynestedscrolling.view; import android.content.Context;
import android.support.v4.view.NestedScrollingParent;
import android.support.v4.view.NestedScrollingParentHelper;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewTreeObserver;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView; /**
* Created by wangjitao on 2017/2/14 0014.
* E-Mail:543441727@qq.com
* 嵌套滑动机制父View
*/ public class MyNestedScrollParent extends LinearLayout implements NestedScrollingParent {
private ImageView img;
private TextView tv;
private MyNestedScrollChild myNestedScrollChild;
private NestedScrollingParentHelper mNestedScrollingParentHelper;
private int imgHeight;
private int tvHeight; public MyNestedScrollParent(Context context) {
super(context);
} public MyNestedScrollParent(Context context, AttributeSet attrs) {
super(context, attrs);
init();
} private void init() {
mNestedScrollingParentHelper = new NestedScrollingParentHelper(this);
} //获取子view
@Override
protected void onFinishInflate() {
img = (ImageView) getChildAt(0);
tv = (TextView) getChildAt(1);
myNestedScrollChild = (MyNestedScrollChild) getChildAt(2);
img.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
if (imgHeight <= 0) {
imgHeight = img.getMeasuredHeight();
}
}
});
tv.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
if (tvHeight <= 0) {
tvHeight = tv.getMeasuredHeight();
}
}
});
} //在此可以判断参数target是哪一个子view以及滚动的方向,然后决定是否要配合其进行嵌套滚动
@Override
public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
if (target instanceof MyNestedScrollChild) {
return true;
}
return false;
} @Override
public void onNestedScrollAccepted(View child, View target, int nestedScrollAxes) {
mNestedScrollingParentHelper.onNestedScrollAccepted(child, target, nestedScrollAxes);
} @Override
public void onStopNestedScroll(View target) {
mNestedScrollingParentHelper.onStopNestedScroll(target);
} //先于child滚动
//前3个为输入参数,最后一个是输出参数
@Override
public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {
if (showImg(dy) || hideImg(dy)) {//如果需要显示或隐藏图片,即需要自己(parent)滚动
scrollBy(0, -dy);//滚动
consumed[1] = dy;//告诉child我消费了多少
}
} //后于child滚动
@Override
public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) { } //返回值:是否消费了fling
@Override
public boolean onNestedPreFling(View target, float velocityX, float velocityY) {
return false;
} //返回值:是否消费了fling
@Override
public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) {
return false;
} @Override
public int getNestedScrollAxes() {
return mNestedScrollingParentHelper.getNestedScrollAxes();
} //下拉的时候是否要向下滚动以显示图片
public boolean showImg(int dy) {
if (dy > 0) {
if (getScrollY() > 0 && myNestedScrollChild.getScrollY() == 0) {
return true;
}
} return false;
} //上拉的时候,是否要向上滚动,隐藏图片
public boolean hideImg(int dy) {
if (dy < 0) {
if (getScrollY() < imgHeight) {
return true;
}
}
return false;
} //scrollBy内部会调用scrollTo
//限制滚动范围
@Override
public void scrollTo(int x, int y) {
if (y < 0) {
y = 0;
}
if (y > imgHeight) {
y = imgHeight;
} super.scrollTo(x, y);
}
}
MyNestedScrollParent主要是实现一下功能
①、在onStartNestedScroll()中判断参数target是哪一个子view以及滚动的方向,然后决定是否要配合其进行嵌套滚动
②、在onNestedPreScroll()中获取需要滚动的距离,根据情况决定自己是否要进行滚动,最后还要将自己滚动消费掉的距离存储在consumed数组中回传给child
就这样基本实现了,很简单有没有,再看看我们接下来要实现的效果,如图:
也很简单,就不在废话了,直接和上面的项目一起,直接上源码 https://github.com/543441727/MyNestedScrolling
See You Next Time !
Android -- NestedScrolling滑动机制的更多相关文章
- 使用Android SwipeRefreshLayout了解Android的嵌套滑动机制
SwipeRefreshLayout 是在Android Support Library, revision 19.1.0加入到support v4库中的一个下拉刷新控件,关于android的下拉刷新 ...
- Android NestedScrolling嵌套滑动机制
Android NestedScrolling嵌套滑动机制 最近项目要用到官网的下拉刷新SwipeRefreshLayout,它是个容器,包裹各种控件实现下拉,不像以前自己要实现事件的拦截,都是通过对 ...
- Android NestedScrolling与分发机制
在Android5.0之间要实现控件的嵌套滑动,都是要自己处理View事件即分发机制. 共有三个方法: dispatchTouchEvent().onInterceptTouchEvent()和 ...
- 【朝花夕拾】Android自定义View篇之(七)Android事件分发机制(下)滑动冲突解决方案总结
前言 转载请声明,转自[https://www.cnblogs.com/andy-songwei/p/11072989.html],谢谢! 前面两篇文章,花了很大篇幅讲解了Android的事件分发机制 ...
- 讲讲Android事件拦截机制
简介 什么是触摸事件?顾名思义,触摸事件就是捕获触摸屏幕后产生的事件.当点击一个按钮时,通常会产生两个或者三个事件--按钮按下,这是事件一,如果滑动几下,这是事件二,当手抬起,这是事件三.所以在And ...
- Android 事件拦截机制一种粗鄙的解释
对于Android事件拦截机制,相信对于大多数Android初学者是一个抓耳挠腮难于理解的问题.其实理解这个问题并不困难. 首先,你的明白事件拦截机制到底是怎么一回事?这里说的事件拦截机制,指的是对触 ...
- Android ListView滑动过程中图片显示重复错乱闪烁问题解决
最新内容建议直接访问原文:Android ListView滑动过程中图片显示重复错乱闪烁问题解决 主要分析Android ListView滚动过程中图片显示重复.错乱.闪烁的原因及解决方法,顺带提及L ...
- Android双向滑动菜单完全解析,教你如何一分钟实现双向滑动特效
转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/9671609 记得在很早之前,我写了一篇关于Android滑动菜单的文章,其中有一个 ...
- Android事件分发机制完全解析,带你从源码的角度彻底理解
Android事件构成 在Android中,事件主要包括点按.长按.拖拽.滑动等,点按又包括单击和双击,另外还包括单指操作和多指操作.所有这些都构成了Android中的事件响应.总的来说,所有的事件都 ...
随机推荐
- js获取地址栏URL上的参数
获取地址栏上的URL参数现在最简单通用的方法应该就是下面这种了. function getUrlParam (name) { var reg = new RegExp('(^|&)' + na ...
- 基于 HTML5 Canvas 的 3D 压力器反序列化
在实际应用中,我觉得能够通过操作 JSON 文件来操作 3D 上的场景变化是非常方便的一件事,尤其是在做编辑器进行拖拽图元并且在图元上产生的一系列变化的时候,都能将数据很直观地反应给我们,这边我们简单 ...
- C# DropDownList 绑定枚举类
第一种 DropDownList_Franchiser_Type.DataSource = ListTypeForEnum(); DropDownList_Franchiser_Type.DataVa ...
- DDD实践
一. 虽然招聘是主旋律,但技术还是得不断的突破.在.net core的实践中,一开始就瞄准了DDD.需要特别感谢https://github.com/EduardoPires/EquinoxProje ...
- OpenXml读取word内容(一)
OpenXml读取word内容注意事项 1.使用OpenXml读取word内容,word后缀必须是".docx":如果word后缀是".doc"需要转成&quo ...
- weex加入iconfont
weex加入图标可能是项目开发中最头疼的事情了,还好有 阿里巴巴矢量图标库解决了开发时的图标问题,下面我们一起来踩坑吧<text class="left"></ ...
- 编码中的setCharacterEncoding 理解<转自-fancychendong>
1.pageEncoding="UTF-8"的作用是设置JSP编译成Servlet时使用的编码. 2.contentType="text/html;charset=UTF ...
- hdu 4409 Family Name List(LCA&有坑点)
Family Name List Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) ...
- 【SqlServer系列】JSON数据
1 概述 本文将结合MSDN简要概述JSON数据. 2 具体内容 JSON 是一种流行的数据格式,用于在现代 Web 和移动应用程序中交换数据. JSON 还可用于在 Microsoft Az ...
- 在java项目中使用webservice
今天学习webservice,主要参考了网络上的一些文章. 1.关于原理的介绍:个人认为这篇文章写得不错了,戳这里. 2.关于demo的编写:个人认为这篇文章很简洁,也能运行成功,戳这里. 按照上面那 ...