ACTION_UP не работает, но ACTION_DOWN работает нормально

Я учился (медленно), как программировать игры для Android, используя Java и Android Studio. Моим основным источником обучения были учебные пособия, расположенные на учебном веб-сайте Kilobolt. . Я нашел их чрезвычайно полезными, но поскольку они представляют собой учебные пособия по Android ADT для Eclipse, и им уже 3 года, мне пришлось много учиться в каждом разделе, чтобы изучить Android Studio (огромный процесс).

Я обнаружил, что в основном логика в учебнике была надежной. Моя проблема в том, что в классе SingleTouchHandler платформы, предоставляемой Kilobolt, ACTION_UP, похоже, не работает. Вот входной класс:

public interface Input {

public static class TouchEvent {
    public static final int TOUCH_DOWN = 0;
    public static final int TOUCH_UP = 1;
    public static final int TOUCH_DRAGGED = 2;
    public static final int TOUCH_HOLD = 3;

    public int type;
    public int x, y;
    public int pointer;

}

public boolean isTouchDown(int pointer);

public int getTouchX(int pointer);

public int getTouchY(int pointer);

public List<TouchEvent> getTouchEvents();

}

и вот его реализация:

public class SingleTouchHandler implements TouchHandler {
boolean isTouched;
int touchX;
int touchY;
Pool<TouchEvent> touchEventPool;
List<TouchEvent> touchEvents = new ArrayList<TouchEvent>();
List<TouchEvent> touchEventsBuffer = new ArrayList<TouchEvent>();
float scaleX;
float scaleY;

public SingleTouchHandler(View view, float scaleX, float scaleY) {
    PoolObjectFactory<TouchEvent> factory = new PoolObjectFactory<TouchEvent>() {
        @Override
        public TouchEvent createObject() {
            return new TouchEvent();
        }
    };
    touchEventPool = new Pool<TouchEvent>(factory, 100);
    view.setOnTouchListener(this);

    this.scaleX = scaleX;
    this.scaleY = scaleY;
}

@Override
public boolean onTouch(View v, MotionEvent event) {
    synchronized(this) {
        TouchEvent touchEvent = touchEventPool.newObject();
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                touchEvent.type = TouchEvent.TOUCH_DOWN;
                isTouched = true;
                break;
            case MotionEvent.ACTION_MOVE:
                touchEvent.type = TouchEvent.TOUCH_DRAGGED;
                isTouched = true;
                break;
            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_UP:
                touchEvent.type = TouchEvent.TOUCH_UP;
                isTouched = false;
                break;
        }

        touchEvent.x = touchX = (int)(event.getX() * scaleX);
        touchEvent.y = touchY = (int)(event.getY() * scaleY);
        touchEventsBuffer.add(touchEvent);

        return true;
    }
}

@Override
public boolean isTouchDown(int pointer) {
    synchronized(this) {
        if(pointer == 0)
            return isTouched;
        else
            return false;
    }
}

@Override
public int getTouchX(int pointer) {
    synchronized(this) {
        return touchX;
    }
}

@Override
public int getTouchY(int pointer) {
    synchronized(this) {
        return touchY;
    }
}

@Override
public List<TouchEvent> getTouchEvents() {
    synchronized(this) {
        int len = touchEvents.size();
        for( int i = 0; i < len; i++ )
            touchEventPool.free(touchEvents.get(i));
        touchEvents.clear();
        touchEvents.addAll(touchEventsBuffer);
        touchEventsBuffer.clear();
        return touchEvents;
    }
}
}

Опять же, это структура, предоставленная хорошим человеком Джеймсом Чо из Kilobolt, и при желании она легко доступна на веб-сайте Kilobolt.

В моей реализации: TOUCH_DOWN, переменная, используемая для представления любого использования ACTION_DOWN, работает нормально. Однако TOUCH_UP (для ACTION_UP) вообще не работает. Вот блок кода, где я использую его в своей функции обновления игр:

    int len = touchEvents.size();
    for (int i = 0; i < len; i++) {
        TouchEvent event = (TouchEvent) touchEvents.get(i);
        if (event.type == TouchEvent.TOUCH_DOWN) {



        }

        if (event.type == TouchEvent.TOUCH_UP) {
            if (inBounds(event, 190, 350, 100, 100)) {

                game.setScreen(new Menu(game));
            }
        }
    }

