ScrollView和RecyclerView的滑动事件处理
程序员文章站
2022-08-03 17:14:14
View的事件传递不做重复性的说明,关键函数在viewGroup的dispatchTouchEvent中,可参考其他博客学习。重点说明嵌套滑动的处理。public boolean dispatchTouchEvent(MotionEvent ev) {.........//down事件是一个touch事件的开始,所以在down事件的时候,要清理掉其他的if (onFilterTouchEventForSecurity(ev)) { final int action = e...
View的事件传递不做重复性的说明,关键函数在viewGroup的dispatchTouchEvent中,可参考其他博客学习。重点说明嵌套滑动的处理。
public boolean dispatchTouchEvent(MotionEvent ev) {
.........
//down事件是一个touch事件的开始,所以在down事件的时候,要清理掉其他的
if (onFilterTouchEventForSecurity(ev)) {
final int action = ev.getAction();
final int actionMasked = action & MotionEvent.ACTION_MASK;
// Handle an initial down.
if (actionMasked == MotionEvent.ACTION_DOWN) {
// Throw away all previous state when starting a new touch gesture.
// The framework may have dropped the up or cancel event for the previous gesture
// due to an app switch, ANR, or some other state change.
cancelAndClearTouchTargets(ev);
resetTouchState();
}
/ Check for interception. touchTarget是一个链表,记录的是哪些view来接收这个触摸事件,
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
内部拦截的关键点是 此处,如果不要求不拦截,该变量的修改,requestDisallowInterceptTouchEvent。
if (!disallowIntercept) {
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action); // restore action in case it was changed
} else {
intercepted = false;
}
} else {
// There are no touch targets and this action is not an initial down
// so this view group continues to intercept touches.
intercepted = true;
}
// If intercepted, start normal event dispatch. Also if there is already
// a view that is handling the gesture, do normal event dispatch.
if (intercepted || mFirstTouchTarget != null) {
ev.setTargetAccessibilityFocus(false);
}
// Check for cancelation.
final boolean canceled = resetCancelNextUpFlag(this)
|| actionMasked == MotionEvent.ACTION_CANCEL;
// Update list of touch targets for pointer down, if needed.
final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
TouchTarget newTouchTarget = null;
boolean alreadyDispatchedToNewTouchTarget = false;
if (!canceled && !intercepted) {
//手势操作的开始
View childWithAccessibilityFocus = ev.isTargetAccessibilityFocus()
? findChildWithAccessibilityFocus() : null;
if (actionMasked == MotionEvent.ACTION_DOWN
|| (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
final int actionIndex = ev.getActionIndex(); // always 0 for down
final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)
: TouchTarget.ALL_POINTER_IDS;
// Clean up earlier touch targets for this pointer id in case they
// have become out of sync.
removePointersFromTouchTargets(idBitsToAssign);
final int childrenCount = mChildrenCount;
if (newTouchTarget == null && childrenCount != 0) {
final float x = ev.getX(actionIndex);
final float y = ev.getY(actionIndex);
// Find a child that can receive the event.
// Scan children from front to back.
final ArrayList<View> preorderedList = buildTouchDispatchChildList();
final boolean customOrder = preorderedList == null
&& isChildrenDrawingOrderEnabled();
//查找子view
final View[] children = mChildren;
for (int i = childrenCount - 1; i >= 0; i--) {
final int childIndex = getAndVerifyPreorderedIndex(
childrenCount, i, customOrder);
final View child = getAndVerifyPreorderedView(
preorderedList, children, childIndex);
// If there is a view that has accessibility focus we want it
// to get the event first and if not handled we will perform a
// normal dispatch. We may do a double iteration but this is
。。。。。//如果有人需要处理该事件
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
// Child wants to receive touch within its bounds.
mLastTouchDownTime = ev.getDownTime();
if (preorderedList != null) {
// childIndex points into presorted list, find original index
for (int j = 0; j < childrenCount; j++) {
if (children[childIndex] == mChildren[j]) {
mLastTouchDownIndex = j;
break;
}
}
} else {
mLastTouchDownIndex = childIndex;
}
mLastTouchDownX = ev.getX();
mLastTouchDownY = ev.getY();
// view添加到链条中
newTouchTarget = addTouchTarget(child, idBitsToAssign);
alreadyDispatchedToNewTouchTarget = true;
break;
}
// The accessibility focus didn't handle the event, so clear
// the flag and do a normal dispatch to all children.
ev.setTargetAccessibilityFocus(false);
}
if (preorderedList != null) preorderedList.clear();
}
if (newTouchTarget == null && mFirstTouchTarget != null) {
// Did not find a child to receive the event.
// Assign the pointer to the least recently added target.
newTouchTarget = mFirstTouchTarget;
while (newTouchTarget.next != null) {
newTouchTarget = newTouchTarget.next;
}
newTouchTarget.pointerIdBits |= idBitsToAssign;
}
}
}
// Dispatch to touch targets.
if (mFirstTouchTarget == null) {
// No touch targets so treat this as an ordinary view.
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
} else {
// Dispatch to touch targets, excluding the new touch target if we already
// dispatched to it. Cancel touch targets if necessary.
TouchTarget predecessor = null;
TouchTarget target = mFirstTouchTarget;
while (target != null) {
final TouchTarget next = target.next;
if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
handled = true;
} else {
final boolean cancelChild = resetCancelNextUpFlag(target.child)
|| intercepted;
if (dispatchTransformedTouchEvent(ev, cancelChild,
target.child, target.pointerIdBits)) {
handled = true;
}
if (cancelChild) {
if (predecessor == null) {
mFirstTouchTarget = next;
} else {
predecessor.next = next;
}
target.recycle();
target = next;
continue;
}
}
predecessor = target;
target = next;
}
}
// Update list of touch targets for pointer up or cancel, if needed.
if (canceled
|| actionMasked == MotionEvent.ACTION_UP
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
resetTouchState();
} else if (split && actionMasked == MotionEvent.ACTION_POINTER_UP) {
final int actionIndex = ev.getActionIndex();
final int idBitsToRemove = 1 << ev.getPointerId(actionIndex);
removePointersFromTouchTargets(idBitsToRemove);
}
}
if (!handled && mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1);
}
return handled;
}
}
嵌套滑动需要两个角色:父布局和子布局,子view的滑动推动父view的滑动。主动者是子View
问题1:滑动底部RecyclerView时,上半部分无法跟随滑动。RecyclerView实现了嵌套滑动,但是它的父布局如果使用(Scrollview)未能实现嵌套滑动,所以无法上半部分无法滑动。
处理方法:使用有嵌套的ScrollView
问题2:吸顶的效果
处理方法:设置固定的高度来解决吸顶的问题
问题3:滑动底部的时候,如果上面的view可以话滑动的话则优先滑动上面view
处理方法:onNestedPreScroll回调默认是将滑动动作 通过mChildHelper接口传递给父View。此时需要拦截该事件
进行处理,
思考问题4:Filing后的滑动嵌套,在对父view进行fling操做的时候,如果父view已经不可滑动,则剩余fling操作可继续滑动子view。
本文地址:https://blog.csdn.net/lifeijiyuan126/article/details/107350965
推荐阅读
-
Android中控制和禁止ScrollView自动滑动到底部的方法
-
Android中父View和子view的点击事件处理问题探讨
-
详解JavaScript中的事件流和事件处理程序
-
angularJs的事件处理指令测试和代码整理
-
C#开发微信门户及应用(21)-微信企业号的消息和事件的接收处理及解密
-
ScrollView和RecyclerView的滑动事件处理
-
单击和双击事件的冲突处理示例代码
-
详解JavaScript中的事件流和事件处理程序
-
jq----dom.trigger(eventType,data)执行dom绑定事件的处理程序和行为
-
ScrollView(RecyclerView等)为什么会自动滚动原理分析,还有阻止自动滑动的解决方案