3.1 View基础知识
3.1.1 什么是View
- View 是 Android 中所有控件的基类,是一种界面层控件的抽象。
- ViewGroup 也是继承 View
3.1.2 View 的位置参数
- View 的位置有四个顶点决定,top、left、right、bottom
- View 宽高和坐标的关系
width = right - left
height = bottom - top
- 获取
1.Left = getLeft();
.....
- View 额外参数
1.x View 左上角坐标
2.y
3.translationX View 左上角相对于父容器的偏移量
4.translationY
5. x = left + translationX
6. y = top + translationY
3.1.3 MotionEvent 和 TouchSlop
- MotionEvent:手机触摸屏幕产生事件
1.ACTION_DOWN:触摸屏幕
2.ACTION_MOVE:在屏幕上移动
3.ACTION_UP:松开
- getX/getRawX 区别
1.getX/getY 当前 View 左上角的 x、y 坐标
2.getRawX/getRawY 相对于手机屏幕左上角 x 、y 坐标
- TouchSlop
1.系统能识别出被认为是滑动的最小距离(和设备有关)
2.ViewCongfiguration.get(getContext()).getScaledTouchSlop()
3.1.4 VelocityTracker、GestureDetector 和 Scroller
- VelocityTracker
1.速度追踪,追踪手机在滑动过程中速度,水平和竖直方向
VelocityTracker velocityTracker = VelocityTracker.obtain();
velocityTracker.addMovement(event);
velocityTracker.computeCurrentVelocity(1000);
int x = (int)velocityTracker.getXVelocity();
int y = (int)velocityTracker.getYVelocity();
velocityTracker.clear();
velocityTracker.recycler();
- GestureDetector
1.手势检测,辅助检测用户单击、滑动、长按、双击等
2.onSingleTapUp(单击)、onFling(快速滑动)、onScroll(拖动)、onLongPress(长按)、onDoubleTap(双击)
GestureDetector gestureDetector = new GestureDetector(this);
gestureDetector.setIsLongpressEnabled(false);
boolean result = gestureDetector.onTouchEvent(event);
return result;
- Scroller
1.弹性滑动对象,View 的 scrollTo/scrollBy 是瞬间完成
Scroller mScroller = new Scroller(context);
private void smoothScrollTo(int destX,int destY){
int scrollX = getScrollX();
int delta = destX - scrollX;
mScroller.startScroll(scrollX,0,destX,0,1000);
invalidate();
}
@Override
public void computeScroll() {
super.computeScroll();
if (mScroller.computeScrollOffset()) {
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
invalidate();
}
}
3.2 View滑动
- scrollTo/scrollBy
- 动画
- LayoutParams
3.2.1 使用scrollTo/scrollBy
- scrollBy 实际使用 scrollTo
3.2.2 使用动画
<?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
- 将一次滑动分成若干次小滑动,并且在一个时间段内完成。
- Scroller 不能实现 View 滑动,需要配合 computeScroll 方法,不断让 View 重绘,每一次重绘滑动起始时间会有一个时间间隔,通过这个时间间隔得出 View 当前滑动位置,scrollTo 完成 滑动。
3.3.2 通过动画
- ValueAnimator
- addUpdateListener 中 scrollTo
3.3.3 使用延时策略
- Handler
- View 的 postDelayed
- sleep
3.4 View的事件分发机制
3.4.1 点击事件的传递规则
- 所谓点击事件分发,就是 MotionEvent 分发过程
- 三个重要方法
1.dipatcTouchEvent
事件分发
2.onInterceptTouchEvent
事件拦截
3.onTouchEvent
事件处理
public boolean dispatchTouchEvent(MotionEvent ev) {
boolean consume = false;
if(onInterceptTouchEvent(ev)){
consume = onTouchEvent(ev);
} else {
consume = child.dispatchTouchEvent(ev);
}
return consume;
}
- 如果设置 onTouchListener,那么 OntouchEventListener 中的 onTouch 会被调用,如果 onTouch 返回 false,onTouchEvent 会被调用,否则不会。onTouchListener 优先级比 onTouchEvent 高,在 onTouchEvent 中,设置 onClickListener,onClick 会被调用,onTouchListener > onTouchEvent > onClickListener。
- 传递顺序,Activity -> Window -> View
- 结论
结论 |
事件序列以 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 事件分发源码解析
- Activity -> Window(PhoneWIndow) -> DecorView -> 根 View(ViewGroup)
- FLAG_DISALLOW_INTERCEPT 标志位,通过 requestDisallowInterceptTouchEvent 设置,设置后,ViewGroup 无法拦截除 ACTION_DOWN 以外的点击事件。
- 判断子元素是否能哦接收点击事件:1.子元素是否在播动画;2.点击事件坐标落在子元素区域内
3.5 View的滑动冲突
3.5.1 常见的滑动冲突场景
- 场景
1.外部滑动方向和内部滑动方向不一致
2.外部滑动方向和内部滑动方向一致
3.上面两种嵌套
3.5.3 滑动冲突的解决方式
方式 |
简介 |
外部拦截法 |
点击事件都见过父容器处理(重写父容器 onInterceptTouchEvent) |
内部拦截法 |
父容器不拦截任何事件,传递子元素,配合 requestDisallowInterceptTouchEvent |