我有以下类,它代表一个可触摸的视图并绘制一个滑动条。
public class SlideBar extends View {
private int progress;
private int max;
private Paint background;
private Paint upground;
private RectF bar;
private boolean firstDraw;
public SlideBar(Context context, AttributeSet attrs) {
super(context, attrs);
progress = 0;
upground = new Paint();
upground.setColor(Color.parseColor("#C2296C"));
background = new Paint();
background.setColor(Color.parseColor("#777777"));
}
private void onFirstDraw() {
max = getWidth();
bar = new RectF(0, 19, max, 21);
}
public void onDraw(Canvas canvas) {
if (!firstDraw) {
onFirstDraw();
progress = max;
firstDraw = true;
}
canvas.save();
canvas.drawRoundRect(bar, 5, 5, background);
canvas.drawCircle(progress, 20, 9, upground);
canvas.restore();
}
public void setValue(int value) {
progress = value;
}
public boolean onTouchEvent(MotionEvent evt) {
System.out.println(evt.getAction());
progress = (int) evt.getX();
invalidate();
return false;
}
}
但是当触摸并拖动它时,我收到一个 ACTION_DOWN,一些 ACTION_MOVE 然后收到一个 ACTION_CANCEL 并且没有进一步的事件。
为什么会发生这种情况?我不想取消该事件并使其继续拖动栏。
当父容器拦截您的触摸事件时,就会发生这种情况。任何覆盖 ViewGroup.onInterceptTouchEvent(MotionEvent) 的 ViewGroup 都可以做到这一点(例如 ScrollView 或 ListView)。
处理此问题的正确方法是在您认为需要保留运动事件时在父视图上调用 ViewParent.requestDisallowInterceptTouchEvent(boolean) 方法。
这是一个简单的示例(attemptClaimDrag 方法取自 Android 源代码):
/**
* Tries to claim the user's drag motion, and requests disallowing any
* ancestors from stealing events in the drag.
*/
private void attemptClaimDrag() {
//mParent = getParent();
if (mParent != null) {
mParent.requestDisallowInterceptTouchEvent(true);
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
if (iWantToKeepThisEventForMyself(event)) {
attemptClaimDrag();
}
//your logic here
} else {
//your logic here
}
}
当父视图接管其子视图之一时,就会发生
ACTION_CANCEL
。
查看有关 ViewGroup.onInterceptTouchEvent(MotionEvent) 方法的文档。来自链接:
onTouchEvent()
方法来处理;这意味着您应该实现 onTouchEvent()
以返回 true,这样您将继续看到手势的其余部分(而不是寻找父视图来处理它)。此外,通过从 onTouchEvent()
返回 true,您将不会在 onInterceptTouchEvent()
中收到任何后续事件,并且所有触摸处理都必须像平常一样在 onTouchEvent()
中进行。onTouchEvent()
。ACTION_CANCEL
,并且所有其他事件将传递到您的 onTouchEvent()
方法,并且不再出现在此处需要禁止父视图拦截触摸事件:
override fun dispatchTouchEvent(event: MotionEvent): Boolean {
when (event.action) {
MotionEvent.ACTION_DOWN -> {
(parent as? ViewGroup?)?.requestDisallowInterceptTouchEvent(true)
}
MotionEvent.ACTION_UP -> {
(parent as? ViewGroup?)?.requestDisallowInterceptTouchEvent(false)
}
else -> {
}
}
return true
}
触摸时 Android ACTION_CANCEL
另一种替代方式是: 您有
ViewGroup
,里面有 View
。
ViewGroup
的方法onInterceptTouchEvent()
对于false
返回ACTION_DOWN
,对于其他情况返回true
View
始终在 true
或 onTouch
中返回
onTouchEvent
ACTION_DOWN
,ACTION_MOVE
...结果你会看到下一个流程
ACTION_DOWN
迭代:
ViewGroup dispatchTouchEvent >start< ev = ACTION_DOWN
ViewGroup onInterceptTouchEvent false
View dispatchTouchEvent >start< ev = ACTION_DOWN
View onTouch true
View dispatchTouchEvent >finish< true
ViewGroup dispatchTouchEvent >finish< true
ACTION_MOVE
迭代:
ViewGroup dispatchTouchEvent >start< ev = ACTION_MOVE
ViewGroup onInterceptTouchEvent true //<- start intercepting
View dispatchTouchEvent >start< ev = ACTION_CANCEL //<- View is notified that control was intercepted
View onTouch true
View dispatchTouchEvent >finish< true
ViewGroup dispatchTouchEvent >finish< true
ACTION_MOVE
迭代:
ViewGroup dispatchTouchEvent >start< ev = ACTION_MOVE
ViewGroup dispatchTouchEvent >finish< false