Подтвердить что ты не робот

MotionEvent.ACTION_UP не вызван

Рассмотрим следующую схему ниже (для лучшего понимания моей проблемы). enter image description here

Как видите, я рассматриваю вид списка, окруженный отступами. Теперь, если пользователь нажимает на элемент списка, в качестве действия, которое я предоставил, это светло-синий цвет фона. Теперь мое приложение имеет дело с событиями onTouch, чтобы определить такие действия, как

  • Нажмите
  • Размах слева направо
  • Справа налево Проведите

Вот мой код

public boolean onTouch(View v, MotionEvent event) {
        if(v == null)
        {
            mSwipeDetected = Action.None;
            return false;
        }
        switch (event.getActionMasked()) {
        case MotionEvent.ACTION_DOWN: {
            downX = event.getRawX();
            downY = event.getRawY();
            mSwipeDetected = Action.Start;

         // Find the child view that was touched (perform a hit test)
            Rect rect = new Rect();
            int childCount = listView.getChildCount();
            int[] listViewCoords = new int[2];
            listView.getLocationOnScreen(listViewCoords);
            int x = (int) event.getRawX() - listViewCoords[0];
            int y = (int) event.getRawY() - listViewCoords[1];
            View child;
            for (int i = 0; i < childCount; i++) {
                child = listView.getChildAt(i);
                child.getHitRect(rect);
                if (rect.contains(x, y)) {
                    mDownView = child;
                    break;
                }
            }


            return false; // allow other events like Click to be processed
        }
        case MotionEvent.ACTION_MOVE: {
            upX = event.getRawX();
            upY = event.getRawY();
            float deltaX=0,deltaY=0;
             deltaX = downX - upX;
             deltaY = downY - upY;

                if(deltaY < VERTICAL_MIN_DISTANCE)
                {
                            setTranslationX(mDownView, -(deltaX));
                            setAlpha(mDownView, Math.max(0f, Math.min(1f, 1f - 2f * Math.abs(deltaX) / listView.getWidth())));
                            return false;
                }
                else
                {
                    forceBringBack(v);
                }

                          return false;              

        }
        case MotionEvent.ACTION_UP:
        {

             stopX = event.getX();
             float stopValueY = event.getRawY() - downY;             
             float stopValue = stopX - downX;

             if(!mDownView.isPressed())
             {
                 forceBringBack(mDownView);
                 return false;
             }             

             boolean dismiss = false;
             boolean dismissRight = false;


             if(Math.abs(stopValue)<10)
             {
                 mSwipeDetected = Action.Start;
             }
             else
             {
                 mSwipeDetected = Action.None;

             }
             String log = "";
             Log.d(log, "Here is Y" + Math.abs(stopValueY));
             Log.d(log, "First Comparison of Stop Value > with/4" + (Math.abs(stopValue) > (listView.getWidth() /4)));
             Log.d(log, "Second Comparison " + (Math.abs(stopValueY)<VERTICAL_MIN_DISTANCE));
             Log.d(log, "Action Detected is " + mSwipeDetected + " with Stop Value  " + stopValue);

             if((Math.abs(stopValue) > (listView.getWidth() /4))&&(Math.abs(stopValueY)<VERTICAL_MIN_DISTANCE))
             {
                 dismiss = true;
                 dismissRight = stopValue > 0;

                 if(stopValue>0)
                 {
                 mSwipeDetected = Action.LR;

                 }
                 else
                     mSwipeDetected = Action.RL;
             }
             Log.d(log, "Action Detected is " + mSwipeDetected + " with Stop Value after dissmiss" + stopValue);

             if(dismiss)
             {
                 if(dismissRight)
                     mSwipeDetected = Action.LR;
                 else
                     mSwipeDetected = Action.RL;
                 animate(mDownView)
                 .translationX(dismissRight ? listView.getWidth() : - listView.getWidth())
                 .alpha(0)
                 .setDuration(mAnimationTime)
                 .setListener(new AnimatorListenerAdapter() {
                     public void onAnimationEnd(Animator animation)
                     {

                     }
                });
             }
             else
             {
                 animate(mDownView)
                 .translationX(0)
                 .alpha(1)
                 .setDuration(mAnimationTime)
                 .setListener(null);
             }


             break;           

        }
        }
        return false;
    }

Как видите, я определяю выполненное действие в MotionEvent.ACTION_UP и соответствующим образом устанавливаю значение Enum Action. Эта логика работает как талисман, если пользователь не пересекает границу представления списка.

Теперь, если пользователь, двигая (или, в частности), перемещая палец по элементу списка, переходит от синего к оранжевому, MotionEvent.ACTION_UP не будет передан listview, что заставляет мой код не принимать решение и из-за translationX() и setAlpha(), поскольку в этом случае действие не определено, этот конкретный элемент списка становится пустым.

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

Можно ли что-нибудь сделать, чтобы, даже если бы я не столкнулся с MotionEvent.ACTION_UP, я все же мог принять какое-то решение?

Благодарю.

4b9b3361

Ответ 1

Вы должны return true; в case MotionEvent.ACTION_DOWN:, поэтому обрабатывается MotionEvent.ACTION_UP.


Как объяснено на View.OnTouchListener:

Возвращает

Истина, если слушатель потребляет событие, в противном случае - false.

MotionEvent.ACTION_UP Не будет вызван до тех пор, пока не произойдет MotionEvent.ACTION_DOWN, логическим объяснением этого является невозможность возникновения ACTION_UP, если ACTION_DOWN никогда не происходило до него.

Эта логика позволяет разработчику блокировать дальнейшие события после ACTION_DOWN.

Ответ 2

Также обратите внимание, что при определенных обстоятельствах (например, поворот экрана) жест может быть отменен, и в этом случае MotionEvent.ACTION_UP НЕ будет отправлено. Вместо этого MotionEvent.ACTION_CANCEL. Поэтому нормальный оператор switch действия должен выглядеть примерно так:

switch (event.getActionMasked()) {
    case MotionEvent.ACTION_DOWN:
        // check if we want to handle touch events, return true
        // else don't handle further touch events, return false
    break;

    // ... handle other cases

    case MotionEvent.ACTION_UP:
    case MotionEvent.ACTION_CANCEL:
        // finish handling touch events
        // note that these methods won't be called if 'false' was returned
        // from any previous events related to the gesture
    break;
}

Ответ 3

Я не думаю, что добавление return true; на случай MotionEvent.ACTION_DOWN: в конечном итоге решит проблему. Это просто усложнило ситуацию, когда return false могло бы выполнить работу как обаяние.

На что обратить внимание: MotionEvent.ACTION_DOWN:/*something*/return true; заблокирует любые другие обратные вызовы Listener, доступные для представления, даже onClickListenerm, в то время как правильно return false в MotionEvent.ACTION_UP: может помочь распространению MotionEvent к правильному месту назначения.

Ссылка на его исходный код: https://github.com/romannurik/android-swipetodismiss

Ответ 4

Как объяснил Данпе в своем кратком ответе - мне пришлось добавить код ACTION_DOWN, чтобы распознать ACTION_UP.

            case MotionEvent.ACTION_DOWN:

                return true;

            case MotionEvent.ACTION_UP:

                XyPos xyPos = new XyPos();
                xyPos.x = last_x;
                xyPos.y = last_y;
                handleViewElementPositionUpdate(xyPos);

                break;

У меня был весь метод onTouch (..), возвращающий true в любом случае, поэтому я не уверен, почему этого было недостаточно... но приятно иметь это быстрое решение.. (спасибо!)