OnInterceptTouchEvent, onTouchEvent только видеть ACTION_DOWN

У меня есть ViewGroup верхнего уровня, который я называю SliderView, в котором я хочу обнаружить прокрутку. Это в основном работает, но один странный отказ сохраняется.

Суть SliderView заключается в том, чтобы переопределить onInterceptTouchEvent и, как только пользователь на самом деле будет прокручивать, верните "true", чтобы другие представления не могли отображать MotionEvent. Вот фрагмент кода:

public class SliderView extends ViewGroup
{
  enum MoveState { MS_NONE, MS_HSCROLL, MS_VSCROLL };
  private MoveState moveState = MoveState.MS_NONE;

  ... other code ...

  public boolean onInterceptTouchEvent(MotionEvent e)
  {
    final int action = e.getAction();
    switch (action & MotionEvent.ACTION_MASK)
    {
      case MotionEvent.ACTION_DOWN:
        moveState = MoveState.MS_NONE;
        break;

      case MotionEvent.ACTION_MOVE:
        if (moveState == MoveState.MS_NONE)
        {
          if (motion is horizontal)
          {
            moveState = MoveState.MS_VSCROLL;
            return true;
          }
          else
            moveState = MoveState.MS_VSCROLL; // let child window handl MotionEvent
        }
        else if (moveState == MoveState.MS_HSCROLL)
          return true; // don't let children see motion event.
    }
    return super.onInterceptTouchEvent (e);
  }

  ... other code ...
}

Насколько я понимаю, мой SliderView (который является самым внешним видом) всегда должен получать onInterceptTouchEvent. В одном из моих тестов, где дочерний уровень верхнего уровня есть, однако в следующем случае это не так.

Когда дочерний элемент верхнего уровня является ScrollView, onInterceptTouchEvent получает ACTION_MOVE, а мой код делает то, что я хочу. В другом случае, когда дочерний элемент верхнего уровня является LinearLayout, иногда он прерывается: он всегда получает ACTION_DOWN, но получает ACTION_MOVE только в том случае, если пользователь касается виджета внутри LinearLayout; при касании пустой области происходит только ACTION_DOWN.

Я буду замечать, что он ведет себя так, как будто проблемы с отказом происходят вне SliderView. Однако, если это так, зачем мне получать события ACTION_DOWN?

Второе примечание: глядя на исходный код ScrollView, я вижу, что он проверяет "inChild"; Я не понял, для чего это и как это может быть важно.

Ответ 1

Из-за ответа user123321 здесь

onInterceptTouchEvent только вызывается, если у родителя есть дочернее представление, которое возвращает "true" из onTouchEvent. Когда дочерний объект возвращает true, родитель теперь имеет возможность перехватить это событие

Ответ 2

Из ссылки разработчика Android (http://developer.android.com/reference/android/view/ViewGroup.html#onInterceptTouchEvent(android.view.MotionEvent)):

"2. .... Также, вернув true из onTouchEvent(), вы не получите никаких следующих событий в onInterceptTouchEvent(), и вся обработка касания должна произойти в onTouchEvent(), как обычно. "

Может быть, потому что ваш onTouchEvent всегда возвращает true..?

Ответ 3

Вам нужно только позвонить

requestDisallowInterceptTouchEvent(true);

на родительском представлении, например:

        @Override
        public boolean onTouch(View view, MotionEvent motionEvent) {
            view.getParent().requestDisallowInterceptTouchEvent(true);
            switch(motionEvent.getActio){
            }

            return false; 

         }

Ответ 4

При перехвате onTouchEvent есть две возможности для правильного перехвата касаний (все остальные по умолчанию).

  • Возвращает false в onInterceptTouchEvent()

    @Override
    public boolean onInterceptTouchEvent(MotionEvent me) {
        return false;
    }
    
  • Возвращает true в onTouchEvent()

    @Override
    public boolean onTouchEvent(MotionEvent me) {
    
        switch (me.getAction()) {
        case MotionEvent.ACTION_DOWN:
            log("MotionEvent.ACTION_DONE");
            break;
        case MotionEvent.ACTION_MOVE:
            log("MotionEvent.ACTION_MOVE");
            break;
        case MotionEvent.ACTION_CANCEL:
            log("MotionEvent.ACTION_CANCEL");
            userActionDown = false;
            break;
        case MotionEvent.ACTION_UP:
            log("MotionEvent.ACTION_UP");
            break;
        }
    
        return true;
    }
    
  • Затем для вашего случая (и других). Выполняйте все ваши вычисления в onTouchEvent(), как показано выше. OnInterceptTouchEvent() будет вызываться только один раз для ACTION_DOWN. Но, onTouchEvent также получит событие ACTION_DOWN, и вам нужно вернуть true, а не супер.

Подробнее о onInterceptTouchEvent(): http://developer.android.com/reference/android/view/ViewGroup.html#onInterceptTouchEvent(android.view.MotionEvent)

ps - Когда вы задаете вопросы здесь, вы также должны написать описание того, что вы пытаетесь сделать. Возможно, вы, возможно, найдете гораздо лучшие способы сделать что-то. Для вашего случая навигации реальный ответ, который вы ищете, ViewPager. Он отлично работает и очень прост в реализации. Вы также должны ознакомиться с некоторыми другими удобными навигационными патчами, которые Android может предложить разработчикам: ссылка.