В моем вышеприведенном коде, когда сенсорный экран отрывается от экрана, TOUCH_UP должен позволить моему игровому состоянию измениться на экран меню. Однако ничего не происходит.

Если бы я поместил game.setScreen(new Menu(game)); в оператор if:

if (event.type == TouchEvent.TOUCH_DOWN) работает нормально.

Это ошибка в коде или я неправильно понимаю, как работают ACTION_UP и другие действия? Я тестирую с помощью Samsung Galaxy S5.

Спасибо за любую помощь. Изучение Android Studio и разработка игр было чертовски сложным процессом, но я определенно становлюсь лучше.


person Frank Palmasani    schedule 31.07.2015    source источник
comment
Мне немного любопытно, зачем ты вообще это делаешь. Вы делаете много работы, чтобы предоставить API, очень похожий на уже существующий, и при этом упускаете важные детали (например, разницу между отменой и повышением).   -  person Gabe Sechan    schedule 31.07.2015
comment
@Gabe Sechan, не могли бы вы уточнить или предоставить мне ресурс для чтения? Я хотел бы знать. Как я уже сказал, я очень новичок в программировании для Android и Android Studio в целом (но немного в Java). Я открыт ко всему и хочу узнать больше.   -  person Frank Palmasani    schedule 01.08.2015


Ответы (2)


Вероятно, вы возвращаете false ACTION_DOWN.

ACTION_UP работает только тогда, когда вы возвращаете true в своем событии ACTION_DOWN.

person Bruno Andrade    schedule 22.07.2016

Вы, кажется, заново изобретаете велосипед здесь. Вы пробовали более простой подход?

Во-первых, реализуйте настоящие слушатели, такие как View.OnTouchListener. Это просто, было представлено на уровне API 1 и отлично работает.

На самом деле у меня даже не было бы класса SingleTouchHandler (если, конечно, вы не планируете устанавливать этот прослушиватель касаний в нескольких представлениях. В этом случае реализуйте OnTouchListener вместо TouchHandler). Я бы просто инициализировал метод onTouch в конструкторе OnTouchListener, например так:

view.setOnTouchListener(new View.OnTouchListener(){
    @Override
    public boolean onTouch(View v, MotionEvent event){
        //Synchronize if you wish
        switch(event.getAction()){
            case MotionEvent.ACTION_DOWN:
                //Press code
                break;
            case MotionEvent.ACTION_MOVE:
                //Move code
                break;
            case MotionEvent.ACTION_UP:
                //release code
                break;
        }
        return true;
    }
});

Совершенно не нужны дополнительные занятия. Просто включите это в конструктор вашего подкласса View. Это было протестировано и работает, как и ожидалось, на SurfaceViews, которые, если вы кодируете динамические игры, вам все равно следует использовать. Во-вторых, как уже говорилось, если вы хотите установить этот OnTouchListener для нескольких представлений, вам нужно будет сделать его собственным классом, таким как тот, который у вас уже есть (SingleTouchHandler), и реализовать View.OnTouchListener (не TouchHandler). Однако, опять же, если вы программируете динамические игры, вся графика, скорее всего, будет отображаться на одном SurfaceView, а это означает, что будет только одно представление, которому можно назначить этот OnTouchListener, что устраняет необходимость в классе вместе (используйте подход показано выше).

person Alexander Guyer    schedule 31.07.2015
comment
Я, вероятно, буду звучать очень наивно, но я пытаюсь понять это хорошо, поместил бы я код, который у вас есть выше, в мой класс TouchHandler, или это будет дополнительный метод в моем классе GameScreen? - person Frank Palmasani; 01.08.2015
comment
Он должен быть помещен в конструктор вашего класса GameScreen (или любой другой метод, назначающий ему вид). По сути, если предположить, что GameScreen расширяет какой-то вид, сразу после его инициализации вызовите приведенный выше код. - person Alexander Guyer; 01.08.2015
comment
Я понял, где я запутался, я не уверен, где мой игровой класс создает представление. У меня есть класс с именем AndroidFastRenderView, который, я считаю, обрабатывает представление для всей игры и всех ее различных экранов/состояний, поэтому мне интересно, принадлежит ли он этому. - person Frank Palmasani; 01.08.2015