相对于Android 2.X版本中的长按、点击操作,从Android 4.X开始,滑动操作出现在了Android中
滑动一个View,本质上就是移动一个View,通过不断地改变View的坐标来实现这一效果
屏幕最左上角的顶点作为Android坐标系的原点,从这个点向右是X轴正方向,从这个点向下是Y轴正方向。 系统提供了getLocationOnScreen(int location[])方法来获取Android坐标系中点的位置。另外,在触控事件中使用getRawX()、getRawY()方法所获得的坐标同样是Android坐标系中的坐标。
描述子视图在父视图中的位置关系,以父视图左上角为坐标原点,以原点向右为X轴正方向,以原点向下为Y轴正方向 在触控事件中,通过getX()、getY()所获得的坐标就是视图坐标系中的坐标
MontionEvent中封装的一些常用的事件常量
// 单点触摸按下动作public static final int ACTION_DOWN = 0;// 单点触摸离开动作public static final int ACTION_UP = 1;// 触摸点移动动作public static final int ACTION_MOVE = 2;// 触摸动作取消public static final int ACTION_CANCEL = 3;// 触摸动作超出边界public static final int ACTION_OUTSIDE = 4;// 多点触摸按下动作public static final int ACTION_POINTER_DOWN = 5;// 多点离开动作public static final int ACTION_POINTER_UP = 6;通常在onTouchEvent(MotionEvent event)方法中通过event.getAction()方法来获取触控事件的类型 Android系统提供了非常多的方法来获取坐标值、相对距离等,如图。这些方法可以分成两类: * View提供的获取坐标方法:getTop() getLeft() getRight() getBottom() * MotionEvent提供的方法:getX() getY() getRawX() getRawY() 
基本思想:当触摸View时,系统记下当前触摸点坐标;当手指移动时,系统记下移动后的触摸点坐标,从而获取到相对于前一次坐标点的偏移量,并通过偏移量来修改View的坐标,这样不断重复,从而实现滑动过程。
当MotionEvent.ACTION_DOWN时,记录触摸点坐标;当MotionEvent.ACTION_MOVE时:
或
// 计算偏移量int offsetX = rawX - lastX;int offsetY = rawY - lastY;// 在当前left、top、right、bottom的基础上加上偏移量layout(getLeft() + offsetX, getTop() + offsetY, getRight() + offsetX, getBottom() + offsetY);// 重新设置初始坐标lastX = rawX;lastY = rawY;使用绝对坐标系,在每次执行完ACTION_MOVE的逻辑后,一定要重新设置初始坐标,因为从触摸按下直至触摸离开,触摸点的相对位置不会改变,而绝对位置会一直改变
offsetLeftAndRight()与offsetTopAndBottom()scrollTo与scrollByscrollTo、scrollBy方法移动的是View的content,即让View的内容移动,如果在ViewGroup中使用scrollTo、scrollBy方法,移动的将是所有子View scrollBy方法移动的是屏幕(可视区域),而不是View,如图为scrollBy(20, 10)的效果
因此,如果将scrollBy中的参数dx和dy设置为正数,那么content将向坐标轴负方向移动,如果将scrollBy中的参数dx和dy设置为负数,那么content将向坐标轴正方向移动 在使用绝对坐标时,可以通过使用scrollTo方法来实现这一效果
不管使用scrollTo还是scrollBy方法,子View的平移都是瞬间发生的。Google建议使用自然的过渡动画来实现移动效果。通过Scroller类可以实现平滑移动的效果。 虽然scrollBy方法是让子View瞬间从某点移动到另一个点,但是由于在ACTION_MOVE事件中不断获取手指移动的微小的偏移量,从而在整体上获得一个平滑移动的效果。Scroller类的实现原理与其类似。 使用Scroller类需要如下三个步骤: * 初始化Scroller * 重写computeScroll()方法,实现模拟滑动
computeScroll()方法不会自动调用,只能通过invalidate()→draw()→computeScroll()来间接调用. * startScroll开启模拟过程(此例子为手指移动时scrollBy,手指离开后startScroll)
详见第7章
例:实现类似QQ滑动侧边栏的布局,初始时显示内容界面,当用户手指滑动超过一段距离时,内容界面侧滑显示菜单界面。 * 初始化ViewDragHelper ViewDragHelper通常定义在一个ViewGroup的内部,并通过其静态工厂方法进行初始化
mViewDragHelper = ViewDragHelper.create(this, callback);拦截事件@Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) { return mViewDragHelper.shouldInterceptTouchEvent(ev);}@Overridepublic boolean onTouchEvent(MotionEvent event) { //将触摸事件传递给ViewDragHelper,此操作必不可少 mViewDragHelper.PRocessTouchEvent(event); return true;}处理computeScroll() 使用ViewDragHelper同样需要重写computeScroll()方法,因为ViewDragHelper内部也是通过Scroller来实现平滑移动的@Overridepublic void computeScroll() { if (mViewDragHelper.continueSettling(true)) { ViewCompat.postInvalidateOnAnimation(this); }}处理回调Callbackprivate ViewDragHelper.Callback callback = new ViewDragHelper.Callback() { // 何时开始检测触摸事件 @Override public boolean tryCaptureView(View child, int pointerId) { //如果当前触摸的child是mMainView时开始检测 return mMainView == child; } // 处理垂直滑动 @Override public int clampViewPositionVertical(View child, int top, int dy) { return 0; } // 处理水平滑动 @Override public int clampViewPositionHorizontal(View child, int left, int dx) { return left; }};继续优化:在ViewDragHelper.Callback中,通过重写onViewReleased()方法,可以实现当手指离开屏幕后实现的操作。这个方法内部是通过Scroller类来实现的,这也是前面重写computeScroll()方法的原因
关闭菜单时,将MainView还原到初始状态,即坐标为(0, 0)的点,打开菜单时,则将MainView移动到(300, 0)坐标。
在自定义ViewGroup的onFinishInflate()方法中,定义MenuView和MainView
新闻热点
疑难解答