我有一个覆盖ViewGroup,它是我想用来在用户与应用程序交互时显示效果的屏幕大小,但仍然将onTouch事件传递给任何底层视图。
我对所有MotionEvents(不只是DOWN)感兴趣,因此onInterceptTouchEvent()不适用于此处,就好像我返回true我的叠加将消耗所有事件,如果为false则只接收DOWN事件(同样适用于onTouch )。
我以为我可以覆盖Activitys dispatchTouchEvent(MotionEvent ev)并在我的叠加层中调用自定义触摸事件,但这会导致根据我的视图位置不转换输入坐标(例如,所有事件都会通过似乎比实际触摸低20左右,因为系统条没有考虑在内。)
答案 0 :(得分:7)
我用一个非完美的解决方案解决了这个问题,但在这种情况下工作。
我创建了另一个ViewGroup
,其中包含一个可点击的子项,其宽度和高度设置为fill_parent
(这是我在添加效果之前的要求)并覆盖了onIntercept...()
。在onIntercept...
中,我将事件传递到我的覆盖视图到自定义onDoubleTouch(MotionEvent)
方法并在那里执行相关处理,而不会中断输入事件的平台路由并让底层视图组正常运行。
容易!
答案 1 :(得分:6)
构建此类拦截器的唯一方法是使用自定义ViewGroup布局。
但是在任何场合实施ViewGroup.onInterceptTouchEvent()都是不够的,因为子视图可以调用ViewParent.requestDisallowInterceptTouchEvent(),如果孩子这样做,你的视图将停止接收对interceptTouchEvent的调用(请参阅here获取例如,我小时候你可以这样做。)
这是我写的一个类,当我需要这样的东西时,它是自定义的FrameLayout,它可以完全控制任何委托者的触摸事件
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.FrameLayout;
/**
* A FrameLayout that allow setting a delegate for intercept touch event
*/
public class InterceptTouchFrameLayout extends FrameLayout {
private boolean mDisallowIntercept;
public interface OnInterceptTouchEventListener {
/**
* If disallowIntercept is true the touch event can't be stealed and the return value is ignored.
* @see android.view.ViewGroup#onInterceptTouchEvent(android.view.MotionEvent)
*/
boolean onInterceptTouchEvent(InterceptTouchFrameLayout view, MotionEvent ev, boolean disallowIntercept);
/**
* @see android.view.View#onTouchEvent(android.view.MotionEvent)
*/
boolean onTouchEvent(InterceptTouchFrameLayout view, MotionEvent event);
}
private static final class DummyInterceptTouchEventListener implements OnInterceptTouchEventListener {
@Override
public boolean onInterceptTouchEvent(InterceptTouchFrameLayout view, MotionEvent ev, boolean disallowIntercept) {
return false;
}
@Override
public boolean onTouchEvent(InterceptTouchFrameLayout view, MotionEvent event) {
return false;
}
}
private static final OnInterceptTouchEventListener DUMMY_LISTENER = new DummyInterceptTouchEventListener();
private OnInterceptTouchEventListener mInterceptTouchEventListener = DUMMY_LISTENER;
public InterceptTouchFrameLayout(Context context) {
super(context);
}
public InterceptTouchFrameLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public InterceptTouchFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public InterceptTouchFrameLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyle) {
super(context, attrs, defStyleAttr, defStyle);
}
@Override
public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
getParent().requestDisallowInterceptTouchEvent(disallowIntercept);
mDisallowIntercept = disallowIntercept;
}
public void setOnInterceptTouchEventListener(OnInterceptTouchEventListener interceptTouchEventListener) {
mInterceptTouchEventListener = interceptTouchEventListener != null ? interceptTouchEventListener : DUMMY_LISTENER;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
boolean stealTouchEvent = mInterceptTouchEventListener.onInterceptTouchEvent(this, ev, mDisallowIntercept);
return stealTouchEvent && !mDisallowIntercept || super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
boolean handled = mInterceptTouchEventListener.onTouchEvent(this, event);
return handled || super.onTouchEvent(event);
}
}
此类提供setInterceptTouchEventListener()
来设置自定义拦截器。
当需要禁止拦截触摸事件时,它会将其传递给父视图,但会一直拦截它们。 但是,它不会让侦听器再拦截事件,所以如果侦听器在拦截触摸事件时返回true,则会被忽略。
通过这种方式,您可以透明地接收通过ViewGroup的每个触摸事件,而不会破坏子视图行为。
答案 2 :(得分:2)
这就是我所做的,建立在Dori的答案上,我在onceptTouch上使用了它。 。 。
float xPre = 0;
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
if(Math.abs(xPre - event.getX()) < 40){
return false;
}
if(event.getAction() == MotionEvent.ACTION_DOWN || event.getAction() == MotionEvent.ACTION_UP || event.getAction() == MotionEvent.ACTION_MOVE){
xPre = event.getX();
}
return true;
}
基本上,当进行任何重要的动作时,通过xPre记住它发生的位置;然后,如果该点足够接近下一个要点,那么子视图的onClick / onTouch返回false并让onTouch执行默认操作。
答案 3 :(得分:1)
当触摸流从根视图开始到子视图并且在到达最底层的子视图之后,它(触摸)在遍历所有OnTouchListener时传播回根视图,如果子视图可以使用的操作返回true而false用于休息,那么对于您要拦截的操作,可以在这些父项的OnTouchListner下编写。
如果根视图A有两个子X和Y,并且您希望拦截A中的滑动触摸并希望将其余触摸传播到子视图(X和Y),则通过返回false来覆盖X和Y的OnTouchListner并且对于休息是真的。并覆盖A的OnTouchListner,滑动返回true,其余返回false。
答案 4 :(得分:0)
我尝试了上述答案中的所有解决方案,但仍然遇到了接收所有触摸事件的问题,同时仍允许儿童观看消费事件。在我的场景中,我想听听孩子们观看会消耗的事件。在重叠的子视图开始消耗事件后,我无法收到后续事件。
我终于找到了一个有效的解决方案。使用RxBinding库,您可以观察所有触摸事件,包括重叠子视图所消耗的事件。
Kotlin Code代码段
RxView.touches(view)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
object : Observer<MotionEvent> {
override fun onCompleted() {
}
override fun onNext(t: MotionEvent?) {
Log.d("motion event onNext $t")
}
override fun onError(e: Throwable?) {
}
}
)