View的事件体系基础知识
View的位置参数
View的四个位置主要有思安个顶点决定,分别对应四个属性,top、left、right、bottom.
- top:表示View的原始左上角的纵坐标
- left:表示View的原始左上角的横坐标
- bottom:表示View的原始右下角的纵坐标
- right:表示View的原始右下角的横坐标
这些坐标都是相对于View的父容器来说的,都是相对坐标
从Android3.0开始增加了几个参数:
- x:表示View左上角的横坐标
- y:表示View左上角的纵坐标
- translationX:表示View左上角相对于父容器的横坐标的偏移量,默认值是0
- translationY:表示View左上角相对于父容器的纵坐标的偏移量,默认值是0
换算关系是:
- x=left+translationX
- y=top+translationY
1.View属性动画的时候,变化的是x / y和translationX / translationY,left / right和top / bottom不会改版变化
2.其他不是属性动画情况,translationX,translationY会一直为0
MotionEvent的位置坐标信息
MotionEvent提供的方法:
- getX():点击位置到View的左边的距离
- getY():点击位置到View的上边的距离
- getRawX():点击位置到手机屏幕左边的距离
- getRawY();点击位置到手机屏幕上边的距离
TouchSlop是系统所能识别出的最小的滑动距离
ViewConfiguration.get(getContext()).getScaledTouchSlop()可获取,在处理滑动时,可以利用它来过滤
速度追踪
VelocityTracker,用于追踪手机在滑动过程中的速度,包括水平和竖直方向的速度
使用过程如下:
首先初始化VelocityTracker对象
VelocityTracker mVelocityTracker = VelocityTracker.obtain();
在View的onTouchEvent方法中追踪当前点击事件的速度
@Override
public boolean onTouchEvent(MotionEvent event) {
mVelocityTracker.addMovement(event);
}
通过如下方式来获取速度
mVelocityTracker.computeCurrentVelocity(1000);//计算速度
float xVelocity = mVelocityTracker.getXVelocity();//获取x方向的速度
float yVelocity = mVelocityTracker.getYVelocity();//获取y方向的速度
computeCurrentVelocity这个方法的参数表示的是一个时间单元或者时间间隔,单位毫秒,在这时间间隔内手指在水平或竖直方向所划过的像素数。
不使用了的时候,重置并回收
mVelocityTracker.clear();//重置
mVelocityTracker.recycle();//回收
手势检测,用于辅助检测用户的单击,滑动,长按,双击等行为。
1.创建一个GestureDetector对象并实现OnGestureListener接口,还可以实现OnDoubleTapListener监听双击的行为~~~
mGestureDetector = new GestureDetector(new MyOnGestureListener());//MyOnGestureListener是OnGestureListener接口实现类
//下面这一步是对双击的监听
mGestureDetector.setOnDoubleTapListener(new MyOnDoubleTapListener());//MyOnDoubleTapListener是OnDoubleTapListener接口实现类
2.接管View的onTouchEvent方法,onTouchEvent或者OnTouchListener中添加如下:
imgView.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
boolean consume = mGestureDetector.onTouchEvent(event);//这里添加了
return consume;
}
});
//或者
public boolean onTouchEvent(MotionEvent event) {
boolean consume = mGestureDetector.onTouchEvent(event);//这里添加了
return consume;
}
做完上面两步,我们就可以实现对手势检测了。
查看Android源码发现SimpleOnGestureListener,实现了onGestureListener和OnDoubleTapListener
public static class SimpleOnGestureListener implements OnGestureListener, OnDoubleTapListener,
OnContextClickListener {
其实实现这个两个接口要实现好多方法,以后我们使用SimpleOnGestureListener可以有选择的实现某些常用的方法,减少代码量。
View的滑动
1. scrollTo和scrollBy方式
- View的scrollTo和scrollBy是瞬间完成滑动
- 这里滑动的是View的内容,View的本身并没有移动
- scrollBy是相对于上次的滑动。scrollTo是滑动到某个位置
@Override
public boolean onTouchEvent(MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
lastX = (int) event.getX();
lastY = (int) event.getY();
break;
case MotionEvent.ACTION_MOVE:
int offsetX = x - lastX;
int offsetY = y - lastY;
((View) getParent()).scrollBy(-offsetX, -offsetY);//偏移量
//或者用下面的scrollTo
// int mScrollX = ((View) getParent()).getScrollX();//初始x
// int mScrollY = ((View) getParent()).getScrollY();//初始y
// ((View) getParent()).scrollTo(mScrollX - offsetX, mScrollY - offsetY);//加上偏移量
break;
}
return true;
}
2.属性动画方式 Android属性动画
// 属性动画方式,,相对坐标
@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;
// 改变x y
setTranslationX(getTranslationX() + offsetX);
setTranslationY(getTranslationY() + offsetY);
break;
}
return true;
}
3.改变布局参数MarginLayoutParams方式
@Override
public boolean onTouchEvent(MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// 记录触摸点坐标
lastX = (int) event.getX();
lastY = (int) event.getY();
break;
case MotionEvent.ACTION_MOVE:
// 计算偏移量
int offsetX = x - lastX;
int offsetY = y - lastY;
ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) getLayoutParams();
layoutParams.leftMargin = getLeft() + offsetX;
layoutParams.topMargin = getTop() + offsetY;
setLayoutParams(layoutParams);
break;
}
return true;
}
4.改变View的left、top、right、bottom
4.1视图坐标方式——使用相对坐标
@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;
// 在当前left、top、right、bottom的基础上加上偏移量
layout(getLeft() + offsetX,
getTop() + offsetY,
getRight() + offsetX,
getBottom() + offsetY);
//或者下面这样,原理同上面一样
//offsetLeftAndRight(offsetX);
//offsetTopAndBottom(offsetY);
break;
}
return true;
}
4.2视图坐标方式——使用绝对坐标
@Override
public boolean onTouchEvent(MotionEvent event) {
int rawX = (int) (event.getRawX());
int rawY = (int) (event.getRawY());
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// 记录触摸点坐标
lastX = rawX;
lastY = rawY;
break;
case 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;
break;
}
return true;
}
当然其他滑动方式也都可以使用绝对坐标,也是没有问题的。
5.弹性滑动
View也是滑动的内容,本身不滑动
5.1初始化Scroller
// 初始化Scroller
mScroller = new Scroller(context);
5.2 onTouchEvent方法操作Scroller
@Override
public boolean onTouchEvent(MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
lastX = (int) event.getX();
lastY = (int) event.getY();
break;
case MotionEvent.ACTION_MOVE:
int offsetX = x - lastX;
int offsetY = y - lastY;
((View) getParent()).scrollBy(-offsetX, -offsetY);
break;
case MotionEvent.ACTION_UP:
// 手指离开时,执行滑动过程
View viewGroup = ((View) getParent());
mScroller.startScroll(
viewGroup.getScrollX(),//开始位置x
viewGroup.getScrollY(),//开始位置y
-viewGroup.getScrollX(),//滑动距离x
-viewGroup.getScrollY());//滑动距离y
invalidate();
break;
}
return true;
}
5.3 调用computeScroll()刷新
@Override
public void computeScroll() {
super.computeScroll();
// 判断Scroller是否执行完毕
if (mScroller.computeScrollOffset()) {
((View) getParent()).scrollTo(
mScroller.getCurrX(),
mScroller.getCurrY());
// 通过重绘来不断调用computeScroll
invalidate();
}
}