View.OnTouchListener()在父布局上无法工作

4
我在父布局上设置了一个View.OnTouchListener,但似乎并没有起作用。
下面是UI的XML代码:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:baselineAligned="false"
    android:orientation="vertical"
    android:id="@+id/mainActivityLinearLayout"
    android:background="@drawable/listviewborder"
    tools:context=".MainActivity" >

    <View
        android:id="@+id/lineView"
        android:layout_width="match_parent"
        android:layout_height="2dp"
        android:background="@color/gray" />

    <ListView
        android:id="@+id/gameListView"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="2"
        android:dividerHeight="2dp" />

</LinearLayout>

这里是设置在父布局上的OnTouchListener:

mainActivityView = this.findViewById(R.id.mainActivityLinearLayout);
mainActivityView.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            System.out.println("Touch test activity");
            return true;
        }
    });

onTouch()方法从未被调用,字符串也未打印出来。

我已经为ListView实现了OnItemClick,现在想要检测整个布局的触摸事件。

我也尝试过:

gameListView = (ListView) findViewById(R.id.gameListView);
gameListView.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            System.out.println("Touch test list");
            return true;
        }
    });

尽管这样也没有起作用。 为什么父布局没有触摸事件?我应该如何让它工作?

LinearLayout 的任何部分都暴露出来了吗?如果一个 ListView 在它的上面,并且处理了点击事件,那么你的 LinearLayout 就得不到它了。 - MikeHelland
从你的代码中,我看到一个布局被填充了2 dp的灰色,然后所有的listview都在上面,这意味着没有父布局的空间可以触摸。你可能想给你的父布局添加padding,这样你就可以捕获触摸事件了。 - Can Gokdere
@MazeHatter 我明白了,是的,ListView在整个布局的顶部。只是想知道是否有办法使LinearLayout即使在其下面,也能捕获on touch事件。 - Hong Wei Wang
你可以在所有内容上放置一个透明视图,并从其中获取点击/触摸坐标。 - MikeHelland
或者将list.setClickable(false),那么我认为下面的视图就可以获取它了。 - MikeHelland
显示剩余2条评论
3个回答

2
onTouch()中返回true,可以消耗触摸事件; 父视图的OnTouchListener将不会被调用。文档说明如下:(链接)

public abstract boolean onTouch (View v, MotionEvent event)

[...]

返回值
如果监听器已经消耗了事件,则返回true,否则返回false。

您也可以通过onInterceptTouchEvent()让父ViewGroup拦截其子View的触摸事件,详见此指南

我曾经遇到过同样的问题。我有一个容器和一个EditText,它们覆盖了整个容器。我扩展了EditText并重写了onTouchEvent方法,我调用了父类但总是返回false。所以我让普通的EditText事件像往常一样工作(比如上下滚动),同时在容器中添加了我的自定义手势检测器(在我的情况下是左右滑动)。唯一的问题就是性能非常差!当我这样做时,上下滚动变得很慢,但是滑动(为我的容器定义)效果很好。有什么想法为什么会出现这种情况吗? - Alireza Ahmadi
我将滑动检测逻辑从容器事件监听器移动到我的自定义EditText中,解决了性能问题。因此,在onTouchEvent方法中,我发送一个事件副本到手势检测器和另一个事件副本到super方法。然后,我使用一个接口在我的EditText和Activity之间进行通信,当发生左右滑动时,EditText会告诉Activity。尽管我的解决方案完全有效,但我仍然更喜欢你提到的第一种方法。顺便说一句,非常感谢你的答复!我陷入困境好几个小时了!它帮了我很多。 - Alireza Ahmadi

1
  1. Make this class to detect gesture listener

       class MyGestureDetector extends SimpleOnGestureListener {
    
        @Override
        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
                float velocityY) {
            try {
                if (Math.abs(e1.getY() - e2.getY()) > SWIPE_MAX_OFF_PATH)
                    return false;
                // right to left swipe
                if (e1.getX() - e2.getX() > SWIPE_MIN_DISTANCE
                        && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
                    Log.d("asdsa", "left");
                    // Toast.makeText(SelectFilterActivity.this, "Left Swipe",
                    // Toast.LENGTH_SHORT).show();
                } else if (e2.getX() - e1.getX() > SWIPE_MIN_DISTANCE
                        && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
                    Log.d("asdsa", "right");
                    Fragment fr = getActivity().getSupportFragmentManager()
                            .findFragmentByTag("list_items");
                    if (fr != null) {
                        Log.d("asdsa", "not null");
                        if (fr.isVisible()) {
                            Log.d("asdsa", "visible");
                            getActivity().getSupportFragmentManager()
                                    .beginTransaction()
                                    .setCustomAnimations(0, R.anim.right_out)
                                    .remove(fr).commit();
                        }
                    }
                    // Toast.makeText(SelectFilterActivity.this, "Right Swipe",
                    // Toast.LENGTH_SHORT).show();
                }
            } catch (Exception e) {
                // nothing
            }
    
            return false;
        }
    
  2. Declare this variables in activity:

    private static final int SWIPE_MIN_DISTANCE = 120;
    
    private static final int SWIPE_MAX_OFF_PATH = 250;
    private static final int SWIPE_THRESHOLD_VELOCITY = 200;
    private GestureDetector gestureDetector;
    View.OnTouchListener gestureListener;
    
  3. Now inside your activity onCreate do this:

    gestureDetector = new GestureDetector(getActivity(), new MyGestureDetector());
    gestureListener = new View.OnTouchListener() {
        public boolean onTouch(View v, MotionEvent event) {
            return gestureDetector.onTouchEvent(event);
        }
    };
    listView.setOnTouchListener(gestureListener);
    
  4. Finally check the gestures if occurring or not , for me it worked perfect.


1

我刚遇到了同样的问题。 我认为这是因为触摸事件从未到达LinearLayout。 为了解决这个问题,我拦截了自定义视图的触摸事件。

public class MyPager extends ViewPager {

    public RecentsPager(Context context) {
        super(context);
    }

    public RecentsPager(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        // Do not allow swiping to switch between child pager pages
        return false;
    }
}

当你想要捕获触摸事件时,返回true。如果返回false,则触摸事件将传递给子视图。

希望这对正在寻找答案的人有所帮助。

编辑: 您还可以尝试为父布局添加属性android:clickable="true"。但这可能会影响到子视图的点击(我在某些情况下使用它,但不确定它是否适用于所有情况)


这个能用吗?我甚至无法在我的活动中添加这个覆盖函数。 - Rethinavel
@RethinavelPillai 你应该将这个放在视图内部。在我的情况下,它是一个ViewPager。 - xialin
请问您能否分享一些代码?这将非常有帮助。 - Rethinavel
@RethinavelPillai 我更新了我的答案。基本上,onInterceptTouchEvent适用于任何视图类。如果你真的想要它,创建一个扩展LinearLayout类的CustomLinearLayout,并添加该方法。在你的布局xml中,用CustomLinearLayout替换LinearLayout。我添加了另一种解决方案,看看是否符合你的目的。 - xialin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接