Как различаться между дочерними и родительскими видами групповых сенсорных событий
Я решил опубликовать этот вопрос и ответить в ответе на этот комментарий на этот вопрос:
Как обрабатывать щелчок в дочерних представлениях и касание в родительских группах представлений?
Я вставлю комментарий сюда:
Предположим, что я хочу переопределить события касания только для обработки некоторых из них. дети, что я могу сделать внутри этой функции, чтобы она работала ? Я имею в виду, что для некоторых детей это будет работать, как обычно, а для некоторых, Родительский вид будет решать получат ли они сенсорные события или нет.
Итак, вопрос заключается в следующем: как я могу запретить родительскому onTouchEvent()
переопределять некоторые дочерние элементы onTouchEvent()
, в то время как он переопределяет другие дочерние элементы?
2 ответа:
onTouchEvents()
для вложенных групп представлений можно управлять с помощьюboolean
onInterceptTouchEvent .Значение по умолчанию для
OnInterceptTouchEvent
равно false.Родительский
onTouchEvent
принимается раньше дочернего. еслиOnInterceptTouchEvent
возвращает false, он отправляет событие движения вниз по цепочке обработчикуOnTouchEvent
ребенка. Если он возвращает true, Родительский обработает событие касания.Однако могут быть случаи, когда мы хотим, чтобы некоторые дочерние элементы управляли
OnTouchEvent
s и некоторые управляется родительским представлением (или, возможно, родителем родителя).Это может осуществляться в более чем одним способом.
- один из способов защиты дочернего элемента от родительского
OnInterceptTouchEvent
заключается в реализацииrequestDisallowInterceptTouchEvent .Public void requestDisallowInterceptTouchEvent (boolean disallowIntercept)
Это мешает любому из родительских представлений управлять
OnTouchEvent
для этого элемента, если элемент имеет обработчики событий включены.
- Если
OnInterceptTouchEvent
равно false, то будет вычислен дочерний элементOnTouchEvent
. Если у вас есть методы в дочерних элементах, обрабатывающие различные события касания, все связанные обработчики событий, которые отключены, вернут OnTouchEvent родительскому элементу.Этот ответ:
https://stackoverflow.com/a/13540006/3956566 дает хорошую визуализацию того, как проходит распространение сенсорных событий через:parent -> child|parent -> child|parent -> child views.
- другим способом является возврат различных значений из
OnInterceptTouchEvent
для родителя.Этот пример взят из управление сенсорными событиями в группе просмотра и демонстрирует, как перехватить
OnTouchEvent
ребенка, когда пользователь прокручивает.4а.
@Override public boolean onInterceptTouchEvent(MotionEvent ev) { /* * This method JUST determines whether we want to intercept the motion. * If we return true, onTouchEvent will be called and we do the actual * scrolling there. */ final int action = MotionEventCompat.getActionMasked(ev); // Always handle the case of the touch gesture being complete. if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) { // Release the scroll. mIsScrolling = false; return false; // Do not intercept touch event, let the child handle it } switch (action) { case MotionEvent.ACTION_MOVE: { if (mIsScrolling) { // We're currently scrolling, so yes, intercept the // touch event! return true; } // If the user has dragged her finger horizontally more than // the touch slop, start the scroll // left as an exercise for the reader final int xDiff = calculateDistanceX(ev); // Touch slop should be calculated using ViewConfiguration // constants. if (xDiff > mTouchSlop) { // Start scrolling! mIsScrolling = true; return true; } break; } ... } // In general, we don't want to intercept touch events. They should be // handled by the child view. return false; }
Edit: чтобы ответить на комментарии.
Это некоторый код из той же ссылки, показывающий, как создать параметры прямоугольника вокруг вашего элемента:
4b.// The hit rectangle for the ImageButton myButton.getHitRect(delegateArea); // Extend the touch area of the ImageButton beyond its bounds // on the right and bottom. delegateArea.right += 100; delegateArea.bottom += 100; // Instantiate a TouchDelegate. // "delegateArea" is the bounds in local coordinates of // the containing view to be mapped to the delegate view. // "myButton" is the child view that should receive motion // events. TouchDelegate touchDelegate = new TouchDelegate(delegateArea, myButton); // Sets the TouchDelegate on the parent view, such that touches // within the touch delegate bounds are routed to the child. if (View.class.isInstance(myButton.getParent())) { ((View) myButton.getParent()).setTouchDelegate(touchDelegate); }
Давайте исправим проблему.
У вас случайно есть группа просмотра с кучей детей. Вы хотите перехватить событие касания для всего, что находится в этой группе просмотра, за небольшим исключением некоторых детей.
Я уже давно ищу ответ на один и тот же вопрос. Не сумел найти ничего разумного и поэтому самостоятельно придумал следующее решение.Следующий фрагмент кода предоставляет обзор соответствующего кода ViewGroup, который перехватывает все прикосновения, за исключением тех, которые поступают из представлений, имеющих специальный набор тегов (вы должны установить его в другом месте кода).
private static int NO_INTERCEPTION; private boolean isWithinBounds(View view, MotionEvent ev) { int xPoint = Math.round(ev.getRawX()); int yPoint = Math.round(ev.getRawY()); int[] l = new int[2]; view.getLocationOnScreen(l); int x = l[0]; int y = l[1]; int w = view.getWidth(); int h = view.getHeight(); return !(xPoint < x || xPoint > x + w || yPoint < y || yPoint > y + h); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { for (int i=0; i<floatingMenuItems.getChildCount(); i++){ View child = floatingMenuItems.getChildAt(i); if (child == null || child.getTag(NO_INTERCEPTION) == null) { continue; } if(isWithinBounds(child, ev)){ return false; } } return true; }