4

I've set a View.OnTouchListener on a parent layout, but it doesn't appear to work.

Here is the XML for the UI:

<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>

Here is the OnTouchListener set on the parent layout:

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() is never called and the string never prints.

Not that I have implemented OnItemClick for the ListView and I just want to be able to detect touches on the whole layout.

I've also tried

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;
        }
    });

although this didn't work either.

Why is the parent layout not getting touch events? How can I get it working?

Paul Lammertsma
  • 37,593
  • 16
  • 136
  • 187
Hong Wei Wang
  • 1,388
  • 3
  • 19
  • 29
  • Is there any part of the LinearLayout exposed? If a ListView is on top of it, and handle's the click, your LinearLayout won't get it. – MikeHelland Jan 21 '14 at 01:02
  • From your code, I see that a layout is filled with 2 dp gray and then all listview, meaning that there is no space of parent layout that you can actually touch. You may want to add padding to your parent so that you can capture touch events – Can Gokdere Jan 21 '14 at 01:06
  • @MazeHatter I see, yes the listview is on top of the whole layout. Just wonder if there are anyway to make it so that LinearLayout can catch the on touch event even if it is under it. – Hong Wei Wang Jan 21 '14 at 01:07
  • You could place a transparent View over everything, and get the click/touch coordinates from that. – MikeHelland Jan 21 '14 at 01:08
  • Or make list.setClickable(false), then I think the view underneath could get it – MikeHelland Jan 21 '14 at 01:10
  • Also is it possible to set the on touch on listview? I tried that and it didn't work either. – Hong Wei Wang Jan 21 '14 at 01:10
  • @ MazeHatter I would like the listview to be clickable. – Hong Wei Wang Jan 21 '14 at 01:11

3 Answers3

2

By returning true in onTouch(), you consume the touch event; the parent view's OnTouchListener will not be invoked. The documentation states:

public abstract boolean onTouch (View v, MotionEvent event)

[...]

Returns
True if the listener has consumed the event, false otherwise.

You may instead be interested in having the parent ViewGroup intercept touch events from its children through onInterceptTouchEvent(), as detailed in this guide.

Paul Lammertsma
  • 37,593
  • 16
  • 136
  • 187
  • I had the same problem. I had a container and a EditText that covered all of the container. I extended EditText and overrided onTouchEvent, I called the parent but always returned false.So i let the normal EditText event work as usual. (like scrolling up and down) and also my custom gesture detector in container added to it. (in my case swipe left and right) Only one problem and that was really bad performance! When i do this scrolling up and down is slow but swiping (defined for my container) works great. Any idea why it act this way? – Alireza Ahmadi Aug 04 '14 at 15:17
  • I solved my performance problem by moving the logic for swipe detection from container eventListener to my custom EditText. So in onTouchEvent method i send one copy of event to my gesture detector and another copy to supper method. Then i use an interface for communication between my EditText and activity so when swipe left or right occurs edit text tell activity about it. Even though my solution works just fine i still prefer the first approach you mentioned. BTW thank you so much for your answer!! I was stocked for hours! It helped a lot. – Alireza Ahmadi Aug 04 '14 at 15:25
1

I encountered same problem just now. I think it's because the touch event never reaches LinearLayout. What I did to solve this problem is to intercept the touch event for a cutom view

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;
    }
}

when you want to catch the touch, return true. if return false, touch event will pass down to children views.

Hope this helps for whoever searching for it.

EDIT: You can also try to add attribute android:clickable="true" for parent layout. This may affect the clicking on child views though (I use it in some cases but not sure it works for all)

xialin
  • 7,686
  • 9
  • 35
  • 66
  • Does this work? I can't even add this Override function in my activity. – Rethinavel Mar 02 '15 at 07:35
  • @RethinavelPillai you should have this inside the view. in my case, it's a ViewPager – xialin Mar 03 '15 at 03:39
  • Would you please share some code. It would be really helpful. – Rethinavel Mar 03 '15 at 06:13
  • @RethinavelPillai I updated my answer. basically, the onInterceptTouchEvent works for any view class. if you really want it, create a CustomLinearLayout that extends the LinearLayout class, and add that method. in your layout xml, replace LinearLayout with your CustomLinearLayout. I've added another solution, see if that serves your purpose – xialin Mar 04 '15 at 05:46
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.

grebulon
  • 7,697
  • 5
  • 42
  • 66
Surender Kumar
  • 1,123
  • 8
  • 15