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 ...
随机推荐
- LNMP脚本安装
#!/bin/bash#nginx:nginx-1.8.0.tar.gz#mysql:mysql-5.5.50.tar.gz#php:php-5.5.31.tar.gz#the software pa ...
- shell编写mysql备份工具
如需转载,请经本人同意. 这是之前写的一个备份脚本,调用的备份工具是xtrabackup 编写思路是:每周一全备份,备份后提取lSN号,对备份文件进行压缩,其余时候在LSN的基础上进行增量备份,并对3 ...
- Oracle 11g 修改字符集 为 ZHS16GBK
方法一:sqlplus “用户/密码 as sysdba” sqlplus “sys/sys as sysdba” 方法二:sqlplus/nolog SQL> conn /as sysdba ...
- 【Visual Lisp】人机交互与数据处理(表除外)-lisp
;;本专题所讲述的内容是人机交互以及常规数据处理技术;;★★★01.人机交互★★★;;△△△键盘输入交互△△△(getint "请输入整数");;从键盘输入整数,如果不是整数则重复 ...
- ruby 学习笔记 1
写ruby blog 系统的记录下.也是对我学ruby的点滴记录. 先介绍下我的学习环境.系统:ubuntu12.04文档:techotopia ,ruby文档,the hard way learn ...
- Python 4 —— 函数与模块
函数和模块的使用 一.函数 一个例子说明一切. def hello(): print "hello world" def increment(num): num += 1 retu ...
- Bootstrap 栅格系统
1.简介 Bootstrap内置了一套响应式.移动设备优先的流式栅格系统,随着屏幕设备或视口(viewport)尺寸的增加,系统会自动分为最多12列.它包含了易于使用的预定义classe,还有强大的m ...
- PHP——使用header()函数下载文件
思路:先指明内容的MIME类型,内容描述,内容长度(也即文件大小). 一.下载txt文件的程序: <?phpheader('Content-Type:text/plain');header('C ...
- nginx实时记录请求状态信息( ngx_realtime_request_module)
cd /usr/local/src/ wget "http://nginx.org/download/nginx-1.4.2.tar.gz" tar -xzvf nginx.tar ...
- U8SDK——游戏接入SDK(只接入抽象框架)
上一篇文章我们说了整个U8 SDK抽象成的设计,那这篇文章,我们就来验证一下,他是否如我们期待的那样,简单灵活. 正如之前所说,对于每个游戏,只需要接入抽象层,而每个渠道SDK的接入,就是该抽象层的一 ...