《Android开发艺术探索》读书笔记--第3章 View的事件体系

文章目录

    • 3.1 View基础知识
      • 3.1.1 什么是View
      • 3.1.2 View 的位置参数
      • 3.1.3 MotionEvent 和 TouchSlop
      • 3.1.4 VelocityTracker、GestureDetector 和 Scroller
    • 3.2 View滑动
      • 3.2.1 使用scrollTo/scrollBy
      • 3.2.2 使用动画
      • 3.2.3 改变布局参数
      • 3.2.4 各种滑动方式对比
    • 3.3 弹性滑动
      • 3.3.1 Scroller
      • 3.3.2 通过动画
      • 3.3.3 使用延时策略
    • 3.4 View的事件分发机制
      • 3.4.1 点击事件的传递规则
      • 3.4.2 事件分发源码解析
      • 3.5 View的滑动冲突
      • 3.5.1 常见的滑动冲突场景
      • 3.5.3 滑动冲突的解决方式

3.1 View基础知识

3.1.1 什么是View

  1. View 是 Android 中所有控件的基类,是一种界面层控件的抽象。
  2. ViewGroup 也是继承 View

3.1.2 View 的位置参数

  1. View 的位置有四个顶点决定,top、left、right、bottom《Android开发艺术探索》读书笔记--第3章 View的事件体系_第1张图片
  2. View 宽高和坐标的关系
width = right - left
height = bottom - top
  1. 获取
1.Left = getLeft();
.....
  1. View 额外参数
1.x View 左上角坐标
2.y
3.translationX View 左上角相对于父容器的偏移量
4.translationY

5. x = left + translationX
6. y = top + translationY

3.1.3 MotionEvent 和 TouchSlop

  1. MotionEvent:手机触摸屏幕产生事件
    1.ACTION_DOWN:触摸屏幕
    2.ACTION_MOVE:在屏幕上移动
    3.ACTION_UP:松开
  2. getX/getRawX 区别
    1.getX/getY 当前 View 左上角的 x、y 坐标
    2.getRawX/getRawY 相对于手机屏幕左上角 x 、y 坐标
  3. TouchSlop
    1.系统能识别出被认为是滑动的最小距离(和设备有关)
    2.ViewCongfiguration.get(getContext()).getScaledTouchSlop()

3.1.4 VelocityTracker、GestureDetector 和 Scroller

  1. VelocityTracker
    1.速度追踪,追踪手机在滑动过程中速度,水平和竖直方向
VelocityTracker velocityTracker = VelocityTracker.obtain();
velocityTracker.addMovement(event);
// 1s内手指滑过的像素点
velocityTracker.computeCurrentVelocity(1000);
int x = (int)velocityTracker.getXVelocity();
int y = (int)velocityTracker.getYVelocity();
// 回收内存
velocityTracker.clear();
velocityTracker.recycler();
  1. GestureDetector
    1.手势检测,辅助检测用户单击、滑动、长按、双击等
    2.onSingleTapUp(单击)、onFling(快速滑动)、onScroll(拖动)、onLongPress(长按)、onDoubleTap(双击)
//1.实现 OnGestureListener
//2.创建
GestureDetector gestureDetector = new GestureDetector(this);
//长按屏幕后无法拖动
gestureDetector.setIsLongpressEnabled(false);
//3.目标 View onTouchEvent 方法
boolean result = gestureDetector.onTouchEvent(event);
return  result;
  1. Scroller
    1.弹性滑动对象,View 的 scrollTo/scrollBy 是瞬间完成
//1.创建 Scroller 
Scroller mScroller = new Scroller(context);
//2.滑动指定位置
private void  smoothScrollTo(int destX,int destY){
      int scrollX = getScrollX();
      int delta = destX  - scrollX;
      //1000ms 内滑向destX
      mScroller.startScroll(scrollX,0,destX,0,1000);
      // View 重绘 draw
      invalidate();
}
//3.重写 computeScroll
@Override
public void computeScroll() {
    super.computeScroll();
    //根据时间的流逝来计算出当前的scrollX和scrollY的值
    if (mScroller.computeScrollOffset()) {
        scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
        invalidate();
    }
}
//computeScrollOffset
//返回 ture 滑动未结束
//false 滑动结束

3.2 View滑动

  1. scrollTo/scrollBy
  2. 动画
  3. LayoutParams

3.2.1 使用scrollTo/scrollBy

  1. scrollBy 实际使用 scrollTo

3.2.2 使用动画

//View 动画
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:fillAfter="true">
    <translate
        android:duration="100"
        android:fromXDelta="0"
        android:fromYDelta="0"
        android:interpolator="@android:anim/linear_interpolator"
        android:toXDelta="100"
        android:toYDelta="100" />

