Android NestedScrolling帮你实现一个简单的嵌套滑动
来源:互联网 发布:中医药大数据 编辑:程序博客网 时间:2024/04/27 13:20
1. 仅仅有图片可以参考.
1. 首先你得知道正常的事件分发机制,即当一个事件被某个拦截时,当前手势接下来的事件都会交给View进行处理。
2. 这也是为什么ScrollView嵌套ListView 产生滑动冲突,体验不佳的原因。
3. 使用NestedScrollParent组合NestedScrollChild并不是改变这种机制,而正是利用这种机制进行恰当的回调。
4. NestedScrollParent和NestedScrollChild是2个接口,实现NestedScrollParent的View不再需要拦截scroll事件。
5.也就是说全程处理滑动事件的仅仅是子View 但是子View能滑动多少,实现NestedScrollParent的父View说了算。
5. 实现NestedScrollChild接口的子View 需要在touch事件里恰当的处理好回调方法,及时准确的通知父View 自己准备要进行的动作。
6. 子View 通过把scroll的动作告知父View ,父View可以通过实现回调的方法来响应这些回调 1.处理自身的位置 2.告知子View要处理的位置。
7. 举个栗子:RecyclerView接到滑动事件,要向上滑动10,再没滑动之前 把要滑动的情况优先告知父View(实现NestedScrollParent)。
8. 父View再接到RecyclerView可以产生向上滑动10的时候优先做出处理,先让自己身滑动5,并告知RecyclerView “我已经消费了5”。
9. RecyclerView 把本来要消费的10 和已经被父View消费的5 综合起来判断自己要消费多少。即RecyclerView仅仅向上滑动了5.
10. 所以关键点就在于你什么时候想消费、并且消费多少 子View的滑动,这影响到父View和子View的具体展示。
布局:仅仅是一个父容器(实现NestedScrollParent) 内嵌入 TopView 和实现了NestedScrollChild的RecyclerView。
<?xml version="1.0" encoding="utf-8"?><com.yushilei.nestedscrolling.NestedScrollLayout android:id="@+id/nested" 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" android:background="#d8daa2" android:orientation="vertical" tools:context=".MainActivity"> <RelativeLayout android:id="@+id/text_top" android:layout_width="match_parent" android:layout_height="300dp" android:background="#4400ff00"> <TextView android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:text="topView" android:textSize="30sp" android:textStyle="bold"/> </RelativeLayout> <android.support.v7.widget.RecyclerView android:id="@+id/recycler" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#00ccff" android:padding="10dp"/></com.yushilei.nestedscrolling.NestedScrollLayout>
代码:
package com.yushilei.nestedscrolling;import android.content.Context;import android.support.v4.view.NestedScrollingParent;import android.support.v4.view.ViewCompat;import android.support.v7.widget.RecyclerView;import android.util.AttributeSet;import android.util.Log;import android.view.View;import android.view.ViewGroup;import android.widget.LinearLayout;import android.widget.OverScroller;/** * @author by yushilei. * @time 2016/9/1 -10:57. * @Desc */public class NestedScrollLayout extends LinearLayout implements NestedScrollingParent { private View topV; private RecyclerView recycler; private OverScroller mScroller; public NestedScrollLayout(Context context, AttributeSet attrs) { super(context, attrs); } String TAG = "NestedScrollLayout"; /** * 当实现NestedScrollingChild的子view将要滑动时,回调实现NestedScrollingParent的父View * nestedScrollAxes代表方向 父View可以根据自己的意愿来响应子view在某个方向上的滑动 */ @Override public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) { Log.d(TAG, "onStartNestedScroll nestedScrollAxes=" + nestedScrollAxes); //1、响应纵向滑动 即上下滑动时,父View可以接受响应 return (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0; } @Override public void onNestedScrollAccepted(View child, View target, int axes) { Log.d(TAG, "onNestedScrollAccepted axes=" + axes); } @Override public void onStopNestedScroll(View child) { Log.d(TAG, "onStopNestedScroll" + child); } @Override public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) { Log.d(TAG, "onNestedScroll dxConsumed=" + dxConsumed + ";dyConsumed=" + dyConsumed + ";dxUnconsumed=" + dxUnconsumed + ";dyUnconsumed=" + dyUnconsumed); } /** * 如果父View 在onStartNestedScroll 响应子view,那么子View在滑动即将滑动时 * 会产生子View要滑动的距离 dx 和dy * onNestedPreScroll 可以通过 dx dy consumed[0] consumed[1] 来影响父View 和子View的变化 * 即 父View可以消耗掉部分 或者全部子View的滑动距离 */ @Override public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) { //2、dy>0 代表向上滑动 dy<0 代表向下滑动 //3、 如果向上滑动 判断TopView是否需要进行隐藏 boolean hiddenTop = dy > 0 && getScrollY() < topV.getHeight(); //4、 如果向下滑动 判断是否可以展示TopView boolean showTop = dy < 0 && getScrollY() > 0 && !ViewCompat.canScrollVertically(target, -1); if (hiddenTop || showTop) { //5、如果 需要隐藏TopView 或者需要展示TopView 都应该消费掉 dy //5.1、并且把消费掉的距离转换成父View Scroll的距离,已完成对TopView的展示或隐藏 //scrollBy 内部调用scrollTo 所以需要考虑scroll过量的情况,所以从写scrollTo scrollBy(0, dy); // consumed[1]用来告诉 子View 父View 对dy的消费程度, // 子View 就是根据dy 和consumed[1] 来判断自己还能滑动多少距离的 consumed[1] = dy; } Log.d(TAG, "onNestedPreScroll dx=" + dx + ";dy=" + dy + ";consumed" + consumed); } public void fling(int velocityY) { mScroller.fling(0, getScrollY(), 0, velocityY, 0, 0, 0, topV.getHeight()); invalidate(); } /** * 就是当快速滑动手指Up的瞬间 产生的Fling动作,这个时候需要根据Fling的速度 * 来计算View停下来的位置 */ @Override public boolean onNestedPreFling(View target, float velocityX, float velocityY) { Log.d(TAG, "onNestedPreFling velocityX" + velocityX + ";velocityY=" + velocityY); if (getScrollY() >= topV.getHeight()) { return true; } fling((int) velocityY); return true; } @Override protected void onFinishInflate() { super.onFinishInflate(); topV = findViewById(R.id.text_top); recycler = (RecyclerView) findViewById(R.id.recycler); mScroller = new OverScroller(getContext()); } /** * 重写scrollTo 需要根据 x ,y 的坐标来判断是否还能继续滑动了 */ @Override public void scrollTo(int x, int y) { Log.d(TAG, "scrollTo x=" + x + ";y=" + y); //当y的值=0时 就是TopView全部露出的时候,不能继续向下滑动了 if (y < 0) { y = 0; } //如果y的值大于 TopView的高度,那么就是父View 始终保持在向上滑动topV.getHeight()的距离上 if (y > topV.getHeight()) { y = topV.getHeight(); } if (y != getScrollY()) { super.scrollTo(x, y); } } /** * 必须重写onMeasure 不然 RecyclerView会始终占据部分屏蔽 */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); //1、在这里 我们实际上是让RecyclerView 的高度为 整个父布局的高度 ViewGroup.LayoutParams layoutParams = recycler.getLayoutParams(); layoutParams.height = getMeasuredHeight(); recycler.setLayoutParams(layoutParams); //2、改变父布局的高度 即让父布局的高度为 topView的高度+ RecyclerView的高度 //3、按本例子来看,打印的结果 即 RecyclerView的高度为屏幕高度 ,父布局高度为 屏幕高度+ TopView的高度 //4、也就是说 父布局不仅仅是初始化看到的屏幕那边大,如果你给RecyclerView 的Adapter 方法加打印的话 //你就可以看到 RecyclerView实际加载的布局 要比初始显示的多很多,就是这个原因了 setMeasuredDimension(getMeasuredWidth(), topV.getMeasuredHeight() + recycler.getMeasuredHeight()); Log.d(TAG, "Height=" + getMeasuredHeight() + " TopV height=" + topV.getHeight() + ";recycler =" + recycler.getMeasuredHeight()); }}
- 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)
- Android 5.0 嵌套滑动NestedScrolling 源码解析
- 一探Android嵌套滑动 NestedScrolling
- 二探Android嵌套滑动 NestedScrolling
- Android嵌套滑动机制(NestedScrolling)
- 【操作系统】C语言模拟操作系统优先数调度算法
- 图的遍历
- Qt之QScrollArea
- Bellman-Ford算法及其队列优化与实战入门
- 全球首款同时同频全双工软件无线电平台
- Android NestedScrolling帮你实现一个简单的嵌套滑动
- HDU 4165 Pills (卡特兰数)
- 知识链接
- JMX之通过RMI方式连接JMX Server
- 使用Xcode7来跑iOS10真机
- ArcGIS 10.2中栅格数据的属性表问题
- left join 左边有数据,右边无数据,查询结果出现inner join的情况(and 和 where 的区别)
- 关于调用频繁调用MediaPlayer播放音乐导致ANR的解决办法
- 线程与进程的区别