View的滑动--让View动起来

前言

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。




    


时期还可以通过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动起来)