[TOC]
View基础
一些基础:
MotionEvent:触摸事件序列:ACTION_DOWN、ACTION_MOVE、ACTION_UP
TouchSlop:可以识别出的滑动最小距离;判断此时是否属于滑动事件
VelocityTracker:滑动速度
GestureDetector:手势检测,手势处理场景:双击、滑动、长按等
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36@Override
public boolean onTouchEvent(MotionEvent event) {
return gestureDetector.onTouchEvent(event);
}
GestureDetector gestureDetector = new GestureDetector(this.getContext(), new GestureDetector.OnGestureListener() {
@Override
public boolean onDown(MotionEvent e) {
return false;
}
@Override
public void onShowPress(MotionEvent e) {
}
@Override
public boolean onSingleTapUp(MotionEvent e) {
return false;
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
return false;
}
@Override
public void onLongPress(MotionEvent e) {
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
return false;
}
});实现View的滑动方式
- 通过View本身提供的scrollBy/scrollTo
- 通过动画给View添加平移效果
- 通过改变View的LayoutParams使得View重新布局从而实现滑动
View的弹性滑动
- Scroller:本身没有实现弹性滑动,需要和View的computeScroll配合实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36// 1. 首先初始化一个对象
private void init(Context context) {
scroller = new Scroller(context);
}
// 2. 然后重写View的computeScroll方法和调用的方法
public void smoothScrollTo(int destX, int dextY) {
int scrollX = getScrollX();
int delta = destX - scrollX;
//在1s内滑向delta,效果就是慢慢滑动
scroller.startScroll(scrollX, 0, delta, 0,1000);
invalidate();
}
@Override
public void computeScroll() {
if (scroller.computeScrollOffset()) {
scrollTo(scroller.getCurrX(), scroller.getCurrY());
postInvalidate();
}
}
// 3. 调用
private void initView() {
final MyButton button = findViewById(R.id.button);
linearLayout = findViewById(R.id.lin);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
linearLayout.smoothScrollTo(-500,0);
}
});
}
流程:首先通过Scroller类的startScroll()+invalidate()触发View的computeScroll(),在computeScroll()中让Scroller类去计算最新的坐标信息,拿到最新的坐标偏移信息后还是要调用View的scrollTo来实现滑动Scroller工作机制: Scroller本身并不能实现View的滑动,他需要配合View的computeScroll方法,他不断的让View重绘,每一次重绘就会根据时间间隔,算出当前的位置,然后通过scrollTo进行滑动,这样每一次重绘都会移动一小点距离,多次小幅度移动就组成了弹性滑动。
- 通过插值器动画
- 使用延时策略
View的事件分发
View的事件分发主要涉及三个方法:
- dispatchTouchEvent:主要负责View事件的分发,返回值收到当前View的onTouchEvent事件返回值和下级View的dispatchTouchEvent的影响。
- onInterceptTouchEvent:内部调用,用于判断是否拦截当前事件。
- onTouchEvent:用来处理点击事件,返回值表示是否消费当前事件。
伪代码表示三者的关系:
1 | public boolean dispatchTouchEvent(MotionEvent ev) { |
具体流程参考下图:
事件分发常见的问题集合
事件分发的主要流程?
用户从触摸屏幕至离开会产生一系列的事件,down-move-move……move-up,Android会将一个事件包装成MotionEvent,从Activity#dispatchTouchEvent开始分发,activity->window->decorView->viewGroup->View,主要涉及到几个方法:
- dispatchTouchEvent:用于事件的分发,所有的事件都必须经过这个方法的分发,然后决定是自身消费还是往下分发,true表示不继续分发,false继续往下分发,如果是ViewGroup则调用onInterceptTouchEvent进行判断是否拦截该事件
- onInterceptTouchEvent:ViewGroup方法,用于判断是否拦截事件
- onTouchEvent:事件消费是否消费
子View在消费掉Down事件后,后续的事件都会传递给它,怎么实现的?
ViewGroup中有一个mFirstTouchTarget成员变量用于保存消费事件的子View的信息,因为Android是支持多指操作的,所以mFirstTouchTarget是一个TouchTarget的链表,View的dispatchTouchEvent有三个阶段:判断是否拦截;找到消费的View,更新mFirstTouchTarget;根据是否拦截和mFirstTouchEvent再次分发事件
CANCEL事件什么时候触发?
Down事件父View不拦截,后续的事件父View重新拦截的时候,会将拦截事件改为CANCEL事件分发给子View
requestDisallowInterceptTouchEvent方法的作用?
干预父View的事件分发,比如在接收到Down事件后调用该方法阻止viewGroup拦截事件,但是如果ViewGroup从Down事件就拦截了,那么子view调用会无效,因为不是Down事件并且mFirstTouchEvent为null,那么viewGroup就直接拦截分发了
DOWN事件被子View消费了,ViewGroup是否可以拦截后续的事件,如果拦截了,当前这个事件viewGroup能消费吗?子View还会收到事件吗?
可以拦截,拦截后当前事件会被改为CANCEL事件分发给子View,该事件就不能被viewGroup消费了。所以可以拦截后续事件,不能消费,子View会收到CANCEL事件。
当View Disable时,会消费事件吗?
可以消费,但是不会有任何的反应。只要clickable、long_clickable或者context_clickable其中一个为true的话,就会对事件进行消费。
onTouchListener、onClickListener的优先级?
View需要处理事件时,如果设置了OnTouchListener,OnTouchLisener的onTouch方法会被回调,返回false则View的onTouchEvent被调用,true,onTouchEvent不会被调用,可见OnTouchListener优先级高于onTouchEvent,在onTouchEvent回调中,如果设置了OnClickListener,则onClick会被回调,所以优先级:OnTouchListener > onTouchEvent > OnClickListener
View的绘制流程
View的绘制主要涉及三个方法:
- measure: 测量,根据xml和代码测量ViewGroup和View的大小
- layout:布局,根据测量结果及布局参数确定显示的位置
- draw:绘制。