Android 坐标系和 MotionEvent 分析、滑动
1.Android坐标系
在Android中,屏幕最左上角的顶点作为Android坐标系的原点,这个点向左是X轴正方向,这个点向下是Y轴正方向。
系统提供了getLocationOnScreen(int location[])这样的方法来获得Android坐标系中中点的位置(即该图的左上角在Android坐标系中的坐标)。另外,触控事件中使用getRawX() 、getRawY()方法所获得的坐标同样是Android坐标系中的坐标。
2.视图坐标系
Android除了上面说的坐标系之外,还有一个视图坐标系,它描述了子视图在父视图中的位置关系。这两种坐标系并不矛盾也不复杂,他们的作用是相辅相成的,正方向的指向是相同的,只是原点的位置不再是Android坐标系中的屏幕最左上角,而是以父视图左上角为坐标原点。在触控事件中,通过getX()、getY()坐标就是视图坐标系中的坐标
3.触控事件—MotionEvent
触控事件MotionEvent在用户交互中,占据着举足轻重的地位,学好触控事件是掌握后续内容的基础,首先我们先看看MotionEvent中封装的一些常用的常量,它定义了触控事件的不同类型。
/** 单点触摸按下操作*/
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() 方法来获取触控事件的类型并通过switch-case方法来筛选,这代码的模式基本固定,如下:
@Overrride
public boolean onTouchEvent(MotionEvent event) {
// 获取当前输入点的X,Y坐标(视图坐标)
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;
}
这个代码模板是不涉及多点操作的,基本上可以帮助完成常见的触控操作的监听。
下面图示可以帮助你了解测量时的API如何使用它们,并帮助对MotionEvent和Android坐标系有了一个比较清楚的认识。
4.七种滑动方法
(1).layout方法
简单的来说就是,记录触摸的初始值和偏移量,然后通过layout方法移动控件。
(2).offsetLeftAndRight()与offsetTopAndBottom()
这个是系统提供的一个对上下、左右移动的API的封装。当计算出来偏移量后,需要使用下面的代码就可以重新布局,效果和使用Layout一样,代码如下。
// 同时对left和right进行偏移
offsetLeftAndRight(offsetX);
// 同时对top和bottom进行偏移
offsetTopAndBottom(offsetY);
(3).LayoutParams
LayoutParams保存了一个View的布局参数。我们可以改变LayoutParams来动态修改一个布局的位置参数,从而来达到改变View位置的效果。我们可以很方便的在程序中使用getLayoutParams()来获取一个View的LayoutParams。获取到偏移量后就可以通过setLayoutParams()来改变其LayoutParams。不过,需要注意的是LayoutParams的类型问题,要根据父容器的类型来设置不同的类型的LayoutParams。如果不清楚父类容器是什么,可以使用ViewGroup.MarginLayoutParams。
(4).scrollTo 与 scrollBy
这两个方法是view提供的方法来改变一个View的位置,scrollTo(x,y)表示移动到一个具体的坐标点(x,y),而scrollBy(dx, dy)表示移动的增量为dx,dy。需要注意的是,在View上面执行这两个方法,移动的并不是View本身而是View的内容,如果在ViewGroup中使用scrollTo,scrollBy方法,那么移动的是所有的子View,如果在View中使用,那么移动的将是View的内容,例如TextView,内容就是它的文本,ImageView,内容就是它的drawable对象。
通过上面的分析,我们就应该知道为什么不能在View中使用这两个方法来拖动这个View了。那么我们就要在View所在的ViewGroup中来使用scrollBy方法,移动它的子View,代码如下所示:
((View) getParent.getScrollBy(offsetX, offsetY);
但是因为参考系的问题,在真正操作的时候,如果需要根据拖动来滑动,则需要做的事情就是在计算参数时需要做转负值操作:
int offsetX = x - lastX;
int offsetY = y - lastY;
((View)getParent.scrollBy(-offsetX, -offsetY);
通过查看下面的图就可以了解到为何要做这个处理。
(5).Scroller
Scroller类提供了自然过度动画来实现移动效果,即实现平滑移动的效果,而不再是瞬间完成的移动。下面我们会演示一下如何使用Scroller类实现平滑移动。需求是:子View跟随手指的滑动而滑动,但是在手指离开屏幕时,让子View平滑的移动到初始位置,即屏幕左上角。
使用Scroller类需要如下三个步骤:
- 初始化Scroll
首先要通过它的构造方法来创建一个Scroller对象:
// 初始化Scroller
mScroller = new Scroller(context);
- 重写computeScroll()方法,实现模拟滑动
下面我们需要重写computeScroll(),它是使用Scroller的核心,系统在绘制view的时候回在draw()方法中调用该方法。这个方法实际上就是使用的ScrollTo方法。再结合Scroller对象,帮助获取到当前的滚动值。我们可以通过不断地瞬间移动一个小的距离来实现整体上的平滑移动效果。computeScroll的代码可以利用如下模版代码来实现:
@Override
public void computeScroll() {
super.computeScroll();
// 判断Scroller是否执行完毕
if (mScroller.computeScrollOffset()) {
((View) getParent()).scrollTo(
mScroller.getCurrX(),
mScroller.getCurrY());
// 通过重绘来不断调用computeScroll
invalidate();
}
}
Scroller 类提供了computeScrollOffset()方法来判断是否完成了整个滑动,同时也提供了getCurrX(),getCurrY()方法来获得当前的滑动坐标。在上面的代码中,唯一需要注意的是invalidate()方法,因为只能在computeScroll()方法中获取模拟过程中的scrollX和scrollY坐标,但是computeScroll是不会自动调用的,只能通过invalidate() --》draw()--》computeScroll()来间接调用computeScroll方法,实现循环获取scrollX和scrollY的目的。当模拟过程结束后,scroll.computeScrollOffset()方法会返回fasle,从而中断循环,完成整个平滑移动过程。
- startScroll开启模拟过程
startScroll()方法具有两个重载方法:
public void startScroll(int startX, int startY, int dx, int dy) {
startScroll(startX, startY, dx, dy, DEFAULT_DURATION);
}
public void startScroll(int startX, int startY, int dx, int dy, int duration) {
}
在使用的时候,dx 和 dy的正负和上面scrollTo 与 scrollBy的方法的一样。代码如下:
case MotionEvent.ACTION_UP:
// 手指离开时,执行滑动过程
View viewGroup = ((View) getParent());
mScroller.startScroll(viewGroup.getScrollX(), viewGroup.getScrollY(), -viewGroup.getScrollX(), -viewGroup.getScrollY());
invalidate();
break;
6.属性动画
这个目前不做过多的介绍。
7.ViewDragHelper
这个功能很强大,可以先看一下QQ的侧边栏滑动,然后再总结这块的内容。
Android 坐标系和 MotionEvent 分析、滑动的更多相关文章
- Android Scroll分析——滑动效果产生
相对于在Android2.x版本上出现的长按.点击事件的效果,不得不说,滑动操作具有更好的用户体验.因此,从Android 4.X版本开始,出现了更多滑动操作的效果.越来越多第三方应用模仿这样的效果, ...
- android中实现view可以滑动的六种方法
在android开发中,经常会遇到一个view需要它能够支持滑动的需求.今天就来总结实现其滑动的六种方法.其实每一种方法的 思路都是一样的,即:监听手势触摸的坐标来实现view坐标的变化,从而实现vi ...
- ViewPager源码分析——滑动切换页面处理过程
上周客户反馈Contacts快速滑动界面切换tab有明显卡顿,让优化. 自己验证又没发现卡顿现象,但总得给客户一个技术性的回复,于是看了一下ViewPager源码中处理滑动切换tab的过程. View ...
- Android UI效果实现——Activity滑动退出效果
更新说明: 1.在QQ网友北京-旭的提醒下,在SlideFrame的initilize方法中添加了focusable.focusableInTouch.clickable的状态设置,否则会导致部分情况 ...
- Android使用ViewFlipper实现左右滑动效果面
在我的博客中,上次是使用ViewPager实现左右滑动的效果的,请看文章:Android使用ViewPager实现左右滑动效果. 这次我来使用ViewFlipper实现这种效果,好了,先看看效果吧: ...
- android wifi ANR问题分析总结
android wifi ANR问题分析总结 1 看看main进程阻塞在那里? 2 调用关系的函数阻塞在那里? 3 最终阻塞函数的阻塞前的log以及状态
- 从Android系统出发,分析Android控件构架
从Android系统出发,分析Android控件构架 Android中所有的控件追溯到根源,就是View 和ViewGroup,相信这个大家都知道,但是大家也许会不太清楚它们之间的具体关系是什么,在A ...
- [转] Android实时抓包分析 : 善用adb调试桥
Android实时抓包分析 : 善用adb调试桥 谈到android网络抓包,很多人都能想到牛逼轰轰的神器tcpdump.方法就是在android机器上面安装tcpdump,然后通过-w参数把抓包 ...
- [置顶] Android开发之serviceManager分析
Android 开发之serviceManager分析 在Android系统中用到最多的通信机制就是Binder,Binder主要由Client.Server.ServiceManager和Binde ...
随机推荐
- PHP 定界符使用
在PHP代码中,如果不想一行一行的拼接HTML或者JS的话,那么使用定界符将是最好的帮手! 使用方法: <<<eof .......html/js..... eof; 注意事项:(别 ...
- log4j写入数据库
转发自http://www.cnblogs.com/adolfmc/p/3432720.html Log4j 配置数据库连接池(将日志信息保存到数据库) org.apache.log4j.jdbc.J ...
- MFC程序中使用调试宏ASSERT()、ASSERT_VALID()、VERIFY()和TRACE()的区别
其实这篇文章说的很明白了:http://dev.gameres.com/Program/Other/DebugMacro.htm 结论如下: 1.ASSERT()测试它的参数,若参数为0,则中断执行并 ...
- mysql用户权限设置
1.创建新用户 通过root用户登录之后创建 >> grant all privileges on *.* to testuser@localhost identified by &quo ...
- ASP.Net的导出Excel的快速方法,DataTable导出Excel(亲测,非原创)
//使用方法 ExcelHelper.dataTableToCsv(dt,@"D:\1212.xls");System.Diagnostics.Process.Start(@&qu ...
- 对bit、byte、TByte、Char、string、进制的认识
在学校老师就教1byte = 8bit,一个Byte在内存中占8个房间.每个房间都有门牌号.找到内存中的内容就找门牌号,寻址什么的,虽然在听,但是脑袋里一头雾水,到现在只知道会用就行,但原理也不是那么 ...
- 深入探究 WinRT 和 await
在最近发布的使用 Windows 运行时中异步性来始终保持应用程序能够快速流畅地运行这篇博文中,包含了一些如何在 C# 和 Visual Basic 中使用 await 关键字的示例,允许开发人员在使 ...
- MyEclipse调用Matlab打包函数
本文部分内容参考了http://www.360doc.com/content/15/1103/16/1180274_510463048.shtml 一.检查Java环境 对于已经装上JAVA环境的计算 ...
- NYOJ 536 开心的mdd(DP)
开心的mdd 时间限制:1000 ms | 内存限制:65535 KB 难度:3 描述 himdd有一天闲着无聊,随手拿了一本书,随手翻到一页,上面描述了一个神奇的问题,貌似是一个和矩阵有关的 ...
- zoj 3725 - Painting Storages(动归)
题目要求找到至少存在m个连续被染成红色的情况,相对应的,我们求至多有m-1个连续的被染成红色的情况数目,然后用总的数目将其减去是更容易的做法. 用dp来找满足条件的情况数目,, 状态:dp[i][0] ...