Android Scroll滑动

一、为什么要学会Scroll

我们在写自定义View的时候,避免不了要让用户能够和这个View进行交互,而因为Android以触摸作为主要的操作手段,那么明白如何去自作一个可滑动的的View就非常有必要了。
例如ListView、ViewPager等官方提供的View均实现了这以交互方式。


二、坐标

要制作一个可滑动的View,我们首先要明白Android系统中的坐标系是怎样的一个定义。

1、Android坐标系(绝对坐标)

Android的坐标系分为两种,其中一种叫做Android坐标系,或者称为绝对坐标可能更为合适。它以手机屏幕的左上角的顶点作为坐标系的原点。该点向右是X轴,向下既是Y轴。在onTouchEvent事件中可以通过getRawX()和getRawY()来分别获得触发该事件的View相对于绝对坐标系的X轴和Y轴坐标。

2、视图坐标系(相对坐标)

Android坐标系的另一种叫视图坐标系,它的坐标原点永远是直接父节点(parentView)的左上角。所以也可以称它为相对坐标。onTouchEvent中可通过getX()和getY()分别获得X轴和Y轴坐标。


三、获取坐标

获取坐标的方法可以分为两类:

1、View提供的获取坐标的方法。

getTop():获取到的是View自身的顶边到父节点顶边的距离。
getBootom():获取到的是View自身的底边到父节点顶边的距离。
getLeft():获取到的是View自身的左边到父节点左边的距离。
getRight():获取到的是View自身的右边到父节点左边的距离。

2、MotionEvent提供的方法(既触摸事件中)

getX():获取到的是触摸点相对父节点的X轴相对坐标。
getY():获取到的是触摸点相对父节点的Y轴相对坐标。
getRawX():获取到的是触摸点相对屏幕的X轴绝对坐标。
getRawY():获取到的是触摸点相对屏幕的Y轴绝对坐标。


四、实现滑动的几种方法

1、layout方法

调用layout方法去重新布局这个View。layout的各个参数为当前View的left、top、right、bottom加上我们收支滑动的偏移量。下列代码是一个View跟随手指移动的例子:

    @Override  
    public boolean onTouchEvent(MotionEvent event) {
    int x = (int) event.getX();
    int y = (int) event.getY();
    switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            downX = x;
            downY = y;
            break;
        case MotionEvent.ACTION_MOVE:
            int offsetX = x - downX;
            int offsetY = y - downY;
            layout(getLeft() + offsetX, getTop() + offsetY, getRight() + offsetX, getBottom() + offsetY);
            break;
        case MotionEvent.ACTION_UP:
            break;
    }

    return true;
}  

2、使用offsetLeftAndRight与offsetTopAndBottom两个方法

这两个方法为系统提供的一个专门用来移动View的Api。只需要将计算出的偏移量作为参数即可。所以使用这两个方法可将上面的例子改为如下:

@Override
public boolean onTouchEvent(MotionEvent event) {
    int x = (int) event.getX();
    int y = (int) event.getY();
    switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            downX = x;
            downY = y;
            break;
        case MotionEvent.ACTION_MOVE:
            int offsetX = x - downX;
            int offsetY = y - downY;
         offsetLeftAndRight(offsetX);
         offsetTopAndBottom(offsetY);
            break;
        case MotionEvent.ACTION_UP:
            break;
    }

    return true;
}  

3、LayoutParams(偏方,不建议使用,不够精确)

我们知道LayoutParams里面封装了一个View的布局参数,而里面刚好有包含margin值。所以我们可以通过修改margin来达到移动的目的。但是移动的效果并不好,很不精确。代买如下:

   @Override
