Android Scroll滑动

作者: 逝水比喻时光 | 来源:发表于2016-04-27 17:15 被阅读228次

    一、为什么要学会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滑动

        本文链接:https://www.haomeiwen.com/subject/qcvcrttx.html