一、概述

Android在support.v4包中为大家提供了两个非常神奇的类:
如果你从未听说过这两个类,没关系,听我慢慢介绍,你就明白这两个类可以用来干嘛了。相信大家都见识过或者使用过CoordinatorLayout,通过这个类可以非常便利的帮助我们完成一些炫丽的效果,例如下面这样的:
这样的效果就非常适合使用NestedScrolling机制去完成,并且CoordinatorLayout背后其实也是利用着这套机制,So,我相信你已经明白这套机制可以用来干嘛了。
但是,我相信你还有个问题
恩,我们简单分析下:
按照上图:
假设我们按照传统的事件分发去理解,首先我们滑动的是下面的内容区域,而移动却是外部的ViewGroup在移动,所以按照传统的方式,肯定是外部的Parent拦截了内部的Child的事件;但是,上述效果图,当Parent滑动到一定程度时,Child又开始滑动了,中间整个过程是没有间断的。从正常的事件分发(不手动调用分发事件,不手动去发出事件)角度去做是不可能的,因为当Parent拦截之后,是没有办法再把事件交给Child的,事件分发,对于拦截,相当于一锤子买卖,只要拦截了,当前手势接下来的事件都会交给Parent(拦截者)来处理。
但是NestedScrolling机制来处理这个事情就很好办,所以对这个机制进行深入学习,一来有助于我们编写嵌套滑动时一些特殊的效果;二来是我为了对CoordinatorLayout做分析的铺垫~~~
ps:具体在哪个v4版本中添加的,就不去深究了,如果你的v4中没有上述两个类,升级下你的v4版本。NestedScrolling机制这个词,个人称呼,不清楚官方有没有这么叫,勿深究。
二、预期效果
当然讲解这两个类,肯定要有案例的支撑,不然太过于空洞了。好在,我这里有个非常好的案例可以来描述:
很久以前,我写过这样一篇文章:
Android通过自定义控件实现360软件详情页效果
完全按照传统的方式去编写的,而且为了连续滑动,做了一些非常特殊处理,比如手动去分发DOWN事件类的,有兴趣可以阅读下。
效果图是这样的:
今天我们就利用这个效果,作为NestedSroll机制的案例,最后我们还会简单分析一下源码,其实源码还是比较简单的~~
ps:CoordinatorLayout可以很方便实现该效果,后续的文章也会对CoordinateLayout做一些分析。
三、实现
上述效果图,分为3部分:顶部布局;中间的ViewPager指示器;以及底部的RecyclerView;
RecyclerView其实就是NestedSrollingChild的实现类,所以本例主要的角色是去实现NestedScrollingParent.
(1)布局文件
首先预览下布局文件,脑子里面有个大致的布局:
<com.zhy.view.StickyNavLayout xmlns:tools="http://schemas.android.com/tools" xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <RelativeLayout android:id="@id/id_stickynavlayout_topview" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="#4400ff00" > <TextView android:layout_width="match_parent" android:layout_height="256dp" android:gravity="center" android:text="软件介绍" android:textSize="30sp" android:textStyle="bold" /> </RelativeLayout> <com.zhy.view.SimpleViewPagerIndicator android:id="@id/id_stickynavlayout_indicator" android:layout_width="match_parent" android:layout_height="50dp" android:background="#ffffffff" > </com.zhy.view.SimpleViewPagerIndicator> <android.support.v4.view.ViewPager android:id="@id/id_stickynavlayout_viewpager" android:layout_width="match_parent" android:layout_height="match_parent" > </android.support.v4.view.ViewPager> </com.zhy.view.StickyNavLayout>
StickyNavLayout是直接继承自LinearLayout的,并且设置的是orientation="vertical",所以直观的就是控件按顺序纵向排列,至于测量需要做一些特殊的处理,因为不是本文的重点,可以自己查看源码,或者上面提到的文章。
(2) 实现NestedScrollingParent
NestedScrollingParent是一个接口,实现它需要实现如下方法:
public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes); public void onNestedScrollAccepted(View child, View target, int nestedScrollAxes); 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();
在写具体的实现前,先对需要用到的上述方法做一下简单的介绍:
主要关注的就是这三个方法~
这里内部View表示不一定非要是直接子View,只要是内部View即可。
下面看一下我们具体的实现:
public class StickyNavLayout extends LinearLayout implements NestedScrollingParent
{
@Override
public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes)
{
return (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0;
}
@Override
public void onNestedPreScroll(View target, int dx, int dy, int[] consumed)
{
boolean hiddenTop = dy > 0 && getScrollY() < mTopViewHeight;
boolean showTop = dy < 0 && getScrollY() > 0 && !ViewCompat.canScrollVertically(target, -1);
if (hiddenTop || showTop)
{
scrollBy(0, dy);
consumed[1] = dy;
}
}
@Override
public boolean onNestedPreFling(View target, float velocityX, float velocityY)
{
if (getScrollY() >= mTopViewHeight) return false;
fling((int) velocityY);
return true;
}
}
以上代码就能实现下面的效果:
对于fling方法,我们利用了OverScroll的fling的方法,对于边界检测,是重写了scrollTo方法:
public void fling(int velocityY)
{
mScroller.fling(0, getScrollY(), 0, velocityY, 0, 0, 0, mTopViewHeight);
invalidate();
}
@Override
public void scrollTo(int x, int y)
{
if (y < 0)
{
y = 0;
}
if (y > mTopViewHeight)
{
y = mTopViewHeight;
}
if (y != getScrollY())
{
super.scrollTo(x, y);
}
}
详细的解释可以看上面提到的文章,这里就不重复了。
到这里呢,可以看到NestedScrolling机制说白了非常简单:
就是NestedScrollingParent内部的View,在滑动到时候,会首先将dx、dy传入给NestedScrollingParent,NestedScrollingParent可以决定是否对其进行消耗,一般会根据需求消耗部分或者全部(不过这里并没有实际的约束,你可以随便写消耗多少,可能会对内部View造成一定的影响)。
用白话和原本的事件分发机制作对比就是这样的(针对正常流程下一次手势):
具体的源码会比本博文复杂,因为涉及到触摸非内部View区域的一些交互,非本博文重点,可以参考源码。
四、原理
原理其实就是看内部View什么时候回调NestedScrollingParent各种方法的,直接定位到内部View的onTouchEvent:
@Override
public boolean onTouchEvent(MotionEvent e) {
switch (action) {
case MotionEvent.ACTION_DOWN: {
int nestedScrollAxis = ViewCompat.SCROLL_AXIS_NONE;
if (canScrollHorizontally) {
nestedScrollAxis |= ViewCompat.SCROLL_AXIS_HORIZONTAL;
}
if (canScrollVertically) {
nestedScrollAxis |= ViewCompat.SCROLL_AXIS_VERTICAL;
}
startNestedScroll(nestedScrollAxis);
} break;
case MotionEvent.ACTION_MOVE: {
if (dispatchNestedPreScroll(dx, dy, mScrollConsumed, mScrollOffset)) {
dx -= mScrollConsumed[0];
dy -= mScrollConsumed[1];
vtev.offsetLocation(mScrollOffset[0], mScrollOffset[1]);
}
} break;
case MotionEvent.ACTION_UP: {
fling((int) xvel, (int) yvel);
resetTouch();
} break;
case MotionEvent.ACTION_CANCEL: {
cancelTouch();
} break;
}
return true;
}
可以看到:
ACTION_DOWN调用了startNestedScroll;ACTION_MOVE中调用了dispatchNestedPreScroll;ACTION_UP可能会触发fling以调用resetTouch。
startNestedScroll内部实际上:
#NestedScrollingChildHelper
public boolean startNestedScroll(int axes) {
if (hasNestedScrollingParent()) {
// Already in progress
return true;
}
if (isNestedScrollingEnabled()) {
ViewParent p = mView.getParent();
View child = mView;
while (p != null) {
if (ViewParentCompat.onStartNestedScroll(p, child, mView, axes)) {
mNestedScrollingParent = p;
ViewParentCompat.onNestedScrollAccepted(p, child, mView, axes);
return true;
}
if (p instanceof View) {
child = (View) p;
}
p = p.getParent();
}
}
return false;
}
去寻找NestedScrollingParent,然后回调onStartNestedScroll和onNestedScrollAccepted。
dispatchNestedPreScroll中会回调onNestedPreScroll方法,内部的scrollByInternal中还会回调onNestedScroll方法。
fling中会回调onNestedPreFling和onNestedFling方法。
resetTouch中则会回调onStopNestedScroll。
代码其实没什么贴的,大家直接找到onTouchEvent一眼就能看到,调用的方法名都是dispatchNestedXXX方法,实际内部都是通过NestedScrollingChildHelper实现的。
所以如果你需要实现和NestedScrollingParent协作的内部View,记得实现NestedScrollingChild,然后内部借助NestedScrollingChildHelper这个辅助类,核心的方法都封装好了,你只需要在恰当的实际去传入参数调用方法即可。
ok,这样的一个机制一定要去试试,很多滑动相关的效果都可以借此实现;
源码地址:
github地址:https://github.com/hongyangAndroid/Android-StickyNavLayout
本地下载:http://xiazai./201705/yuanma/Android-StickyNavLayout().rar
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对的支持。
# android
# 嵌套滑动机制
# nestedscrolling机制
# nestedscrolling使用
# Android嵌套滑动冲突的解决方法
# Android解决viewpager嵌套滑动冲突并保留侧滑菜单功能
# Android中RecyclerView嵌套滑动冲突解决的代码片段
# Android深入探究自定义View之嵌套滑动的实现
# 回调
# 如果你
# 有个
# 你可以
# 是这样
# 这两个
# 对其
# 的是
# 都是
# 就能
# 还会
# 会对
# 可以看到
# 消耗掉
# 自定义
# 这套
# 涉及到
# 可以根据
# 中会
# 可以用来
相关文章:
如何快速生成ASP一键建站模板并优化安全性?
深圳网站制作的公司有哪些,dido官方网站?
广州顶尖建站服务:企业官网建设与SEO优化一体化方案
高端企业智能建站程序:SEO优化与响应式模板定制开发
唐山网站制作公司有哪些,唐山找工作哪个网站最靠谱?
深圳 网站制作,深圳招聘网站哪个比较好一点啊?
如何通过建站之星自助学习解决操作问题?
如何在橙子建站中快速调整背景颜色?
高防服务器租用如何选择配置与防御等级?
详解jQuery中基本的动画方法
齐河建站公司:营销型网站建设与SEO优化双核驱动策略
活动邀请函制作网站有哪些,活动邀请函文案?
建站之星安装需要哪些步骤及注意事项?
JS中使用new Date(str)创建时间对象不兼容firefox和ie的解决方法(两种)
香港服务器部署网站为何提示未备案?
实例解析Array和String方法
如何在香港服务器上快速搭建免备案网站?
,交易猫的商品怎么发布到网站上去?
如何通过PHP快速构建高效问答网站功能?
高端网站建设与定制开发一站式解决方案 中企动力
教学论文网站制作软件有哪些,写论文用什么软件
?
,sp开头的版面叫什么?
招贴海报怎么做,什么是海报招贴?
如何在云主机上快速搭建网站?
最好的网站制作公司,网购哪个网站口碑最好,推荐几个?谢谢?
香港代理服务器配置指南:高匿IP选择、跨境加速与SEO优化技巧
学生网站制作软件,一个12岁的学生写小说,应该去什么样的网站?
建站之星如何实现网站加密操作?
微信小程序 五星评分(包括半颗星评分)实例代码
网站制作大概多少钱一个,做一个平台网站大概多少钱?
官网网站制作腾讯审核要多久,联想路由器newifi官网
建站之星代理平台如何选择最佳方案?
香港服务器租用费用高吗?如何避免常见误区?
电影网站制作价格表,那些提供免费电影的网站,他们是怎么盈利的?
如何设计高效校园网站?
简单实现Android文件上传
建站之星安装模板失败:服务器环境不兼容?
,柠檬视频怎样兑换vip?
黑客如何通过漏洞一步步攻陷网站服务器?
建站VPS选购需注意哪些关键参数?
如何快速启动建站代理加盟业务?
手机网站制作与建设方案,手机网站如何建设?
图册素材网站设计制作软件,图册的导出方式有几种?
北京网站制作的公司有哪些,北京白云观官方网站?
ppt制作免费网站有哪些,ppt模板免费下载网站?
广东企业建站网站优化与SEO营销核心策略指南
如何快速生成凡客建站的专业级图册?
建站之星安装路径如何正确选择及配置?
网站制作培训多少钱一个月,网站优化seo培训课程有哪些?
如何选购建站域名与空间?自助平台全解析
*请认真填写需求信息,我们会在24小时内与您取得联系。