Passing touch events to the parent view
Asked Answered
K

9

40

I have a custom ViewSwitcher in which I implemented touch events so I am able to scroll through screens using the touchscreen.

My layout hierarchy looks like this:

<ViewSwitcher>

    <LinearLayout>
        <ListView />
    </LinearLayout>

    <LinearLayout>
        <ListView />
    </LinearLayout>

</ViewSwitcher>

Now, the problem is that the touch events are being consumed by the ListViews and I am not able to switch the views. It works fine when I don't have the ListViews. I need to be able to scroll through the views and scroll the ListView.

How do I solve this?

EDIT: I also need the ListView items to be clickable.

Thanks in advance!

Kook answered 16/6, 2011 at 15:40 Comment(0)
K
84

Thank you everyone for answering the question. But I was able to figure it out in a much simpler manner. Since my ViewSwitcher wasn't detecting the touch event, I intercepted the touch event, called the onTouchEvent() and returned false. Here:

@Override
public boolean onInterceptTouchEvent(MotionEvent ev)
{
    onTouchEvent(ev);
    return false;
}

By overriding the onInterceptTouchEvent(), I was able to intercept the touch event in the activity. Then I called the onTouchEvent() in the ViewSwitcher which handles the switching of the ListViews. And finally by returning false, it makes sure that the ViewGroup doesn't consume the event.

Kook answered 17/6, 2011 at 10:28 Comment(6)
suppose i want to have a horizontalScrollView inside cells of the gridView, how come using this solution doesn't work well on this case, and allow me to do both scrolling horizontally and click/long click on cells of the gridview ?Litigable
This helped me solve a ViewPager problem, thought I'd throw this out for posterity. I have views inside a ViewPager that have to have OnClickListener/OnTouchListener s attached to them. These consume motion events resulting in the parent ViewPager not scrolling. By applying this trick to the layout that contains the view pager I can forward all touch events to the pager (scroller) regardless of whether or not the child consumes the event as a click.Warchaw
I don't think it is the "official" way to call onTouchEvent() in onInterceptTouchEvent() unless this is meant as some kind of hack. Because onTouchEvent() will be called anyway if you return true from onInterceptTouchEvent().Nekton
More information about this answer (done the correct way) here: developer.android.com/training/gestures/viewgroup.htmlSarette
After hours of trial and error with other solutions, I found this one, and it worked in my case. A horizontalscrollview and a parent linearlayout where the linearlayout wasn't receiving touch eventsSignally
@Srichand Where exactly did you override onInterceptTouchEvent() and OnTouchEvent() methods? In ListViewRenderer or ViewSwitcherRenderer?Ketosis
D
18

The simplest way to pass child view's TouchEvent to parent view is by adding this to child view:

@Override
public boolean onTouchEvent(MotionEvent event) { 
    super.onTouchEvent(event);
    return false;
}
Deodorize answered 3/9, 2014 at 18:2 Comment(2)
This is the only solution that worked for me for the RecyclerView. Looks like you have to extend RecyclerView, override onTouchEvent and that disables any further interactions on it and the event chain continues.Every
When I add this to my recycler view then list item dont take click eventsMarkson
G
6

In my parent layout, the only way I have found to prevent the child from capturing the touch event is by overriding onInterceptTouchEvent to return true.

@Override
public boolean onInterceptTouchEvent(MotionEvent ev)
{
    return true;
}
Gail answered 9/5, 2017 at 22:44 Comment(0)
Z
1

I don't think there is an easy way for you to do this.

It's not that complicated, but you will need to create your own view that extends the ListView. Then you can override the onTouch handler and decide (depending on the touch event) whether you want to handle it (and return true) or pass it down to the parent View.

The problem also is that once a View handles a touch event, it is the one that will get all the remaining ones...

From the Android documentation :

onTouch() - This returns a boolean to indicate whether your listener consumes this event. The important thing is that this event can have multiple actions that follow each other. So, if you return false when the down action event is received, you indicate that you have not consumed the event and are also not interested in subsequent actions from this event. Thus, you will not be called for any other actions within the event, such as a finger gesture, or the eventual up action event

So, for example, if you want to have vertical move to scroll through the list and during the same touch event (without lifting your finger), you want horizontal move to switch the views, that's going to be quite challenging.

But if you can use gestures for example or handle everything in your custom view and depending on what the MotionEvent is, send commands to the ViewSwitcher.

