Android Scroll滑动效果精炼详解第(一)课:基础理论和简单实现

一、前期基础知识储备

Scroll英文释义:滑动效果

(1)Scroller类:上官方代码:

This class encapsulates scrolling. You can use scrollers (Scroller or OverScroller) to collect the data you need to produce a scrolling animation—for example, in response to a fling gesture. Scrollers track scroll offsets for you over time, but they don't automatically apply those positions to your view. It's your responsibility to get and apply new coordinates at a rate that will make the scrolling animation look smooth.

由官方文档,我们可以知道,Scroller类是专门用来实现Android滑动效果的。从Android2.X版本开始,出现了常见的长按、点击、滑动操作,为安卓手机带来了更好的用户体验。从Android4.X版本开始,滑动操作就大量的出现了,各种应用也都采用了这种非常好的交互效果,来改善应用程序的体验。

比如某QQ首页与侧页的交互就是采用了这种实现方式。

 

细心的读者也许会发现,这个效果和Android侧滑栏的效果非常相像,我们在来看看某印象笔记的首页与侧滑栏的交互效果。

 

相比之下,我们就会发现,Android Scroll滑动效果与AndroidDrawerLayout侧滑栏的实现方式是有所区别的,前者更像是在同一视图上的操作,右边的视图发生了滑动事件,移动了自己的位置,然后左边的视图进入;而侧滑栏的效果,更像是在同一平面上,两者的关系是覆盖的关系,上面的视图移动出现了,然后对下面的视图进行了覆盖。

(2)滑动效果是如何产生的

滑动一个View,本质上来说就是移动一个View或者说就是改变一个View的坐标。改变其当前所处的位置,它的原理与动画的效果实现非常相似,都是通过不断的地改变View的坐标来实现这一效果的。所以,要实现View的滑动,就必须监听用户触摸的事件,并根据事件传入的坐标,动态且不断的改变View的坐标,从而实现View跟随用户触摸的滑动而滑动。

二、了解Android中坐标系和屏幕的触控事件

(1)Android中的坐标系概念分为两种:①以屏幕左上角为原点的Android坐标系;②以父控件左上角为坐标原点的视图坐标系。

①Android坐标系:坐标原点为屏幕左上角,向左为正,向下为正;

 

②视图坐标系:以父容器控件左上角为坐标原点,向左为正,向下为正;

 

Android中获取坐标值和相对距离的方法

1)View提供的获取坐标方法:getTop() 、getLeft()、 getRight()、 getBottom()

 

2)MotionEvent提供的方法:getX()、getY()、getRawX()、 getRawY()

 

————————————————————我是分隔线——————————————————

(2)屏幕的触控事件——MotionEvent

触控事件在用户交互中,占着举足轻重的地位,学好触控事件是掌握后续自定义View的基础。首先来看看,官方文档中的描述:

Object used to report movement (mouse, pen, finger, trackball) events. Motion events may hold either absolute or relative movements and other data, depending on the type of device.Motion events describe movements in terms of an action code and a set of axis values. The action code specifies the state change that occurred such as a pointer going down or up. The axis values describe the position and other movement properties.

For example, when the user first touches the screen, the system delivers a touch event to the appropriate View with the action code ACTION_DOWN and a set of axis values that include the X and Y coordinates of the touch and information about the pressure, size and orientation of the contact area.

由,官方文档,我们可以知道MotionEvent中封装的一些常用的事件常量,它定义了触控事件的不同类型:

类型

说明

ACTION_DOWN

单点触控按下动作

ACTION_UP

单点触摸离开动作

ACTION_MOVE

触摸点移动动作

ACTION_CANCEL

触摸动作取消

ACTION_OUTSIDE

触摸动作超出边界

ACTION_POINTER_DOWN

多点触摸按下动作

ACTION_POINTER_UP

多点离开动作

通常情况下,我们会在onTouchEvent()方法中通过event.getAction()方法来获取触控事件的类型,并使用switch-case方法来筛选,这个可以作为模板代码

注:每次完整的滑动事件从down开始,up结束,中间有若干个move事件

三、上代码,具体实现滑动

