一、
1、Activity页面构成元素
- 1)在activity 中 通过 setContentView() 方法设置为根布局。在每一个Activity 中 都包含一个Window 对象,它由 PhoneWindow来实现
- 2)PhoneWindow 将一个 DecorView 设置为整个应用窗口的根View。
- 3)DecorView,由TitleView 和ContentView组成, 前者承载的是一个Actionbar,后者承载的是一个FrameLayout,
2、MotionEvent和TouchSlop
- 1)手指接触屏幕后 会产生以下的事件:
ACTION_DOWN // 收支接触
ACTION_MOVE // 手指在屏幕上移动
ACTION_UP //手指从屏幕松开
ACTION_CANCEL //取消
复制代码
-
2)MotionEvent 对象获取的 点击的 x 和 y ,
①.使用 getX / getY 获取的是相对于当前View左上角的x和y,
②.而 getRawX / getRawY 获取的是相对于手机屏幕左上角的坐标。 -
3)TouchSlop:是系统所能识别的最小滑动距离,小于它则视未发生滑动,在不同的设备上获取的值不同。通过:
ViewConfiguration.get(getContext()).getScaledTouchSlop() //获得
复制代码
3、VelocityTracker、GestureDetector和Scroller
- 1)VelocityTracker:速度追踪,用于追踪滑动过程中的速度,包括水平和竖直速度。
//1.在 onTouchEvent 方法中追踪当前事件的速度
VelocityTracker tracker = VelocityTracker.obtain();
tracker.addMovement(event);
//2.获取当前速度
tracker.computeCurrentVelocity(1000);//计算速度
int xVelocity = (int) tracker.getXVelocity();
int yVelocity = (int) tracker.getYVelocity();
//3.在不需要的时候重置回收内存
tracker.recycle();
复制代码
- 2)GestureDetector:用于检测单击、滑动、长按、双击等手势。
首先要实现 GestureDetector.OnGestureListener接口,如果需要双击,则需实现 GestureDetector.OnDoubleTapListener 接口;
//设置监听
mGestureDetector = new GestureDetector(this);
//避免长按后无法拖动,自己测试时发现不设置,长按后也可以拖动
mGestureDetector.setIsLongpressEnabled(false);
复制代码
- 3)Scroller:弹性滑动,可是实现有过度效果的滑动,View 的 ScrollTo/ScrollBy 都是瞬间滑动完成的。
4、View 的滑动
- 1)View的滑动主要有如下三种方式:
- scrollTo /scrollBy :适合对view 的内容改变;
- 动画: 主要用于没有交互的View 和实现复杂的动画效果;
- 改变布局参数:操作稍微复杂,适合有交互的View 。
- 2)View 的 ScrollTo/ScrollBy 方法
public void scrollTo(int x, int y) {
if (mScrollX != x || mScrollY != y) {
int oldX = mScrollX;
int oldY = mScrollY;
mScrollX = x;
mScrollY = y;
invalidateParentCaches();
onScrollChanged(mScrollX, mScrollY, oldX, oldY);
if (!awakenScrollBars()) {
postInvalidateOnAnimation();
}
}
}
public void scrollBy(int x, int y) {
scrollTo(mScrollX + x, mScrollY + y);
}
复制代码
其中 scrollBy 调用的是 scrollTo,它实现了使用当前位置的相对滑动,而scrollTo 是基于所传参数的绝对滑动.
- 3)使用动画
通过动画为View 添加平移效果,View 的 tanslationX 和 tanslationY 属性,可以采用传统的动画和属性动画。 - 4)改变布局参数
通过改变View 的LayoutParams 使得 View 重新布局实现滑动。
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams)
mButton.getLayoutParams();
params.width += 100;
params.leftMargin += 100;
mButton.setLayoutParams(params);
// 或者 mButton.requestLayout();
复制代码
5、弹性滑动
为了避免 滑动的生硬,可以采用弹性滑动,提高用户体验。
这里主要有 :Scroller、动画、延时三种方式。
- 1)Scroller 的典型使用,主要是 invalidate方法起的作用。
Scroller mScroller = new Scroller(context);
/**
* 滑动到指定位置
*
* @param destX X 滑动距离
* @param destY Y 滑动距离
*/
private void smoothScrollTo(int destX, int destY) {
//滑动起点X
int scrollX = getScrollX();
//滑动起点Y
int scrollY = getScrollY();
//1000 ms内慢慢滑向 (destX,destY)
mScroller.startScroll(scrollX, scrollY, destX, destY, 1000);
//重绘
invalidate();
}
/**
* 使View 不断重绘
*/
@Override
public void computeScroll() {
/**
* computeScrollOffset 方法通过时间流逝百分比计算 scrollX和scrollY
* 返回true 表示滑动未结束
*/
if (mScroller.computeScrollOffset()) {
//滑动到当前位置,通过小幅度滑动实现弹性滑动
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
//再次重绘
postInvalidate();
}
}
复制代码
- 2)对于延时达到弹性滑动,主要是利用 了Handler 或者 View 的 postDelayed 方法,或者线程的 sleep方法。
6、View 的事件分发机制
- 1)所有的Touch事件都封装到 MotionEvent 里面;
- 2)事件处理包括三种情况,分别为:传递—-dispatchTouchEvent()函数、拦截——onInterceptTouchEvent()函数、消费—-onTouchEvent()函数和OnTouchListener;
- 3)事件类型分为 ACTION_DOWN, ACTION_UP, ACTION_MOVE , ACTION_POINTER_DOWN, ACTION_POINTER_UP , ACTION_CANCEL 等,每个事件都是以 ACTION_DOWN 开始 ACTION_UP 结束。
用下面伪代码表示事件分发过程及其关系:
//事件分发
public boolean dispatchTouchEvent(MotionEvent event) {
boolean consume = false;
//是否被拦截
if (onInterceptTouchEvent(event))
{
//被拦截,处理事件
consume = onTouchEvent(event);
} else {
//未被拦截,向下分发
consume = childView.dispatchTouchEvent(event);
}
return consume;
}
复制代码
1、activity --->ViewGroup --->view 正常流程:
- 1)当activity dispatchTouchEvent() 返回 true :代表事件停止分发,activity 的onTouchEvent()方法不执行。其子view也接受不到事件。
- 2)ViewGroup onInterceptTouchEvent() 返回ture时,onTouchEvent() 也返回 true时;
ViewGroup
①、
ViewGroup oninterceptTouchEnent 返回 ture时,表示:传递给子View的事件被拦截了,
ViewGroup onTouchEvent() 返回 true时,表示:该事件被VieWGroup自身消费了,不传递给父View的onTouch方法中处理了。
代码:
EmptyViewGroup: onInterceptTouchEvent--->ACTION_DOWN
EmptyViewGroup: onTouchEvent--->ACTION_DOWN
...
EmptyViewGroup: onTouchEvent--->ACTION_MOVE
...
EmptyViewGroup: onTouchEvent--->ACTION_UP
复制代码
②、
ViewGroup oninterceptTouchEnent 返回 ture时,表示:传递给子View事件被拦截了,
ViewGroup onTouchEvent() 返回 false时,表示:ViewGroup也不处理此事件,交给上层父View(avtivity)的OnTouchEvent()方法处理。
代码:
//viewGroup只消费 ACTION_DOWN 事件
EmptyViewGroup: onInterceptTouchEvent--->ACTION_DOWN
EmptyViewGroup: onTouchEvent--->ACTION_DOWN
//activity 将事件消费
DemoActivity: onTouchEvent--->ACTION_DOWN
...
DemoActivity: onTouchEvent--->ACTION_MOVE
...
DemoActivity: onTouchEvent--->ACTION_UP
复制代码
③、 ViewGroup onInterceptTouchEvent 返回 false时,表示事件传递给子View,不拦截。
ViewGroup onTouchEvent() 返回 false时,ViewGroup也不处理此事件,交给上层父View(avtivity)的OnTouchEvent()方法处理。
View onTouchEvent() 返回 true: 该子View消费了此事件。不传递到ViewGroup的OnTouchEvent()方法中。
EmptyViewGroup: onInterceptTouchEvent--->ACTION_DOWN
EmptyView: onTouchEvent--->ACTION_DOWN
EmptyViewGroup: onInterceptTouchEvent--->ACTION_MOVE
EmptyView: onTouchEvent--->ACTION_MOVE
...
EmptyViewGroup: onInterceptTouchEvent--->ACTION_UP
EmptyView: onTouchEvent--->ACTION_UP
复制代码
④、 ViewGroup onInterceptTouchEvent 返回 false时,表示事件传递给子View,不拦截。
ViewGroup onTouchEvent() 返回 false时,ViewGroup也不处理此事件,交给上层父View(avtivity)的OnTouchEvent()方法处理。
View onTouchEvent() 返回 false: 该子View不消费了此事件。传递到ViewGroup的OnTouchEvent()方法中。
EmptyViewGroup: onInterceptTouchEvent--->ACTION_DOWN
EmptyView: onTouchEvent--->ACTION_DOWN
EmptyViewGroup: onTouchEvent--->ACTION_DOWN
DemoActivity: onTouchEvent--->ACTION_DOWN
DemoActivity: onTouchEvent--->ACTION_MOVE
...
DemoActivity: onTouchEvent--->ACTION_UP
复制代码
由于activity作为顶级的View,当activity 中所有得子View 都不消费onTouchEvent()事件时,ativity的OnTouchEvent() 无论返回true/false。该activity都会消费用户触发的事件。在onTouchEvent()中。