2018.03.08、View的事件分发机制笔记

一、

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()中。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值