Zsa answered 16/6, 2011 at 17:52 Comment(0)
R
0

Did you try setting the ListView items as non-clickable like this: listView.setClickable(false); This should propogate the click event upwards.

Retina answered 16/6, 2011 at 15:56 Comment(1)
That didnot fix it. And also I need the items of the listview to be clickable.Kook
J
0

If your view wants to pass the event up, make sure you return false in onTouchEvent. Otherwise, the platform thinks you consumed the event and no further processing is needed.

Jarl answered 16/6, 2011 at 17:46 Comment(0)
E
0

what i did is to set a toucheventlistener on the listview in the viewswitcher, handles the event, detects the fling action and call the metchod of viewswitcher. it works.

Epizoon answered 22/4, 2013 at 22:31 Comment(0)
B
0

You could also insert a call to handle the touchevent in dispatchTouchEvent, but in this case you have also to override onTouchEvent to return true, otherwise only the first MotionEvent DOWN of the gesture will be passed.

This is the touchable wrapper container:

<?xml version="1.0" encoding="utf-8"?>
<view xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/pager"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    class="com.example.myapp.FragmentContainer$TouchableWrapper" />

And the class:

public static class TouchableWrapper extends FrameLayout {
    private GestureDetector gestureDetector;
    public void setGestureDetector(GestureDetector gestureDetector) {
        this.gestureDetector = gestureDetector;
    }

    // these constructors for the XML inflater
    public TouchableWrapper(Context context) {
        super(context);
    }
    public TouchableWrapper(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    public TouchableWrapper(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        Log.d(TAG, "onInterceptTouchEvent " + event.toString());
        return false; // true for intercept, false è default and pass on to children View
    }
    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        Log.d(TAG, "dispatchTouchEvent " + event.toString());
        gestureDetector.onTouchEvent(event);
        return super.dispatchTouchEvent(event);
    }
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.d(TAG, "onTouchEvent " + event.toString());
        return true; //return super.onTouchEvent(event); 
    }
}

This is the GestureDetector reference:

private GestureDetector gestureDetector;

This the GestureListener:

private GestureDetector.SimpleOnGestureListener sOGL = new GestureDetector.SimpleOnGestureListener() {
    private static final int SWIPE_THRESHOLD = 100;
    private static final int SWIPE_VELOCITY_THRESHOLD = 100;

    @Override
    public boolean onDown(MotionEvent e) {
        return true;
    }

    @Override
    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
        boolean result = false;
        try {
            float diffY = e2.getY() - e1.getY();
            float diffX = e2.getX() - e1.getX();
            if (Math.abs(diffX) > Math.abs(diffY)) {
                if (Math.abs(diffX) > SWIPE_THRESHOLD && Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) {
                    if (diffX > 0) {
                        goToRight(); // onSwipeRight();
                    } else {
                        goToLeft(); // onSwipeLeft();
                    }
                }
                result = true;
            } else if (Math.abs(diffY) > SWIPE_THRESHOLD && Math.abs(velocityY) > SWIPE_VELOCITY_THRESHOLD) {
                if (diffY > 0) {
                    // onSwipeBottom();
                } else {
                    // onSwipeTop();
                }
            }
            result = true;

        } catch (Exception exception) {
            exception.printStackTrace();
        }
        return result; // return false indicate event not handled
    }
};

Ant this to load the touchable container fragment and a contained fragment:

public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    Log.d(TAG, "onCreateView()");
    View view = inflater.inflate(R.layout.fragmentcontainer, container, false);

    gestureDetector = new GestureDetector(view.getContext(), sOGL);
    ((FragmentContainer.TouchableWrapper) view).setGestureDetector(gestureDetector);

    FragmentManager fm = this.getFragmentManager();
    FragmentTransaction ft = fm.beginTransaction();
    ft.replace(R.id.pager, frag).commit();

    return view;
}
Budbudapest answered 6/8, 2015 at 8:23 Comment(0)
C
0

It was necessary to handle the touch in the TextView in the parent view with the autoLink = "three" Did so:

private LinearLayout mParentView;
private TextView mTextView;
private View.OnTouchListener mParentListener = new OnTouchListener() {

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        ......
        return false;
    }
};
mParentView.setOnTouchListener(mParentListener);

mTextView.setOnTouchListener(new View.OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        mParentListener.onTouch(mParentView, event);
        return false;
    }
};
Chelyuskin answered 30/5, 2017 at 7:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.