Android中View的滑动
16lz
2021-01-24
实现滑动效果的方式
在Android中有三种方式可以实现View的滑动效果: 1、通过View本身提供的 scrollTo(intx,inty)或者scrollBy(intx,inty)方法来实现View中内容滑动的效果。(内容滑动) 2、通过动画(最好使用属性动画)给View施加平移效果来实现滑动。(View滑动) 3、通过改变View的LayoutParams使得View重新布局来实现滑动效果。
三者对比: 1、scrollTo/scrollBy:操作简单适合对View内容的滑动。 2、动画:操作简单,主要适合没有交互的View和实现复杂的动画效果。 3、改变布局参数:操作稍微复杂,适用于有交互的View。
scroll滑动原理
在此处主要分析scroll方式的滑动。
在源码中,该方法是将View的内容移动到指定位置,可以看出每次 scrollTo(int x,int y)时,都会进行if (mScrollX!= x || mScrollY!= y)判断,检查目的点的坐标是否和偏移量一样,因为scrollTo()是移动到指定的点,如果这次移动的点的坐标和上次偏移量一样,也就是说这次移动和上次移动的坐标是同一个,那么就没有必要进行移动了。 效果图:
上面几幅图就是,通过scrollTo进行上下左右移动后的效果,如果仔细看就会发现为什么当参数的正负与坐标系的正负相反呢?等会儿介绍方向问题。
从源码中可以看出, scrollBy(int x,int y)的本质与 scrollTo(int x,int y)是一致的, scrollTo(int x,int y)是从当前偏移量移动到指定点,而 scrollBy(int x,int y)是在当前偏移量的基础上根据参数提供的偏移量移动,所以 在参数不变的情况下,scrollTo(int x,int y)只能移动一次,而 scrollBy(int x,int y)可以一直地移动下去。
弹性滑动 由于scrollTo(int x,int y)滑动时,会瞬间移动,实际的体验效果不好,所以我们要实现渐进式滑动。渐进式滑动的思想是:将一次大的滑动分成若干次小的滑动,并在一个时间段内完成。 能够实现弹性滑动的方式很多,如通过Scroller、动画、(Handler+postDelay)或(Thread+sleep)等。 在此处使用Scroller实现。 首先,介绍Scroller的几个常用方法:
源码中验证了,上述结论。使用Scroller进行滑动时,在startScroll(intstartX,intstartY,intdx,intdy,intduration),方法中只是进行了初始化。
Scroller实现弹性滑动的流程: 首先是View通过Scroller的startScroll(intstartX,intstartY,intdx,intdy,intduration)方法进行初始化。 然后,会调用View的 invalidate()或postInvalidate()进行重绘。 接着,绘制View的时候会触发computeScroll()方法,接着重写computeScroll(),在computeScroll()里面先调用Scroller的computeScrollOffset()方法来判断滚动是否结束,如果滚动没有结束就调用scrollTo()方法来进行滚动。 最后,scrollTo()方法虽然会重新绘制View,但是还是要手动调用下invalidate()或者postInvalidate()来触发界面重绘,重新绘制View又触发computeScroll(),所以就进入一个递归循环阶段,这样就实现在某个时间段里面滚动某段距离的一个平滑的滚动效果。
在Android中有三种方式可以实现View的滑动效果: 1、通过View本身提供的 scrollTo(intx,inty)或者scrollBy(intx,inty)方法来实现View中内容滑动的效果。(内容滑动) 2、通过动画(最好使用属性动画)给View施加平移效果来实现滑动。(View滑动) 3、通过改变View的LayoutParams使得View重新布局来实现滑动效果。
三者对比: 1、scrollTo/scrollBy:操作简单适合对View内容的滑动。 2、动画:操作简单,主要适合没有交互的View和实现复杂的动画效果。 3、改变布局参数:操作稍微复杂,适用于有交互的View。
scroll滑动原理
在此处主要分析scroll方式的滑动。
- 首先,弄清楚两个变量的概念:mScrollX和mScrollY
<span style="color:#009900;">/** * The offset, in pixels, by which the content of this view is scrolled * horizontally. * {@hide} */ @ViewDebug.ExportedProperty(category = "scrolling")</span> protected int mScrollX; <span style="color:#009900;">/** * The offset, in pixels, by which the content of this view is scrolled * vertically. * {@hide} */ @ViewDebug.ExportedProperty(category = "scrolling")</span> protected int mScrollY;
mScrollX和mScrollY的变化规律:
(1).mScrollX的值总是等于View左边缘和View内容左边缘在水平方向的距离(mScrollX=X1-X2,,其中X1,表示View的左边缘,其中X2,表示View内容的左边缘),当View内容的左边缘位于View的左边缘的左边时,mScrollX大于零,即mScrollX为正值,反之为负值;
(2).mScrollY的值总是等于View上边缘和View内容上边缘在竖直方向的距离(mScrollY=Y1-Y2,,其中Y1,表示View的上边缘,其中Y2,表示View内容的上边缘),当View内容的上边缘位于View的上边缘的上边时,mScrollY大于零,即mScrollY为正值,反之为负值;
默认情况下,mScrollX和mScrollY都等于0(我的理解是在刚进入程序,scroll之前mScrollX=0和mScrollY=0)。
- scrollTo(int x,int y)的源码:
<span style="color:#009900;">/** * Set the scrolled position of your view. This will cause a call to * {@link #onScrollChanged(int, int, int, int)} and the view will be * invalidated. * @param x the x position to scroll to * @param y the y position to scroll to */</span> public void scrollTo(int x, int y) { if (mScrollX != x || mScrollY != y) { int oldX = mScrollX; int oldY = mScrollY; mScrollX = x; mScrollY = y; invalidateParentCaches(); onScrollChanged(mScrollX, mScrollY, oldX, oldY); if (!awakenScrollBars()) { postInvalidateOnAnimation(); } } }
在源码中,该方法是将View的内容移动到指定位置,可以看出每次 scrollTo(int x,int y)时,都会进行if (mScrollX!= x || mScrollY!= y)判断,检查目的点的坐标是否和偏移量一样,因为scrollTo()是移动到指定的点,如果这次移动的点的坐标和上次偏移量一样,也就是说这次移动和上次移动的坐标是同一个,那么就没有必要进行移动了。 效果图:
上面几幅图就是,通过scrollTo进行上下左右移动后的效果,如果仔细看就会发现为什么当参数的正负与坐标系的正负相反呢?等会儿介绍方向问题。
- scrollBy(int x,int y)的源码
/** * Move the scrolled position of your view. This will cause a call to * {@link #onScrollChanged(int, int, int, int)} and the view will be * invalidated. * @param x the amount of pixels to scroll by horizontally * @param y the amount of pixels to scroll by vertically */ public void scrollBy(int x, int y) { scrollTo(mScrollX + x, mScrollY + y); }
从源码中可以看出, scrollBy(int x,int y)的本质与 scrollTo(int x,int y)是一致的, scrollTo(int x,int y)是从当前偏移量移动到指定点,而 scrollBy(int x,int y)是在当前偏移量的基础上根据参数提供的偏移量移动,所以 在参数不变的情况下,scrollTo(int x,int y)只能移动一次,而 scrollBy(int x,int y)可以一直地移动下去。
- scrollTo(int x,int y)移动的方向
弹性滑动 由于scrollTo(int x,int y)滑动时,会瞬间移动,实际的体验效果不好,所以我们要实现渐进式滑动。渐进式滑动的思想是:将一次大的滑动分成若干次小的滑动,并在一个时间段内完成。 能够实现弹性滑动的方式很多,如通过Scroller、动画、(Handler+postDelay)或(Thread+sleep)等。 在此处使用Scroller实现。 首先,介绍Scroller的几个常用方法:
- startScroll(intstartX,intstartY,intdx,intdy,intduration)
/** * Start scrolling by providing a starting point, the distance to travel, * and the duration of the scroll. * * @param startX Starting horizontal scroll offset in pixels. Positive * numbers will scroll the content to the left. * @param startY Starting vertical scroll offset in pixels. Positive numbers * will scroll the content up. * @param dx Horizontal distance to travel. Positive numbers will scroll the * content to the left. * @param dy Vertical distance to travel. Positive numbers will scroll the * content up. * @param duration Duration of the scroll in milliseconds. */ public void startScroll(int startX, int startY, int dx, int dy, int duration) { mMode = SCROLL_MODE; mFinished = false; mDuration = duration; mStartTime = AnimationUtils.currentAnimationTimeMillis(); mStartX = startX; mStartY = startY; mFinalX = startX + dx; mFinalY = startY + dy; mDeltaX = dx; mDeltaY = dy; mDurationReciprocal = 1.0f / (float) mDuration; }
源码中验证了,上述结论。使用Scroller进行滑动时,在startScroll(intstartX,intstartY,intdx,intdy,intduration),方法中只是进行了初始化。
- computeScrollOffset()
Scroller实现弹性滑动的流程: 首先是View通过Scroller的startScroll(intstartX,intstartY,intdx,intdy,intduration)方法进行初始化。 然后,会调用View的 invalidate()或postInvalidate()进行重绘。 接着,绘制View的时候会触发computeScroll()方法,接着重写computeScroll(),在computeScroll()里面先调用Scroller的computeScrollOffset()方法来判断滚动是否结束,如果滚动没有结束就调用scrollTo()方法来进行滚动。 最后,scrollTo()方法虽然会重新绘制View,但是还是要手动调用下invalidate()或者postInvalidate()来触发界面重绘,重新绘制View又触发computeScroll(),所以就进入一个递归循环阶段,这样就实现在某个时间段里面滚动某段距离的一个平滑的滚动效果。
更多相关文章
- Service和Activity通讯的3种常用方式示例
- 【Android】高效ListView
- Android(安卓)架构组件(一)——Lifecycle
- Android(安卓)调用系统相机拍照保存以及调用系统相册的方法
- 学习Android(安卓)--从现在开始
- Android(安卓)studio gradle build 太慢,有时会卡住的解决方法
- Android(安卓)TV Input Framework(TIF)--显示Tv Input
- 使用Lint 和 Annotations来提升代码质量
- Android(安卓)databinding(详解三)--自定义属性使用