View体系——View的滑动

系统提供获取坐标值的方法

View提供的获取坐标值方法:

getTop():View本身顶部到其父布局顶部的距离
getLeft():View本身左边到其父布局左边的距离
getRight():View本身右边到其父布局左边的距离
 getBottom():View本身底部到其父布局顶部的距离 

MotionEvent提供的获取坐标值方法:

getX():点击事件距离View本身左边的距离
getY():点击事件距离View本身顶部的距离
getRawX():点击事件距离屏幕左边的距离
getRawY():点击事件距离屏幕顶部的距离

实现滑动的七种方法

1、layout方法

重写onTouchEvent()方法,通过MotionEvent的getX()、getY()获取当前触摸点的坐标值,在MotionEvent.ACTION_MOVE判断里面计算偏移量并调用layout()方法重新调整View的位置。注意当使用getRawX()、getRawY()方法获取当前触摸点的坐标值时,需要重新设置初始坐标。

View体系——View的滑动_第1张图片
getX()、getY()
View体系——View的滑动_第2张图片
getRawX()、getRawY() 

2、offsetLeftAndRight()和offsetTopAndBottom()

这个方法相当于系统提供的一个对左右、上下移动的API封装,当计算出偏移量后,只需要调用该方法并传入偏移量参数即可,效果与layout()一样。

View体系——View的滑动_第3张图片
offsetLeftAndRight()和offsetTopAndBottom()

3、LayoutParams

使用 getLayoutParams() 方法获取 LayoutParams 时,需要根据 View 所在父布局的类型
来设置不同的类型进行强制转换,如果没有父布局是无法获取 LayoutParams 参数的。或者
可以直接使用 ViewGroup.MarginLayoutParams 直接改变 View 的 Margin 属性。

View体系——View的滑动_第4张图片
LayoutParams

4、scrollTo()、scrollBy()

scrollTo(x, y)表示移动到一个具体的坐标点(x, y);scrollBy(dx, dy)表示移动的增量为dx、dy;
特别注意:
(1)scrollTo()、scrollBy()方法移动的是View的content内容,例如TextView的content就是文本,ImageView的content就是Drawable对象;如果在ViewGroup中使用scrollTo()、scrollBy()方法,那么移动的就是所有的子View。
(2)如果从左往右滑动,那么mScrollX为负值,反之为正值。

View体系——View的滑动_第5张图片
scrollTo()、scrollBy()

5、Scroller类

通过Scroller类可以实现平滑移动的效果,而不再是瞬间完成的移动。

具体使用方法如下:

(1)通过构造方法初始化一个Scroller对象;
(2)重写computeScroll()方法,实现模拟滑动;
(3)调用startScroll()方法开启滑动;

注意点:

(1)View类的computeScroll()方法是使用Scroller类的核心,系统在绘制View的时候会在draw()方法中调用;computeScroll()方法在View中是一个空实现,需要我们自己实现它,一般在该方法里调用scrollTo()实现模拟滑动。但是computeScroll()方法不会自动调用,只能通过invalidate() -> draw() -> computeScroll()来间接调用,所以一般需要在该方法最后添加invalidate()方法进行重绘。
(2)Scroller类的computeScrollOffset()方法用于判断是否完成整个滑动,如果完成了滑动则返回false,否则返回true。
(3)Scroller类提供getCurrX()和getCurrY()方法来获取当前的滑动坐标。
(4)Scroller类的startScroll()方法有两个重载方法,可以设置duration参数来确定滑动时间(默认是250ms);前两个参数表示起始坐标,后两个参数表示偏移量,在获取坐标时通常可以使用getScrollX()和getScrollY()方法来获取父视图中View所滑动到的点的坐标。

Scroller的工作原理:

Scroller本身并不能实现滑动效果,必须结合View的computeScroll()方法才能完成弹性滑动的效果,它不断让View重绘,而每一次重绘距离滑动起始时间会有一个时间间隔(duration),通过这个时间间隔Scroller 就可以得出View当前的滑动位置,知道了滑动位置就可以通过scrollTo() 方法来实现滑动。就这样,View的每一次重绘都会导致View进行小幅度的滑动,而多次小幅度滑动在视觉上就形成了弹性滑动。

Scroller的源码解析:

Scroller类的startScroll()方法并没有调用类似开启滑动的方法,而是保存了传进来的各种参数:startX和startY表示滑动开始的起点,dx和dy表示滑动的距离,duration表示滑动持续的时间。所以 startScroll()方法只是用来做前期准备的,并不能使View进行滑动。关键在于我们在 startScroll()方法后面调用了invalidate()方法,这个方法会导致View重绘,而View的重绘会调用draw()方法,draw()方法又会调用View的computeScroll()方法。

View体系——View的滑动_第6张图片
startScroll()

我们在computeScroll()方法中通过Scroller来获取当前的ScrollX和ScrollY,然后调用scrollTo()方法进行真正的滑动,接着调用invalidate()方法让View进行重绘,而重绘又会调用 computeScroll()方法实现View的滑动,这样通过不断移动一个小的距离并连贯起来就实现了平移滑动的效果。

但是如何获取当前的ScrollX和ScrollY呢?在 computeScrollOffset()方法中,首先会计算动画持续的时间timePassed,如果动画持续的时间小于我们设置的滑动持续时间mDuration,就会进入switch语句,由于startScroll()方法设置的mMode为SCROLL_MODE,所以只会执行分支语句SCROLL_MODE,然后根据插值器Interpolator来计算出在该段时间内的移动距离,并赋值给mCurrX和mCurrY。所以可以直接通过Scroller的getCurrX()和getCurrY()方法获取当前的ScrollX和ScrollY。

View体系——View的滑动_第7张图片
computeScroll()
View体系——View的滑动_第8张图片
computeScrollOffset()

6、属性动画

7、ViewDragHelper

通过ViewDragHelper基本可以实现各种不同的滑动、拖放需求,因此这个方法也是各种滑动解决方案中的终极绝招,但是使用起来比较复杂。Google在其support库中为我们提供了DrawerLayout和SlidingPaneLayout两个布局来帮助开发者实现侧边栏滑动的效果,这两个布局底层就是使用ViewDragHelper来实现的。

你可能感兴趣的:(View体系——View的滑动)