Is it possible to pass an onTouchEvent to multiple views in Android?

StartingGroovy picture StartingGroovy · Nov 18, 2011 · Viewed 14.9k times · Source

I have read a few questions regarding this topic on SO but haven't really found a solid answer to it.

I have a framelayout that I stack multiple custom views on, however the onTouch event only works with the top view. (the custom views are all the same view with the same onTouch event, just multiple of them)

FrameLayout

  • customView[2] <--- this is the last view added and the only one that receives the event
  • customView[1]
  • customView[0]

I'm testing it on Android 2.2 and am wondering if there is any way for the other views below to know where the touch happened?


EDIT (Adding some code)

I'm adding some code to hopefully help explain where I'm running into issues. At first I just automatically had the onTouchEvent return true. This made it so that the last view (in my case customerView[2]) would be the only one generating a value.

However, once I added the method to set the onTouchEvent to return true or false, now the only view returning a generated value is customView[0].

I hope this clears up what I am asking. I'm rather new to this and I appreciate you taking the time to explain it (and of course I appreciate your patience).

Also, I realize that my TextView's don't update with the value on each touchEvent, I'm working on fixing that.

My Activity:

public class MyActivity extend Activity {

    CustomView[] customView;
    TextView[] textView;
    int numViews 3;
    //FrameLayout and Params created

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        for(int i = 0; i < numViews; i++) {
            customView[i] = new CustomView(this, i);


            //Allows the onTouch to be handled by all Views - View[0] is the bottom view
            if(i == 0) {
                customView[i].setTouchBool(true);        //set view's onTouch to return true
            } else {
                customView[i].setTouchBool(false);       //set view's onTouch to return false
            }

            //Set TextView to display the number generated by the CustomView
            textView[i].setText(Double.toString(customView[i].getGeneratedNumber()));

            //Add views to main layout
            frame.addView(textView[i]);
            frame.addView(customView[i]);
        }
    }
}

My View:

public class CustomView extends View {

    boolean onTouchHandler = true;
    int xVal = 0, yVal = 0;
    int index;
    double generatedNum = 0;

    public CustomView(Context context) {
        this(context, 0);
        this.index = 0;
    }

    public CustomView(Context context, int index) {
        super(context);
        this.index = index;
    }


    @Override 
    public boolean onTouchEvent(MotionEvent ev) {
        final int action = ev.getAction();

        switch(action) {
            case MotionEvent.ACTION_DOWN: {
                //do logic 
            }

            case MotionEvent.ACTION_MOVE: {
                //do logic
            }

            case MotionEvent.ACTION_UP: {
                xVal = (int) ev.getX();
                yVal = (int) ev.getY();

                generateNumber(xVal, yVal, index);

                break; 
            }
        }
         return onTouchHandler;    
    }  


    private void generateNumber(int x, int y, int index) {
        if(index == 0) {
            generatedNum = (x / 2) * (y / 2) + 64;
        } else {
            generatedNum = (x / 2) * (y / 2) + (index * 128);
        }
    }

    public double getGeneratedNumber() {
        return generatedNum;
    }

    public boolean setTouchBool(boolean b) {
        this.onTouchHandler = b;
    }
}

Answer

DeeV picture DeeV · Nov 18, 2011

Android will cascade down the views calling onTouchEvent on each one until it receives a true from one of them. If you want a touch event to be handled by all of them, then return false until it reaches the last one.

EDIT:

Ok. If I understand correctly, you have a single top view containing a bunch of child views one layer deep. My original answer was assuming that you had three custom views that were on top of each other in the ViewGroup's hierarchy (View3 is a child of View2. View2 is a child of View1. View1 is a child of ParentView). You want the user's touch event on the parent view to get sent to all of it's children.

If that's the case, AFAIK, there is no view in Android's API that allows that. So, you'll have to make a custom view that does it.

OK, I haven't tested this, so please tell me if it works and if it's what you're trying. Create a custom class that extends whatever object frame is, then override the onTouch method like so.

@Override 
public boolean onTouchEvent(MotionEvent ev) {
   for(int i = 0; i < this.getChildCount(); i++){
      this.getChildAt(i).dispatchTouchEvent(ev);
   } 
   return true;
} 

Now, keep the same logic that your custom views have, except they should all return false because your parent view will not receive the onTouch event unless they do as stated in my previous answer

note: with this implementation, the child view that the user actually touches will fire twice because the logic will go fire child touch event -> return false -> fire parent touch event -> fire child touch event again