public boolean onTouchEvent(MotionEvent event) {
    int x = (int) event.getX();
    int y = (int) event.getY();
    switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            downX = x;
            downY = y;
            break;
        case MotionEvent.ACTION_MOVE:
            int offsetX = x - downX;
            int offsetY = y - downY;
         
            ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) getLayoutParams();
            layoutParams.leftMargin = getLeft() + offsetX;
            layoutParams.topMargin = getTop() + offsetY;
            setLayoutParams(layoutParams);
            break;
        case MotionEvent.ACTION_UP:
            break;
    }

    return true;
}  

4、scrollTo和scrollBy

这种呢,和前面的都不太相同。首先我们要明白,这两个方法的区别,以及他们所移动的到底是什么东西。

(1)两个方法间的区别

首先scrollTo(x,y),这个方法呢,两个参数分别为准确的x、y轴坐标,你给它什么坐标它就移动到哪。

而scrollBy(dx,dy)呢,看参数名就知道是距离,也就是说它会在原来的x、y的基础值上去增量移动。打个比方,一个View受到scrollTo的作用移动到了(100,100)的一个点。之后再次被scrollBy作用增量移动(20,20)。那么此时这个View处在一个什么位置呢?答案是(120,120),因为实在(100,100)这个原值上增量移动了(20,20)。

以上就是这两个方法之间的区别

(2)这两个方法所移动的对象是什么。

前面有提到,这种移动方式和前面的几个都不相同,前面几个都是直接移动的View本身;
而这两个方法所移动实际上是View的显示区域。

打个比方:有一个望远镜,你可以通过望远镜看到这个世界,但是世界是很大很大的,望远镜只能让你看到这有限一小部分,如果你想看右边点的,就要把望远镜往右移动,想看上边点的,就往上移。其他方向同理。
那么这个望远镜的可视区域就是我们的View或者ViewGroup的可视区域。
而世界呢,就是View或ViewGroup背后所藏着的那一整个显示内容,非常非常大,只是你要去移动我们的可视区域才能够看到。

举个栗子:
我有一个ViewGroup,这个ViewGroup里有一个TextView。我调用了这个ViewGroup的scrollBy方法移动了(100,0)。但是呢这时并不是ViewGroup移动了,而是这个ViewGroup所显示出来的区域被移动了。所以TextView应该是到了更左边的距离去了.
那同理,我去调用了TextView的scrollBy方法移动(-20,0)。这时会是谁移动了呢?是TextView中的显示区域被移动了(-20,0)。所以文字应该是到了更右边的位置去了。

(3)他们的移动方向。

scrollTo和scrollBy的移动方向我想大概是学习scroll最容易模糊的一个部分了,最初即便我在网上看了各种相关描述的图之后我也是有迷迷糊糊。
实际上首先呢,你要记住前面所说的scrollTo和scrollBy这两个方法所移动的东西实际上是View的显示区域,而不是当前View本身。

举个栗子:
我又有一个ViewGroup,里面有一个写着helloword的TextView - - ,而这个ViewGroup会根据手指移动的增量去调用scrollBy。我的手指向右移动了30,此时TextView会显示在什么位置呢?
我们分析一下,手指向右移动了30,移动的是显示区域,那么此时显示区域应该是在(30,0)的位置,我们的TextView自然是往左偏了30啦。

ok!那如果手指向下移动30呢?那么此时显示区域向下移动30,内容当然是向上偏了30啦。

(4)实现移动。

下面就用scrollBy去实现跟随手指移动的效果。实际上知道了前面的原理之后,我们就知道了,要让它跟随手指移动只要将偏移量改为负数即可。如下:

@Override
public boolean onTouchEvent(MotionEvent event) {
    int x = (int) event.getX();
    int y = (int) event.getY();
    switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            downX = x;
            downY = y;
            break;
        case MotionEvent.ACTION_MOVE:
            int offsetX = x - downX;
            int offsetY = y - downY; 
         
            ((View) getParent()).scrollBy(-offsetX, -offsetY);
            break;
        case MotionEvent.ACTION_UP:
            break;
    }

    return true;
}

你可能感兴趣的:(Android Scroll滑动)