How to detect when any child view recieves a click

2019-07-28 22:43发布

问题:

Given an arbitrary ViewGroup G with an arbitrary collection of child views, how can I detect when the user clicks on any of the child views? In this case, I want to draw a highlight for G.

I could add an onClick listener for each child, but I'm trying to avoid that so that the code doesn't have to be changed when the layouts change.

Alternatively, I could add onTouch handlers to G and set the highlight during ACTION_DOWN. However, this would trigger for actions that don't actually result in clicks, such as a swipe (the swipe could be handled by ViewPager, for example, and ultimately be irrelevant to G).

My layout for G has the focusable attributes:

android:focusable="true"
android:focusableInTouchMode="true"

Thanks.

回答1:

Here is how I do it:

//in onTouch method of parent, I get the coordinates of click
int x = ((int) motionEvent.getX());
int y = ((int) motionEvent.getY());

//obtain the clickable arrea of the child as HitRect
Rect clickRect = new Rect();
Rect rect = new Rect();
imageView.getHitRect(rect);

//ask if the area contains the coordinates of the click
if(rect.contains(x, y)){
    //do some work like if onClickListener on the child was called.
    return false; //you clicked here, don't need to handle other Childs
}

//ask for other childs like before...

Now, you can target the parent as the delegate of all clicks done inside it, even if it is done in a child.


EDIT:

To ignore other touch event that are not click, you can ask for how much user moved the finger:

case MotionEvent.ACTION_MOVE:
case MotionEvent.ACTION_CANCEL:
     if (Math.abs(motionEvent.getRawX() - initialTouchX) > 5 || Math.abs(motionEvent.getRawY() - initialTouchY) > 5) {
              return true; // user mover finger too much, ignore touch
     }
     return false; // finger still there waiting for click

I give a square of 10 pixels to permit a confortable click, and if you exit it, I ignore it.


EXTRA:

Here is the complete code for click and long click with onTouchListener.



回答2:

You could use the View.getChildCount() to loop through all child views and see if the touch intersects with the child view. This involves getting x and y positions and calculating if it fits within the child view, use View.getChildAt(position) to get the reference to the child view .

So it would be something like this:

int childNr = theView.getChildCount();
for (int i = 0; i < childNr; i++){
     YourView tmp = (YourView) theView.getChildAt(i);
     if(tmp.intersects(x, y)){
          do some work
     }
}

here you would have to put your view variable instead of theView and the class name which handles the views instead of (YourView) and x, y are the coordinates of the pressed spot.



回答3:

In your XML, you could add point all the children to the same onClick method. Inside that method you could draw the highlight to G and then do something (or nothing) for the individual child view.