View的滑动--让View动起来

作者: Jackson杰 | 来源:发表于2017-11-18 21:21 被阅读0次
    前言

    View是Android中所有UI的基础,在Android开发中,我们不可避免地要面临和View的交互问题。这里我们主要介绍View的滑动操作。从Android 4.X开始,滑动操作就大量出现在Android中,本篇文章就介绍如何在应用中添加滑动效果。滑动View的思路是:当触摸事件传递到View的时候,记下起始坐标,当滑动View时,记下滑动后的坐标,通过计算出偏移量,设置到View上。

    实现View滑动有很多种方法,这篇文章主要讲解六种滑动的方法,分别是:layout()、offsetLeftAndRight()与offsetTopAndBottom()、LayoutParams、动画、scollTo与scollBy和Scroller;

    Android中的坐标体系

    在介绍View滑动之前,请先看前篇关于Android坐标体系的介绍 http://www.jianshu.com/p/6c0539cf0416

    触控事件--MotionEvent

    MotionEvent在触控事件中占有很重要的位置,首先来看一下MotionEvent里面定义的常量,全部是和滑动触摸有关的常量。

        public static final int ACTION_BUTTON_PRESS = 11;
        public static final int ACTION_BUTTON_RELEASE = 12;
        public static final int ACTION_CANCEL = 3;
        public static final int ACTION_DOWN = 0;
        public static final int ACTION_HOVER_ENTER = 9;
        public static final int ACTION_HOVER_EXIT = 10;
        public static final int ACTION_HOVER_MOVE = 7;
        public static final int ACTION_MASK = 255;
        public static final int ACTION_MOVE = 2;
        public static final int ACTION_OUTSIDE = 4;
        public static final int ACTION_UP = 1;
        ......
    

    通常情况下,我们处理触摸事件的逻辑是,重写onTouchEvent()方法,在此方法里通过对触摸的手势事件进行判断,做出相应的处理。一般的代码如下:

     @Override
        public boolean onTouchEvent(MotionEvent event) {
            //获取触摸点的坐标
            int x= (int) event.getX();
            int y= (int) event.getY();
            switch (event.getAction()){
                case MotionEvent.ACTION_DOWN: //处理按下事件
    
                    break;
                case MotionEvent.ACTION_MOVE: //处理滑动事件
    
                    break;
                case MotionEvent.ACTION_UP:  //处理抬起事件
    
                    break;
            }
            return true;
        }
    
    layout()方法

    我们知道,在自定义ViewGroup时,会调用onLayout()方法来确定每个子View的位置,每个子View布局的确定是调用子View的layout()方法,来确定子View的位置。

    public class DragView extends View {
    
        //记录上次触摸的坐标
        private int lastX;
        private int lastY;
    
        public DragView(Context context) {
            super(context);
        }
    
        public DragView(Context context, @Nullable AttributeSet attrs) {
            super(context, attrs);
        }
    
        public DragView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
        }
    
        @Override
        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(getLeft()+offsetX,getTop()+offsetY,getRight()+offsetX,getBottom()+offsetY);
                    break;
                case MotionEvent.ACTION_UP:  //处理抬起事件
    
                    break;
            }
            return true;
        }
    }
    
    offsetLeftAndRight()与offsetTopAndBottom()

    和layout()方法差不多,只不过这两个方法分别设置左右和上下偏移量的。
    只需要修改MotionEvent.ACTION_MOVE里的代码

     case MotionEvent.ACTION_MOVE: //处理滑动事件
                    int offsetX = x - lastX;
                    int offsetY = y - lastY;
                    offsetLeftAndRight(offsetX);
                    offsetTopAndBottom(offsetY);
                    break;
    

    效果是一样的。

    LayoutParams

    LayoutParams封装了Layout的位置,宽,高等信息,通过设置LayoutParams来改变View的参数,达到改变View的位置的目的。
    修改MotionEvent.ACTION_MOVE代码如下:

    case MotionEvent.ACTION_MOVE: //处理滑动事件
                    int offsetX = x - lastX;
                    int offsetY = y - lastY; 
                    LinearLayout.LayoutParams layoutParams= (LinearLayout.LayoutParams) getLayoutParams();
                    layoutParams.leftMargin=getLeft()+offsetX;
                    layoutParams.topMargin=getTop()+offsetY;
                    setLayoutParams(layoutParams);
                    break;
    

    在这里,因为自定义DragView的父布局是LinearLayout,所以这里用的是LinearLayout.LayoutParams,如果布局变成RelativeLayout,则就要通过RelativeLayout.LayoutParams来获取layoutParams。

    <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context="com.jackson.scrollview.activity.DragActivity">
    
    
        <com.jackson.scrollview.view.DragView
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:background="@color/colorAccent"/>
    
    </LinearLayout>
    

    时期还可以通过ViewGroup.MarginLayoutParams来实现。

     ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) getLayoutParams();
                    layoutParams.leftMargin = getLeft() + offsetX;
                    layoutParams.topMargin = getTop() + offsetY;
                    setLayoutParams(layoutParams);
    
    scollTo与scollBy

    View内部提供了scollTo与scollBy来改变View的位置,两者的区别也类似于To和By的区别,scollTo(x,y)表示将View移动到具体的坐标(x,y),scollBy(dx,dy)表示移动的增量是dx,dy。需要注意的有两点:

    • scollTo与scollBy移动的是View的Cotent,既View里面的内容,所以我们要想使用正确,必须要到View的父布局ViewGroup中使用。
    • 必须将偏移量设为负值,才能正确地滑动
      既滑动的代码如下:
      ((View)getParent()).scrollBy(-offsetX,-offsetY);
      修改MotionEvent.ACTION_MOVE代码如下:
     case MotionEvent.ACTION_MOVE: //处理滑动事件
                    int offsetX = x - lastX;
                    int offsetY = y - lastY;
                    ((View) getParent()).scrollBy(-offsetX, -offsetY);
                    break;
    
    Scroller

    使用scollTo/scollBy方法来进行滑动时,这个过程是瞬间完成的,用户的体验不好,因此我们使用Scroller实现平滑的过渡效果。Scroller是Android中专门处理滑动效果的工具类。
    总体说来,使用scollTo/scollBy实现的效果是移动,而使用Scroller实现的效果是滚动。
    Scroller的使用主要分为以下几个步骤:

    1. 创建并初始化Scroller,通过Scroller的构造方法来创建Scroller实例。
      mScroller=new Scroller(getContext());
    2. 重写computeScroll()方法,并在其内部完成平滑滚动的逻辑
     @Override
        public void computeScroll() {
            super.computeScroll();
            //判断Scroller是否执行完毕
            if (mScroller.computeScrollOffset()){
                ((View)getParent()).scrollTo(mScroller.getCurrX(),mScroller.getCurrY());
                //通过重绘不断调用computeScroll()
                invalidate();
            }
        }
    
    1. 调用startScroll()方法来初始化滚动数据并刷新界面
    public void smoothScrollTo(int x,int y) {
            int dx = x - mScroller.getFinalX();
            int dy = y - mScroller.getFinalY();
            mScroller.startScroll(mScroller.getFinalX(),mScroller.getFinalY(),dx,dy,3000);
            invalidate();
        }
    

    源码下载 https://github.com/baojie0327/ScrollView

    相关文章

      网友评论

        本文标题:View的滑动--让View动起来

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