</set>

//属性动画
ObjectAnimator.ofFloat(view, "translationX", 0, 100),

3.2.3 改变布局参数

ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams)bt.getLayoutParams();
params.width += 100;
params.leftMargin += 100;
bt.requestLayout();
//或者
bt.setLayoutParams(params);

3.2.4 各种滑动方式对比

scrollTo/scrollBy 1.方便,不影响内部单击事件;2.只能滑动View内容,不能滑动本身
动画 1.3.0以上使用属性动画没有缺点;2.操作简单,适用于没有交互的View和复杂动画效果
LayoutParams 1.使用麻烦,适用于有交互的 View

3.3 弹性滑动

3.3.1 Scroller

  1. 将一次滑动分成若干次小滑动,并且在一个时间段内完成。
  2. Scroller 不能实现 View 滑动,需要配合 computeScroll 方法,不断让 View 重绘,每一次重绘滑动起始时间会有一个时间间隔,通过这个时间间隔得出 View 当前滑动位置,scrollTo 完成 滑动。

3.3.2 通过动画

  1. ValueAnimator
  2. addUpdateListener 中 scrollTo

3.3.3 使用延时策略

  1. Handler
  2. View 的 postDelayed
  3. sleep

3.4 View的事件分发机制

3.4.1 点击事件的传递规则

  1. 所谓点击事件分发,就是 MotionEvent 分发过程
  2. 三个重要方法
1.dipatcTouchEvent
事件分发

2.onInterceptTouchEvent
事件拦截

3.onTouchEvent
事件处理


public boolean dispatchTouchEvent(MotionEvent ev) {
    boolean consume = false;
    //是否需要拦截
    if(onInterceptTouchEvent(ev)){
        //自己处理事件
        consume = onTouchEvent(ev);
    } else {
        //传递给子 View 
        consume = child.dispatchTouchEvent(ev); 
    }
    return consume;
}
  1. 如果设置 onTouchListener,那么 OntouchEventListener 中的 onTouch 会被调用,如果 onTouch 返回 false,onTouchEvent 会被调用,否则不会。onTouchListener 优先级比 onTouchEvent 高,在 onTouchEvent 中,设置 onClickListener,onClick 会被调用,onTouchListener > onTouchEvent > onClickListener。
  2. 传递顺序,Activity -> Window -> View
  3. 结论
结论
事件序列以 down 开始,数量不定 move,以 up 结束
一旦 View 拦截此事件,所有事件都会交个它处理,但是有特殊,通过 onTouchEvent 传递给其他 View 处理
View拦截事件,事件序列由它处理,onInterceptTouchEvent 不再被调用
View处理事件,不消耗 ACTION_DOWN 事件(onTouchEvent 返回 false),其他事件都不会交个它处理,并且事件重新交个父元素处理(onTouchEvent)
View不消耗除ACTION_DOWN 事件,点击事件会消失,父元素 onTouchEvent 不会被调用,View 可以收到后序事件,消失事件有 Activity 处理
ViewGroup默认不拦截任何事件,源码 ViewGroup 中 onInterceptTouch 返回 false
View 没有 onInterceptTouchEvent方法,一旦事件传递给它,onTouchEvent 会被调用
View 的 onTouchEvent 默认 返回 true,除非不可点击,默认 longClickable 为false,bt的clickable 为 true ,tv 为 false
View 的 enable 不影响 onTouchEvent 默认返回值
onClick 会发生前提 View可点击
事件传递有外向内,先传递给父元素,父元素分发给 子View,通过 requesDisabllowInterceptTouchEvent 干预父元素事件分发,ACTION_DOWN 除外

3.4.2 事件分发源码解析

  1. Activity -> Window(PhoneWIndow) -> DecorView -> 根 View(ViewGroup)
  2. FLAG_DISALLOW_INTERCEPT 标志位,通过 requestDisallowInterceptTouchEvent 设置,设置后,ViewGroup 无法拦截除 ACTION_DOWN 以外的点击事件。
  3. 判断子元素是否能哦接收点击事件:1.子元素是否在播动画;2.点击事件坐标落在子元素区域内

3.5 View的滑动冲突

3.5.1 常见的滑动冲突场景

  1. 场景
    1.外部滑动方向和内部滑动方向不一致
    2.外部滑动方向和内部滑动方向一致
    3.上面两种嵌套

3.5.3 滑动冲突的解决方式

方式 简介
外部拦截法 点击事件都见过父容器处理(重写父容器 onInterceptTouchEvent)
内部拦截法 父容器不拦截任何事件,传递子元素,配合 requestDisallowInterceptTouchEvent

你可能感兴趣的:(读书笔记,#,Android开发艺术探索)