NestedScrolling机制(二)——实例
来源:互联网 发布:磁力链接转种子软件 编辑:程序博客网 时间:2024/05/21 18:46
1 实现自己的NestedScrollingParent和NestedScrollingChild
这个示例来自:http://blog.csdn.net/lmj121212/article/details/52974427
自己在原项目代码基础上做了精简和梳理,添加了注释以帮助理解,下载地址:项目代码(已整理)
此项目中的NestedScrollingParent和NestedScrollingChild都是自定义view,理解这个项目有助于我们理清NestedScrolling机制的工作原理。项目效果如下:
直接说代码吧:
activity_main.xml
布局文件很简单,MyNestedScrollParent是一个自定义的LinearLayout,实现了NestedScrollingParent接口,其子元素从上到下依次为一个ImageView、一个TextView和一个MyNestedScrollChild,其中MyNestedScrollChild实现了NestedScrollingChild接口。
<?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" tools:context="com.lmj.com.mynestscroll.MainActivity"> <com.lmj.com.mynestscroll.view.MyNestedScrollParent android:layout_width="match_parent" android:layout_height="match_parent" 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="@string/topStr" /> <com.lmj.com.mynestscroll.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="@string/contenttv" android:textColor="#f0f" /> </com.lmj.com.mynestscroll.view.MyNestedScrollChild> </com.lmj.com.mynestscroll.view.MyNestedScrollParent></RelativeLayout>
MainActivity.java
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); }}
MyNestedScrollChild.java
在
onTouchEvent()
中先后调用了startNestedScroll()
和dispatchNestedPreScroll()
方法,具体逻辑在注释中已经说的很清楚。至于NestedScrollingChild接口的实现则是完全借助了NestedScrollingChildHelper帮助类。
public class MyNestedScrollChild extends LinearLayout implements NestedScrollingChild { private NestedScrollingChildHelper mScrollingChildHelper; 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); } @Override public boolean onTouchEvent(MotionEvent e) { switch (e.getAction()) { case MotionEvent.ACTION_DOWN: lastY = (int) e.getRawY(); break; case MotionEvent.ACTION_MOVE: int y = (int) (e.getRawY()); int dy = y - lastY; lastY = y; if (startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL) //如果找到了支持嵌套滚动的父类 && dispatchNestedPreScroll(0, dy, consumed, offset)) {//父类进行了一部分滚动 int remain = dy - consumed[1];//获取滚动的剩余距离 if (remain != 0) { scrollBy(0, -remain); } } else { scrollBy(0, -dy); } } return true; } //scrollBy内部会调用scrollTo //限制滚动范围 @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); } private NestedScrollingChildHelper getScrollingChildHelper() { if (mScrollingChildHelper == null) { mScrollingChildHelper = new NestedScrollingChildHelper(this); mScrollingChildHelper.setNestedScrollingEnabled(true); } return mScrollingChildHelper; }//以下为接口实现-------------------------------------------------- @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); }}
MyNestedScrollParent.java
在
onStartNestedScroll()
中判断参数target是哪一个子view以及滚动的方向,然后决定是否要配合其进行嵌套滚动在
onNestedPreScroll()
中获取需要滚动的距离,根据情况决定自己是否要进行滚动,最后还要将自己滚动消费掉的距离存储在consumed数组中回传给child
public class MyNestedScrollParent extends LinearLayout implements NestedScrollingParent { private ImageView img; private TextView tv; private MyNestedScrollChild nsc; private NestedScrollingParentHelper mParentHelper; private int imgHeight; private int tvHeight; public MyNestedScrollParent(Context context, AttributeSet attrs) { super(context, attrs); init(); } public MyNestedScrollParent(Context context) { super(context); init(); } private void init() { mParentHelper = new NestedScrollingParentHelper(this); } //获取子view @Override protected void onFinishInflate() { img = (ImageView) getChildAt(0); tv = (TextView) getChildAt(1); nsc = (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) { mParentHelper.onNestedScrollAccepted(child, target, nestedScrollAxes); } @Override public void onStopNestedScroll(View target) { mParentHelper.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 mParentHelper.getNestedScrollAxes(); }//-------------------------------------------------- //下拉的时候是否要向下滚动以显示图片 public boolean showImg(int dy) { if (dy > 0) { if (getScrollY() > 0 && nsc.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); }}
2 使用系统已有的NestedScrollingChild
这个示例来自:http://blog.csdn.net/lmj623565791/article/details/43649913
代码下载地址:项目代码(已整理)
与第一个项目相比,此项目:
- 使用了系统已有的NestedScrollingChild,即RecyclerView
- NestedScrollingChild和NestedScrollingParent并不是直接的父子关系,中间隔了一个ViewPager
主要代码逻辑与第一个项目大同小异,因此不再赘述了。项目效果如下:
- NestedScrolling机制(二)——实例
- NestedScrolling机制(一)——概述
- NestedScrolling机制(一)——概述
- NestedScrolling机制(一)——概述
- NestedScrolling机制(一)——概述
- NestedScrolling机制(三)——机制本质以及源码解析
- NestedScrolling机制(三)——机制本质以及源码解析
- NestedScrolling机制(四)——最后一个例子
- NestedScrolling 嵌套滑动机制:原理简单分析(二)
- NestedScrolling 机制深入解析
- NestedScrolling机制学习(一)
- Android:NestedScrolling机制
- Android NestedScrolling与分发机制
- Android NestedScrolling嵌套滑动机制
- 利用NestedScrolling机制改造SwipeRefreshLayout
- NestedScrolling
- NestedScrolling
- Handler机制实例二
- windows自带软件 Sticky Note
- wampserver域名配置
- Java中对象序列化和反系列化
- 数据存储范围
- 【Python网络爬虫】python网络数据采集读书笔记(第三章)
- NestedScrolling机制(二)——实例
- fatal error LNK1104: 无法打开文件*.exe
- 前端轻松破解支付宝AR抢红包
- 详解D3D/OpenGL光栅化(1)
- Leetcode oj java Delete Node in a Linked List
- Ajax请求中的async:false和async:true的差异
- BZOJ1031 [JSOI2007]字符加密Cipher 后缀数组
- 我怎么学习spring-bean的作用域
- 数据库的常规命令