public boolean onTouchEvent(MotionEvent event) {
        //获取到手指处的横坐标和纵坐标
        int x = (int) event.getX();
        int y = (int) event.getY();

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:

                lastX = x;
                lastY = y;

                break;

            case MotionEvent.ACTION_MOVE:

                //计算移动的距离
                int offsetX = x - lastX;
                int offsetY = y - lastY;
                //调用layout方法来重新放置它的位置
//                layout(getLeft()+offsetX, getTop()+offsetY,
//                       getRight()+offsetX , getBottom()+offsetY);

                //对left和right进行偏移
//                offsetLeftAndRight(offsetX);
                //对top和bottom进行偏移
//                offsetTopAndBottom(offsetY);
                //使用LayoutParams
//                LinearLayout.LayoutParams layoutParams= (LinearLayout.LayoutParams) getLayoutParams();
//                layoutParams.leftMargin = getLeft() + offsetX;
//                layoutParams.topMargin = getTop() + offsetY;
//                setLayoutParams(layoutParams);

                //使用MarginLayoutParams
//                ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) getLayoutParams();
//                layoutParams.leftMargin = getLeft() + offsetX;
//                layoutParams.topMargin = getTop() + offsetY;
//                setLayoutParams(layoutParams);
                //使用scrollBy
                ((View)getParent()).scrollBy(-offsetX,-offsetY);
                
                break;
        }

        return true;
    }
	

	

运行效果如图,自定义一个View之后,实现滑动效果,之后手指就可以对其进行一系列的滑动操作。

实际上,实现滑动的方式有很多,笔者这里第一种采用的是Layout方法,在View绘制的时候,会调用onLayout()方法来设置显示的位置。同样,我们可以通过修改View的left、top、right、bottom四个属性来控制View的坐标。

通过Layout的方法实现滑动效果,比较简单,而且代码的意义也很直观,结合之前的坐标系的相关概念,我们知道Layout实现滑动的步骤实际分为两步:①把触摸事件看成非常多段的点击事件,然后记录每个点击事件的坐标;②通过计算两个触摸点击事件之间的位移变化,然后通过偏移量和原始坐标,得到新的坐标。

延伸:使用Scroller类实现滑动:

    /**
     * public void startScroll(int startX, int startY, int dx, int dy, int duration)
     * 滚动,startX, startY为开始滚动的位置,dx,dy为滚动的偏移量, duration为完成滚动的时间
     * @param destX
     * @param destY
     */
    public void smoothScrollTo(float destX,float destY){
        // smoothScrollTo方法只调用一次 那么值也只产生一次
        int scrollX = getScrollX();
        int scrollY = getScrollY();
        int deltaX= (int) (destX-scrollX);
        int deltaY= (int) (destY-scrollY);
        // 2000秒内滑向destX
        mScroller.startScroll(scrollX, scrollY, deltaX, deltaY, 2000);
        //这里必须调用invalidate()才能保证computeScroll()会被调用,否则不一定会刷新界面,看不到滚动效果
        invalidate();
    }

    /**
     * mScroller.getCurrX() //获取mScroller当前水平滚动的位置
     * mScroller.getCurrY() //获取mScroller当前竖直滚动的位置
     * computeScrollOffset()判断是否滑动完成。只能在computeScroll中获取XY,但computeScroll是不会自动调用的。只能通过
     * invalidate()->draw()->computeScroll()来调用。
     * computeScrollOffset() 返回值为boolean,true说明滚动尚未完成,false说明滚动已经完成。
     * 这是一个很重要的方法,通常放在View.computeScroll()中,用来判断是否滚动是否结束。
     */
    @Override
    public void computeScroll() {
        super.computeScroll();
        if(mScroller.computeScrollOffset()){
            // 这里调用ViewGroup的scrollTo()完成实际的滚动
            // 注意该自定义view继承自view,如果直接调用scrollTo方法没有进行强转为viewGroup,则不会发生滑动
            // 如果自定义view继承自viewGroup,则可以直接调用
            ((View) getParent()).scrollTo(mScroller.getCurrX(),mScroller.getCurrY());
            //通过不断的重绘不断的调用computeScroll方法
            invalidate();
        } else {
            Log.d(TAG, "computeScroll: 滚动完成" + mScroller.computeScrollOffset());
        }
    }

Scroller类本身不能实现滑动,需要在自定义View内部重写computeScroll()方法才能执行滑动。这里定义了一个smoothScroll()方法,接收一个位置坐标,然后就可以通过Scroller实现指定时间移动到指定位置。(实际上可直接用属性动画)

本质上,Scroller,scrollBy()方法实现滑动最终都是依靠scrollTo()方法实现,如果在View中使用,则移动view本身;如果在ViewGroup中使用,则使用其所有的子view。

小结:在《Android群英传》中作者分别实现了7种不同的滑动实现方式,其核心思想都是:当触摸View时,系统记下当前触摸点的坐标;当手指移动时,系统记下移动后触摸点坐标,从而获取到相对于前一次坐标点的偏移量,并通过偏移量来计算下一次的View的坐标,这样不断重复,从而实现滑动过程。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值