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中的事件响应.总的来说,所有的事件都 ...
随机推荐
- 16进制到byte转换
我们经常会看到这样的语法 (byte) 0xAD 0xAD实际是个16进制,转换成二进制为:10101101,转换成10进制是:173,它是个正数 10101101只是int的简写,int由4个byt ...
- 初识CSS
css解释 css样式: css是英文Cascading Style Sheets的缩写,称为层叠样式表,用于对页面进行美化,CSS的可以使页面更加的美观.基本上所有的html页面都或多或少的使用cs ...
- 打开和创建SqlCe(.sdf文件)
打开SqlCe的工具有些少,目前能看到Vs2010安装插件之后打开.sdf文件 [转载]https://weblogs.asp.net/scottgu/vs-2010-sp1-and-sql-ce 需 ...
- 大白话Vue源码系列目录
.first-level{ font-size: 1.2rem; cursor: default; color: #666; } .second-level{ font-size: 1.1rem; p ...
- github搭建个人博客 hexo框架 next主题
之前就想搭建自己的博客,现在终于得以实施. 参考了多篇博客,然后又在虚拟机了往往复复试了好多次, 现在基本搭建完毕. 我是基于hexo博客框架, next主题搭建的博客, 静态网页. 大体就是, ...
- PHP-学习之路1
相信入职快有5个月了,目前项目做过HIS,zySystem,ComStoreSystem当然今天不是来介绍的,后期直到第四个月后APP护身宝经理拍板今后也就是明年正式交于我们团队接手与扩展,运维.虽然 ...
- javaWeb中URLEncoder.encode空格问题
近期开发一个在线坐席的功能.发现推送的消息中空格变成了+ .查询发现URLEncoder.encode的问题.曾经用的时候也没注意过,解决的方法网上是对URLEncoder.encode的之后的字符串 ...
- POJ 3928 & hdu 2492 & Uva1428 PingPong 【树状数组】
Ping pong Time Limit: 2000/1000 MS (Java/Others) ...
- Android Root原理初探
Root Linux:Root == Windows:Adminstrator Android是Linux系统吗? 操作系统 = 系统内核 + 文件系统 Linux发行版:Linux内核 + 文件系统 ...
- Linux批量杀死包括某个keyword的进程
ps -ef|grep ./amplxe-gui|grep -v grep|cut -c 9-15|xargs kill -9 批量杀死包括keyword"./amplxe-gui" ...