Android自定义View--使用NestScrolling机制实现一个上下滑动退出Layout
来源:互联网 发布:2016淘宝如何收藏店铺 编辑:程序博客网 时间:2024/05/01 00:05
效果图
NestScrolling的实现
新建ElasticDragDismissFrameLayout
继承自FrameLayout
,重写onStartNestedScroll
,onNestedPreScroll
,onNestedScroll
,onStopNestedScroll
方法,同时onNestedPreScroll
,onNestedScroll
,onStopNestedScroll
去掉调用父类的方法,改由我们自己实现。
package rc.loveq.elasticdragdismiss.widget;import android.content.Context;import android.support.annotation.NonNull;import android.support.annotation.Nullable;import android.util.AttributeSet;import android.util.Log;import android.view.View;import android.widget.FrameLayout;/** * Author:Rc * 0n 2017/10/29 11:17 */public class ElasticDragDismissFrameLayout extends FrameLayout { public ElasticDragDismissFrameLayout(@NonNull Context context) { this(context, null, 0, 0); } public ElasticDragDismissFrameLayout(@NonNull Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0, 0); } public ElasticDragDismissFrameLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { this(context, attrs, defStyleAttr, 0); } public ElasticDragDismissFrameLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } @Override public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) { Log.d("ElasticDragDismissFrame", "onStartNestedScroll"); return super.onStartNestedScroll(child, target, nestedScrollAxes); } @Override public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) { Log.d("ElasticDragDismissFrame", "onNestedPreScroll dy:" + dy); } @Override public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) { Log.d("ElasticDragDismissFrame", "onNestedScroll dyConsumed" + dyConsumed + " dyUnconsumed:" + dyUnconsumed); } @Override public void onStopNestedScroll(View child) { Log.d("ElasticDragDismissFrame", "onStopNestedScroll"); }}
然后在布局中引入
<?xml version="1.0" encoding="utf-8"?><rc.loveq.elasticdragdismiss.widget.ElasticDragDismissFrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="rc.loveq.elasticdragdismiss.MainActivity"> <android.support.v7.widget.RecyclerView android:id="@+id/recycler" android:layout_width="match_parent" android:layout_height="match_parent"/></rc.loveq.elasticdragdismiss.widget.ElasticDragDismissFrameLayout>
给RecyclerView
设置adapter
和layoutManager
,MyAdapter
的代码这里就不贴了。
public class MainActivity extends AppCompatActivity { RecyclerView mRecyclerView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mRecyclerView = findViewById(R.id.recycler); mRecyclerView.setAdapter(new MyAdapter()); mRecyclerView.setLayoutManager(new LinearLayoutManager(this)); }}
onStartNestedScroll
现在可以滚动了,但是发现在控制台NestScrolling
4个方法中只有onStartNestedScroll
输出。
10-29 11:55:36.350 14079-14079/rc.loveq.elasticdragdismiss D/ElasticDragDismissFrame: onStartNestedScroll10-29 11:55:36.351 14079-14079/rc.loveq.elasticdragdismiss D/ElasticDragDismissFrame: onStartNestedScroll10-29 11:55:37.573 14079-14079/rc.loveq.elasticdragdismiss D/ElasticDragDismissFrame: onStartNestedScroll10-29 11:55:37.574 14079-14079/rc.loveq.elasticdragdismiss D/ElasticDragDismissFrame: onStartNestedScroll
到底是为什么呢?在源码中我们看到一段关于onStartNestedScroll
方法的描述
......This method will be called in response to a descendant view invoking{@link View#startNestedScroll(int)}. Each parent up the view hierarchy will be given an opportunity to respond and claim the nested scrolling operation by returning<code>true</code>This method may be overridden by ViewParent implementations to indicate when the view is willing to support a nested scrolling operation that is about to begin. If it returns true, this ViewParent will become the target view's nested scrolling parent for the duration of the scroll operation in progress. When the nested scroll is finished this ViewParent will receive a call to {@link #onStopNestedScroll(View)}......
也就是说onStartNestedScroll
返回true
我们才能响应嵌套滚动操作,响应后序的滚动事件。这里我们这在垂直方向返回true
@Override public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) { Log.d("ElasticDragDismissFrame", "onStartNestedScroll"); return (nestedScrollAxes & View.SCROLL_AXIS_VERTICAL) != 0; }
这时候我们滚动RecyclerView
,在控制台能看到每个方法的回调都有了。
onNestedPreScroll
接下来,先介绍一下onNestedPreScroll
方法各个参数的意思
@Override public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) { }
@param target View that initiated the nested scroll 引发嵌套滚动的View(这里是RecyclerView) @param dx Horizontal scroll distance in pixels @param dy Vertical scroll distance in pixels @param consumed Output. The horizontal and vertical scroll distance consumed by this parent 父亲(这里是ElasticDragDismissFrameLayout)消耗的距离
当RecyclerView
准备滚动的时候,还没滚动,会调用onNestedPreScroll
,告诉ElasticDragDismissFrameLayout
,要不要消费RecyclerView
我的滚动距离,如果不消费,那么我就开始滚动了。如果消费,那么RecyclerView
滚动的距离=准备滚动的距离-被消费的距离。
也就是说如果
@Override public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) { Log.d("ElasticDragDismissFrame", "onStartNestedScroll dy:" + dy); consumed[1] = dy; }
那么表示ElasticDragDismissFrameLayout
完全消费掉RecyclerView
滚动的距离,RecyclerView
无法滚动(注意:这里fling没有消费),这时ElasticDragDismissFrameLayout
是这样的
package rc.loveq.elasticdragdismiss.widget;import android.content.Context;import android.support.annotation.NonNull;import android.support.annotation.Nullable;import android.util.AttributeSet;import android.util.Log;import android.view.View;import android.widget.FrameLayout;/** * Author:Rc * 0n 2017/10/29 11:17 */public class ElasticDragDismissFrameLayout extends FrameLayout { public ElasticDragDismissFrameLayout(@NonNull Context context) { this(context, null, 0, 0); } public ElasticDragDismissFrameLayout(@NonNull Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0, 0); } public ElasticDragDismissFrameLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { this(context, attrs, defStyleAttr, 0); } public ElasticDragDismissFrameLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } @Override public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) { Log.d("ElasticDragDismissFrame", "onStartNestedScroll"); return (nestedScrollAxes & View.SCROLL_AXIS_VERTICAL) != 0; } @Override public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) { Log.d("ElasticDragDismissFrame", "onStartNestedScroll dy:" + dy); consumed[1] = dy; } @Override public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) { Log.d("ElasticDragDismissFrame", "onNestedScroll dyConsumed" + dyConsumed + " dyUnconsumed:" + dyUnconsumed); } @Override public void onStopNestedScroll(View child) { Log.d("ElasticDragDismissFrame", "onStopNestedScroll"); }}
这时尝试去滚动RecyclerView
,会发现是滚动不了的,因为ElasticDragDismissFrameLayout
完全消费掉RecyclerView
滚动的距离。
onNestedScroll
接着onNestedScroll
方法
@Override public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) { Log.d("ElasticDragDismissFrame", "onNestedScroll dyConsumed" + dyConsumed + " dyUnconsumed:" + dyUnconsumed); }
@param target The descendent view controlling the nested scroll 触发NestScroll的View(这里是RecyclerView) @param dxConsumed Horizontal scroll distance in pixels already consumed by target @param dyConsumed Vertical scroll distance in pixels already consumed by target y方向RecyclerView消费的像素 @param dxUnconsumed Horizontal scroll distance in pixels not consumed by target @param dyUnconsumed Vertical scroll distance in pixels not consumed by target y方向RecyclerView没有消费的像素
在ElasticDragDismissFrameLayout
没有在onNestedPreScroll
方法中消费移动的距离的情况,onNestedPreScroll
方法的dy
等于onNestedScroll
方法的dyConsumed
。
当RecyclerView
滑动到边缘,不能在滑动了,这时候onNestedPreScroll
的dy
等于滑动的距离,因为这时候RecyclerView
不能再滑动,所以onNestedScroll
的dyConsumed
等于0(RecyclerView
没有消费距离),而dxUnconsumed
等于onNestedPreScroll
的dy
。
拖拽缩放
因为想实现的RecyclerView
滚动到边缘之后,能再拖拽一段距离并伴随这缩放,所以在onNestedScroll
处理dyUnconsumed
(注意这里是dyUnconsumed
)。
@Override public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) { dragScale(dyUnconsumed); }
dragScale
private void dragScale(int scroll) { if (scroll == 0) return; totalDrag += scroll; //跟踪滑动的方向并设置缩放的中心 // // don't double track i.e. if start dragging down and then reverse, keep tracking as // dragging down until they reach the 'natural' position if (scroll < 0 && !draggingUp && !draggingDown) { draggingDown = true; if (shouldScale) setPivotY(getHeight()); } else if (scroll > 0 && !draggingDown && !draggingUp) { draggingUp = true; if (shouldScale) setPivotY(0f); } // how far have we dragged relative to the distance to perform a dismiss // (0–1 where 1 = dismiss distance). Decreasing logarithmically as we approach the limit float dragFraction = (float) Math.log10(1 + (Math.abs(totalDrag) / dragDismissDistance)); // calculate the desired translation given the drag fraction float dragTo = dragFraction * dragDismissDistance * dragElacticity; if (draggingUp) { // as we use the absolute magnitude when calculating the drag fraction, need to // re-apply the drag direction dragTo *= -1; } setTranslationY(dragTo); if (shouldScale) { final float scale = 1 - ((1 - dragDismissScale) * dragFraction); setScaleX(scale); setScaleY(scale); } // if we've reversed direction and gone past the settle point then clear the flags to // allow the list to get the scroll events & reset any transforms if ((draggingDown && totalDrag >= 0) || (draggingUp && totalDrag <= 0)) { totalDrag = dragTo = dragFraction = 0; draggingDown = draggingUp = false; setTranslationY(0f); setScaleX(1f); setScaleY(1f); } dispatchDragCallback(dragFraction, dragTo, Math.min(1f, Math.abs(totalDrag) / dragDismissDistance), totalDrag); }
Demo下载
- Android自定义View--使用NestScrolling机制实现一个上下滑动退出Layout
- 自定义view,实现上下滑动
- Android 自定义View 实现手势监听,左右滑动,上下滑动
- android实现上下滑动
- View的滑动实现之一(使用layout实现)
- 【Android自定义控件】不用ScrollView实现上下两屏滑动
- Android 自定义View 实现垂直滑动页
- 自定义LayoutManager实现android-pile-layout滑动卡片堆叠效果
- Android实现上下滑动效果
- Android实现上下滑动效果
- android layout与view机制
- Android 自定义View:实现View的滑动效果
- Android 自定义View:实现View的滑动效果
- 两个Layout一个属性快速实现Android滑动顶部悬停
- Android笔记---使用ViewFlipper组件实现文本上下滑动效果
- Android自定义View--使用ViewAnimator实现一个提交按钮
- Android学习笔记-使用layout方法使View随手指的滑动而滑动
- Android实现浮层的上下滑动(支持内部添加View)
- 微信推“闪开发票”功能,真的能解决用户痛点?
- 守护者计划配合深圳警方 打响打击侵犯公民个人信息违法犯罪第一枪
- 荣耀9将于6月12日正式发布,会用上UFS 2.1吗?
- 欢迎使用CSDN-markdown编辑器
- 租赁这头猪,不要趴在共享经济的风口上!
- Android自定义View--使用NestScrolling机制实现一个上下滑动退出Layout
- javascript的>>>
- Unity3D Android使用Bugly定位崩溃问题总结
- 国家邮政局凌晨发表声明,顺丰菜鸟之争落幕
- hander机制原理
- 垄断者阿里 逆势挑战者顺丰
- 高通推出Quick Charge 4+:充电更快,发热更少
- 程序员揭秘:火爆朋友圈的左右脑年龄测试结果只是一个随机函数!
- Skype有了重大更新,如果你还关注它的话