本节主要介绍View的一些基础知识,主要介绍
View的位置参数
、
MotionEvent
和
TouchSlop
对象、
VelocityTracker
、
GestureDetector
、对象。
由于篇幅原因对Scroller的讲解在另一篇(点击进入)
1.什么是View
View是Android中所有控件的基类
,View是一种界面层的控件的一种抽象,它代表一个控件,除了View还有ViewGroup,ViewGroup也继承了View。这就意味着View本身就可以是单个控件也可以是由多个控件组成的一组控件
,通过这种关系就形成了View关系树的结构。
2.View的位置参数
View的位置坐标是相对于View的父容器来说的,是一种相对坐标
View的位置主要有四个顶点来决定,分别对应于View的四个属性:top、left、right、bottom
- top:是左上角纵坐标,通过getTop() 获取参数
- left:是左上角横坐标 , 通过getLeft() 获取参数
- right:是右下角横坐标, 通过getRight() 获取参数
bottom:是右下角纵坐标, 通过getRightBottom() 获取参数
根据上图,我们很容易得出View的宽高和坐标的关系
width=getRight()-getLeft();
height=getBottom()-getTop();
从Android3.0开始,View增加了额外的几个参数:x
、y
、translationX
、translationY
。而这几个参数都是相对于父容器的坐标 ,它们的意义及关系如下
- x、y是View左上角的位置
- translationX、translationY是View左上角相对于父容器的偏移量
x = getLeft() + getTranslationX();
y = getTop + getTranslationY();
3.MotionEvent 和 TouchSlop
1.MotionEvent
在手指接触屏幕后所产生大的一系列事件中,典型的事件类型有如下几种:
- ACTION_DOWN —— 手指刚接触屏幕
- ACTION_MOVE —— 手指在屏幕上移动
- ACTION_UP —— 手指在屏幕上松开的一瞬间
正常情况下,一次手指触摸屏幕的行为会触发一系列的点击事件,考虑如下几种情况
- 点击屏幕后离开,事件序列为 DOWN -> UP;
- 点击屏幕滑动一会再松开,事件序列为DOWN -> MOVE ….. MOVE -> UP。
通过MotionEvent 对象我们可以得到点击事件发生的x 和 y坐标。为此系统提供了两组方法:
- getX(),getY():返回的是相对于当前View左上角的 x 和 y坐标。
- getRawX 、getRawY 返回的是相对于手机屏幕左上角的x 和 y坐标
2.TouchSlop
TouchSlop 是系统所能识别出的被认为是滑动的最小距离,换句话说,当手指在屏幕上滑动时,如果两次滑动之间的距离小于这个常量,那么系统就不认为你是在进行滑动操作。和设备有关,不同设备值不一样。
通过 ViewConfiguration.get(getContent()).getScaledTouchSlop() 来获取这个常量
意义
:可以用这个常量来做一些滑动过滤,比如说当两次滑动事件的距离小于这个值,我们认为未达到滑动距离的临界值,因此就可以认为他们不是滑动的,这样做可以有更好的用户体验
4.VelocityTracker(速度追踪)
定义:用于追踪手指在滑动过程中的速度,首先在View的onTouchEvent方法中追踪当前单击事件的速度
VelocityTracker velocityTracker=VelocityTracker.obtain();
velocityTracker.addMovement(event);
接着当我们想知道当前的滑动速度时,这个时候可以采用如下方式获得当前速度,这个速度包括水平和竖直方向的速度。
velocityTracker.computeCurrentVelocity(1000);
int xVelocity= (int) velocityTracker.getXVelocity();
int yVelocity= (int) velocityTracker.getYVelocity();
在这一步中有两点需要注意
- 获取速度之前必须先计算速度,即getXVelocity() 和 getYVelocity()之前必须调用computeCurrentVelocity 方法;
- 这里的速度是指一段时间内手指滑过的像素数,比如将时间间隔设为1000ms时,在1s内,手指从左向右滑过100像素,那么水平速度就是100。computeCurrentVelocity 方法的参数表示的是一个时间单元或者说时间间隔,计算速度时得到的速度就是在这个时间间隔内手指在水平或竖直方向上所滑动的像素数
速度=(终点位置 - 起点位置)/时间段
根据上面的公式再加上Androdi系统的坐标系,可以知道,手指顺着坐标轴正方向移动速度为正,反之速度为负数
最后,当不需要使用它的时候,需要调用clear方法来重置并回收内存:
velocityTracker.clear();
velocityTracker.recycle();
贴上完整代码
@Override
public boolean onTouchEvent(MotionEvent event) {
int action=event.getAction();
//追踪当前单击事件速度
VelocityTracker velocityTracker=VelocityTracker.obtain();
velocityTracker.addMovement(event);
switch (action){
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
velocityTracker.computeCurrentVelocity(1000);
int xVelocity= (int) velocityTracker.getXVelocity();
int yVelocity= (int) velocityTracker.getYVelocity();
Log.e("-----x轴速度-----",xVelocity+"");
Log.e("-----y轴速度-----",yVelocity+"");
break;
case MotionEvent.ACTION_UP:
velocityTracker.clear();
velocityTracker.recycle();
break;
}
return true;
}
5.GestureDetector(手势检测)
定义:用于辅助检测用户的单击、滑动、长按、双击等行为。
1.首先创建一个GestureDetector ,MyGestureListener 为实现了手势监听类
mDetector=new GestureDetector(context,new MyGestureListener());
MyGestureListener 类完整代码
public class MyGestureListener extends android.view.GestureDetector.SimpleOnGestureListener {
private static final String TAG = "MyGestureListener";
public MyGestureListener() {
super();
}
/**
* 手指轻触屏幕后离开,伴随着一个action up,单击行为
* @param e
* @return
*/
@Override
public boolean onSingleTapUp(MotionEvent e) {
Log.i(TAG, "onSingleTapUp : " + e.getAction());
return super.onSingleTapUp(e);
}
/**
* 长按屏幕
* 一直 down时触发
* @param e
*/
@Override
public void onLongPress(MotionEvent e) {
Log.i(TAG, "onLongPress : " + e.getAction());
super.onLongPress(e);
}
/**
* 由1个down ,多个move触发,这是拖动行为
* @param e1
* @param e2
* @param distanceX
* @param distanceY
* @return
*/
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
Log.i(TAG, "拖动行为 : ");
Log.i(TAG, "onScroll e1 : " + e1.getAction() + ", e2 : " + e2.getAction() + "," +
" distanceX : " + distanceX + ", distanceY : " + distanceY);
return super.onScroll(e1, e2, distanceX, distanceY);
}
/**
* Touch了滑动一点距离后,up时触发。 (快速滑动行为)
* @param e1
* @param e2
* @param velocityX
* @param velocityY
* @return
*/
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
return super.onFling(e1, e2, velocityX, velocityY);
}
/**
* Touch了还没有滑动时触发
* @param e
*/
@Override
public void onShowPress(MotionEvent e) {
super.onShowPress(e);
}
/**
* down时触发
* @param e
* @return
*/
@Override
public boolean onDown(MotionEvent e) {
Log.i(TAG, "onDown : " + e.getAction());
return super.onDown(e);
}
/**
* 双击的第二下Touch down时触发
* @param e
* @return
*/
@Override
public boolean onDoubleTap(MotionEvent e) {
Log.e("---双击的第二下----",e.getAction()+"");
return super.onDoubleTap(e);
}
/**
* 双击的第二下 down和up都会触发,可用e.getAction()区分。
* @param e
* @return
*/
@Override
public boolean onDoubleTapEvent(MotionEvent e) {
Log.i(TAG, "onDoubleTapEvent : " + e.getAction());
return super.onDoubleTapEvent(e);
}
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
return super.onSingleTapConfirmed(e);
}
@Override
public boolean onContextClick(MotionEvent e) {
return super.onContextClick(e);
}
}
该类继承自android.view.GestureDetector.SimpleOnGestureListener 类,这个类实现了
OnGestureListener,
OnDoubleTapListener,
OnContextClickListener这三个接口。可以根据需求自行实现其中一个接口。
2.接着,接管目标View的onTouchEvent方法,在带监听View的onTouchEvent 方法中添加如下实现:
return mDetector.onTouchEvent(event);
完整调用代码
/**
* 使用手势检测
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
return mGestureDetector.onTouchEvent(event);
}
或在自定义View中,如下
public class GestureView extends View {
public GestureView(Context context) {
super(context);
}
public GestureView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
final GestureDetector mDetector=new GestureDetector(context,new MyGestureListener());
setLongClickable(true);
mDetector.setIsLongpressEnabled(false);
setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
return mDetector.onTouchEvent(event);
}
});
}
public GestureView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
}
建议:如果只是监听滑动相关的,建议自己在onTouchEvent中实现,如果要监听双击这种行为的话,那么就用GestureDetector.