NestedScrolling 嵌套滑动机制:简单实践(一)
来源:互联网 发布:sql varchar转decimal 编辑:程序博客网 时间:2024/05/16 14:00
一、前言
NestedScrollView 机制是周三听其他组的同学分享才知道的。谈起技术分享,让我学习很多:
- 关于视野:学习这件事情,从来不是孤军奋战,应该多多交流和分析、讨论。正如爱尔兰作家说:你有一个苹果,我有一个苹果,我们交换一下,一人还是一个苹果;但是,你有一个思想,我有一个思想,我们交换一下,一人就有两个思想。交流的重要性。
- 关于表达:分享的同学表达能力很强,能清楚的明白参数方法的概念,这是需要基本功的。
- 关于技术进阶:技术的研究通常是建立在已知问题上的,技术是一种工具,不是为了技术而技术,应该是为了快乐而快乐。
关于NestedScrollView 的学习,想在前人的研究基础上,总结两篇文章,一篇文章讲基础应用,一篇简单的深入分析一下源码,所谓知其然,还要知其背后的思想。
本文的写作逻辑如下:
- 前言
- NestedScrollIng 相关类简介
- NestedScrollingChild 与 NestedScrollingParent 方法详解
- DEMO介绍
- 小结
二、NestedScrollIng 相关类简介
2.1、ScrollingView 上层滑动接口
继承该接口的类有:
该接口主要作用是:
An interface that can be implemented by Views to provide scroll related APIs.
2.2、NestedScrollView 嵌套滑动视图
传统的ScrollView 只是继承了帧布局,与该类相关的接口有 :
该类的作用是:
NestedScrollView is just like ScrollView, but it supports acting as both a nested scrolling parent and child on both new and old versions of Android. Nested scrolling is enabled by default.不仅支持传统的 ScrollView功能,还可以支付嵌套的父View 与子View 的滑动,系统默认开启嵌套滑动支持。
2.3、NestedScrollingChild 子视图嵌套滑动接口
与该接口相关的类有 :
该接口的作用:
This interface should be implemented by View subclasses that wish to support dispatching nested scrolling operations to a cooperating parent ViewGroup.Classes implementing this interface should create a final instance of a NestedScrollingChildHelper as a field and delegate any View methods to the NestedScrollingChildHelper methods of the same signature.
2.4、NestedScrollingParent 父视图嵌套滑动接口
与该接口相关的类有 :
该接口的作用:
This interface should be implemented by ViewGroup subclasses that wish to support scrolling operations delegated by a nested child view.Classes implementing this interface should create a final instance of a NestedScrollingParentHelper as a field and delegate any View or ViewGroup methods to the NestedScrollingParentHelper methods of the same signature.
三、NestedScrollingChild 与 NestedScrollingParent 方法详解
官方NestedScrolling机制涉及关键的类(接口),有:
NestedScrollingChildNestedScrollingParentNestedScrollingChildHelperNestedScrollingParentHelper
3.1、NestedScrollingChild相关方法解析
1、publiv void setNestedScrollingEnabled(boolean enabled);
NestedScrollingChild中的方法,设置该View是否开启Nested Scrolling机制,一个相关联方法为isNestedScrollingEnabled()
2、public boolean startNestedScroll(int axes);
在给定的方向(axes)上开始一个Nested Scroll,只有返回值是true的情况下才会执行之后的方法,否则NestedScrolling机制无效. 默认实现(NestedScrollingChildHelper中的实现)会找到该View的parent,调用parent的onStartNestedScroll方法,如果parent为null或者onStartNestedScroll()返回false,则流程结束.否则调用NestedScrollingParent.onNestedScrollAccepted给其一个初始化配置的机会.
3、public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow)
传统的事件分发机制有个onInterceptTouchEvent()方法来拦截事件,让ViewGroup可以优先于子View处理触摸事件,这个机制而Nested Scrolling机制也提供了类似的机制.这个机制的实现就是依靠dispatchNestedPreScroll方法.这个方法会在child消耗掉滑动事件之前调用,给parent一个率先处理滑动事件的机会.
offsetInWindow是自己本身的窗体变化,这个方法需要计算这个值做处理来修正touch事件的做标志。
4、public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, dyUnconsumed, int[] offsetInWindow)
分发nested scroll,这个方法会在该child处理完滑动事件之后调用.无论是否有未消耗的值都会调用. offsetInWindow 同dispatchNestedPreScroll.
5、fling相关
fling机制和scroll类似
6、public void stopNestedScroll();
停止nested scroll
3.2、NestedScrollingChild相关方法解析
1、public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes);
被NestedScrollingChild的startNestedScroll调用,实现者根据具体情况决定是否使用Nested Scroll机制来返回true或false.
2、public void onNestedScrollAccepted(View child, View target, int nestedScrollAxes);
onStartNestedScroll方法返回true之后被调用,他提供了一个初始化nested scroll相关配置的机会,之所以有这个方法可能是因为onStartNestedScroll无论是否接收nested scroll都回被调用,而该方法只有在确定要执行nested scroll时才会被调用.
3、public void onNestedPreScroll(View target, int dx, int dy, int[] consumed);
响应dispatchNestedPreScroll,在child消耗滑动事件之前调用,实现滑动处理的地方.
4、public void onNestedScroll(View target, int dxConsumed, int dyConsumed,int dxUnconsumed, int dyUnconsumed);
响应dispatchNestedScroll,在child消耗滑动事件之后调用.实现滑动处理.
5、public void onStopNestedScroll(View target);
响应stopNestedScroll,可以做清理工作;
3.3、方法调用关系
比较形象的可以聊天图:
DEMO介绍
DEMO主要涉及三个类,NestedScrollingActivity、MyNestedScrollParent、MyNestedScrollChildL;
NestedScrollingActivity:
// 隐藏actionBar getSupportActionBar().hide();
布局文件:顶层布局是相对布局,依次是MyNestedScrollParentL、MyNestedScrollChildL
MyNestedScrollParentL.java
public class MyNestedScrollParentL extends LinearLayout implements NestedScrollingParent { private ImageView mImage; private TextView mTvTitle; private MyNestedScrollChildL mMyNestedScrollChildL; private int mImageHeight; private int mTvTitleHeight; private NestedScrollingParentHelper mNestedScrollingParentHelper; public MyNestedScrollParentL(Context context) { super(context); init(); } public MyNestedScrollParentL(Context context, AttributeSet attrs) { super(context, attrs); init(); } private void init(){ mNestedScrollingParentHelper = new NestedScrollingParentHelper(this); //相关事情都交给NestedScrollingParentHelper做 } @Override protected void onFinishInflate() { super.onFinishInflate(); mImage = (ImageView)getChildAt(0);//获取第一个视图 mTvTitle = (TextView)getChildAt(1); mMyNestedScrollChildL = (MyNestedScrollChildL)getChildAt(2);//OnGlobalLayoutListener 是ViewTreeObserver的内部类,当一个视图树的布局发生改变时,可以被ViewTreeObserver监听到//获取图片的高度 mImage.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { if(mImageHeight <= 0){ mImageHeight = mImage.getMeasuredHeight(); Log.i("TEST", "mImageHeight:" + mImageHeight); } } }); //获取标题的高度 mTvTitle.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { if(mTvTitleHeight <= 0){ mTvTitleHeight = mTvTitle.getMeasuredHeight(); Log.i("TEST", "mTvTitleHeight:" + mTvTitleHeight); } } }); } @Override public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) { Log.i("TEST", "onStartNestedScroll()"); return true; } @Override public void onNestedScrollAccepted(View child, View target, int axes) { Log.i("TEST", "onNestedScrollAccepted()"); mNestedScrollingParentHelper.onNestedScrollAccepted(child, target, axes); } @Override public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) { Log.i("TEST", "onNestedScrollAccepted()"); } @Override public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) { Log.i("TEST", "onNestedPreScroll()");//父布局中除去child部分之外的视图 if(isShowImage(dy) || isHideImage(dy)){ consumed[1] = dy/2; scrollBy(0, dy); // scrollBy 其实会调用我们重写的scrollTo 方法; } } @Override public void onStopNestedScroll(View child) { Log.i("TEST", "onStopNestedScroll()"); mNestedScrollingParentHelper.onStopNestedScroll(child); } @Override public boolean onNestedPreFling(View target, float velocityX, float velocityY) { Log.i("TEST", "onNestedPreFling()"); return false; } @Override public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) { Log.i("TEST", "onNestedFling()"); return false; } @Override public int getNestedScrollAxes() { Log.i("TEST", "getNestedScrollAxes()"); return 0; } @Override public void scrollTo(int x, int y) { Log.i("TEST", "scrollTo():" + "x=" + x + "__y=" + y); if( y < 0){ //父布局中除去child部分之外的视图保持不动 y = 0; } if(y >= mImageHeight){//父布局中除去child部分之外的视图保持图片的高度 y = mImageHeight; } super.scrollTo(x, y); } /** * 判断下拉的时候是否需要向下滑动显示图片 * 下拉的时候 dy < 0 */ public boolean isShowImage(int dy){ Log.i("TEST", "getScrollY():" + getScrollY()); Log.i("TEST", "mMyNestedScrollChildL.getScrollY():" + mMyNestedScrollChildL.getScrollY()); // dy< 0 表示在下拉; if(dy < 0){ if(getScrollY() > 0 && mMyNestedScrollChildL.getScrollY() == 0){ return true; } } return false; } /** * 上滑的时候; * @param dy> 0 表示在上滑 * @return */ public boolean isHideImage(int dy){ Log.i("TEST", "getScrollY():" + getScrollY()); Log.i("TEST", "mMyNestedScrollChildL.getScrollY():" + mMyNestedScrollChildL.getScrollY()); if(dy > 0){ if(getScrollY() < mImageHeight){ return true; } } return false; } @Override public boolean onTouchEvent(MotionEvent event) { Log.i("TEST", "onTouchEvent():" + event.getAction()); return super.onTouchEvent(event); } @Override public boolean dispatchTouchEvent(MotionEvent event) { Log.i("TEST","dispatchTouchEvent _ getY():getRawY:"+event.getRawY()); return super.dispatchTouchEvent(event); }}
MyNestedScrollChildL .java
public class MyNestedScrollChildL extends LinearLayout implements NestedScrollingChild { private NestedScrollingChildHelper mScrollingChildHelper; public static final String Tag = "MyNestedScrollChildL"; private final int[] mScrollOffset = new int[2]; private final int[] mScrollConsumed = new int[2]; private final int[] mNestedOffsets = new int[2]; private int mLastTouchX; private int mLastTouchY; private int showHeight; public MyNestedScrollChildL(Context context) { super(context); init(); } public MyNestedScrollChildL(Context context, AttributeSet attrs) { super(context, attrs); init(); } private void init(){ if (mScrollingChildHelper == null) { mScrollingChildHelper = new NestedScrollingChildHelper(this); mScrollingChildHelper.setNestedScrollingEnabled(true); } } @Override public void setNestedScrollingEnabled(boolean enabled) { Log.i(Tag, "setNestedScrollingEnabled:" + enabled); mScrollingChildHelper.setNestedScrollingEnabled(enabled); } @Override public boolean isNestedScrollingEnabled() { Log.i(Tag, "isNestedScrollingEnabled"); return mScrollingChildHelper.isNestedScrollingEnabled(); } @Override public boolean startNestedScroll(int axes) { return mScrollingChildHelper.startNestedScroll(axes); } @Override public void stopNestedScroll() { Log.i(Tag, "stopNestedScroll"); mScrollingChildHelper.stopNestedScroll(); } @Override public boolean hasNestedScrollingParent() { Log.i(Tag, "hasNestedScrollingParent"); return mScrollingChildHelper.hasNestedScrollingParent(); } @Override public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow) { Log.i(Tag, "dispatchNestedScroll()"); return mScrollingChildHelper.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, offsetInWindow); } @Override public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) { Log.i(Tag, "dispatchNestedPreScroll()"); return mScrollingChildHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow); } @Override public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) { return mScrollingChildHelper.dispatchNestedFling(velocityX, velocityY, consumed); } @Override public boolean dispatchNestedPreFling(float velocityX, float velocityY) { Log.i(Tag, "dispatchNestedPreFling()"); return mScrollingChildHelper.dispatchNestedPreFling(velocityX, velocityY); } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()){ case MotionEvent.ACTION_DOWN: mLastTouchY = (int)(event.getRawY() + 0.5f); int nestedScrollAxis = ViewCompat.SCROLL_AXIS_NONE; nestedScrollAxis |= ViewCompat.SCROLL_AXIS_HORIZONTAL; //按位或运算 Log.i(Tag, "nestedScrollAxis:" + nestedScrollAxis); startNestedScroll(nestedScrollAxis); break; case MotionEvent.ACTION_MOVE: Log.i(Tag, "Child--getRawY:" + event.getRawY()); int x = (int) (event.getX() + 0.5f); int y = (int) (event.getRawY() + 0.5f); int dx = mLastTouchX - x; int dy = mLastTouchY - y; Log.i(Tag, "child:dy:" + dy + ",mLastTouchY:" + mLastTouchY + ",y;" + y); mLastTouchY = y; mLastTouchX = x; if (dispatchNestedPreScroll(dx, dy, mScrollConsumed, mScrollOffset)) { dy -= mScrollConsumed[1]; if (dy == 0) { return true; } } 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); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { if (showHeight <= 0) { showHeight = getMeasuredHeight(); } else { heightMeasureSpec = MeasureSpec.makeMeasureSpec(1000000, MeasureSpec.UNSPECIFIED); } super.onMeasure(widthMeasureSpec, heightMeasureSpec); }}
执行效果:
向上滑动时,标题可以固定:
参考:
1、NestedScrollingChild
2、NestedScrollingParent
3、NestedScrolling
- NestedScrolling 嵌套滑动机制:简单实践(一)
- NestedScrolling 嵌套滑动机制:原理简单分析(二)
- 浅析:Android 嵌套滑动机制(NestedScrolling)
- Android 嵌套滑动机制(NestedScrolling)
- 详解:Android嵌套滑动机制 (NestedScrolling)
- 详解:Android嵌套滑动机制 (NestedScrolling)
- Android 嵌套滑动机制(NestedScrolling)
- Android 嵌套滑动机制(NestedScrolling)
- Android 嵌套滑动机制(NestedScrolling)
- Android 嵌套滑动机制(NestedScrolling)
- Android嵌套滑动机制(NestedScrolling)
- Android 嵌套滑动机制(NestedScrolling)
- Android NestedScrolling嵌套滑动机制
- 解决嵌套滑动的卡顿,Android嵌套滑动机制(NestedScrolling)
- 一探Android嵌套滑动 NestedScrolling
- android嵌套滑动NestedScrolling
- 嵌套滑动 NestedScrolling
- 从源码角度分析嵌套滑动机制NestedScrolling
- 简易爬虫
- 第四天,linux下使用apache ant搭建一个java project
- Java_Ant详解
- GIT 学习记录
- jsp回顾
- NestedScrolling 嵌套滑动机制:简单实践(一)
- 【长郡NOIP2014模拟10.22】字符串查询
- 项目管理利器——maven环境配置
- 【JZOJ3873】【NOIP2014八校联考第4场第2试10.20】乐曲创作(music)
- 【BZOJ 3166】[Heoi2013]Alo 可持久化trie树+set
- J2EE框架之Structs
- 文章标题
- LeetCode 476. Number Complement(Java)
- Numpy学习(1)numpy文件存取