Android: как обнаружить, когда свиток закончился

Я использую метод onScroll для GestureDetector.SimpleOnGestureListener для прокрутки большого растрового изображения на холсте. Когда прокрутка закончилась, я хочу перерисовать растровое изображение, если пользователь хочет прокрутить дальше... от края растрового изображения, но я не вижу, как определить, когда свиток закончился (пользователь поднял палец с экрана).

e2.getAction() всегда возвращает значение 2, так что это не помощь. e2.getPressure, похоже, возвращает довольно постоянные значения (около 0,25) до тех пор, пока окончательный вызов Scroll, когда давление, похоже, упадет примерно до 0,13. Полагаю, я мог бы обнаружить это снижение давления, но это будет далеко не надежным.

Должен быть лучший способ: может ли кто-нибудь помочь, пожалуйста?

Ответ 1

Вот как я решил проблему. Надеюсь, это поможет.

// declare class member variables
private GestureDetector mGestureDetector;
private OnTouchListener mGestureListener;
private boolean mIsScrolling = false;


public void initGestureDetection() {
        // Gesture detection
    mGestureDetector = new GestureDetector(new SimpleOnGestureListener() {
        @Override
        public boolean onDoubleTap(MotionEvent e) {
            handleDoubleTap(e);
            return true;
        }

        @Override
        public boolean onSingleTapConfirmed(MotionEvent e) {
            handleSingleTap(e);
            return true;
        }

        @Override
        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
            // i'm only scrolling along the X axis
            mIsScrolling = true;                
            handleScroll(Math.round((e2.getX() - e1.getX())));
            return true;
        }

        @Override
        /**
         * Don't know why but we need to intercept this guy and return true so that the other gestures are handled.
         * https://code.google.com/p/android/issues/detail?id=8233
         */
        public boolean onDown(MotionEvent e) {
            Log.d("GestureDetector --> onDown");
            return true;
        }
    });

    mGestureListener = new View.OnTouchListener() {
        public boolean onTouch(View v, MotionEvent event) {

            if (mGestureDetector.onTouchEvent(event)) {
                return true;
            }

            if(event.getAction() == MotionEvent.ACTION_UP) {
                if(mIsScrolling ) {
                    Log.d("OnTouchListener --> onTouch ACTION_UP");
                    mIsScrolling  = false;
                    handleScrollFinished();
                };
            }

            return false;
        }
    };

    // attach the OnTouchListener to the image view
    mImageView.setOnTouchListener(mGestureListener);
}

Ответ 2

Вы должны взглянуть на http://developer.android.com/reference/android/widget/Scroller.html. Особенно это может помочь (отсортировано по релевантности):

isFinished();
computeScrollOffset();
getFinalY(); getFinalX(); and getCurrY() getCurrX()
getDuration()

Это означает, что вам нужно создать скроллер.

Если вы хотите использовать касание, вы также можете использовать GestureDetector и определить собственную прокрутку холста. В следующем примере создается ScrollableImageView, и для его использования вам необходимо определить измерения вашего изображения. Вы можете определить свой собственный диапазон прокрутки, и после завершения прокрутки изображение будет перерисовано.

http://www.anddev.org/viewtopic.php?p=31487#31487

В зависимости от вашего кода вы должны рассмотреть invalidate (int l, int t, int r, int b); за недействительность.

Ответ 3

Возвращаясь к этому через несколько месяцев, я теперь придерживался другого подхода: используя обработчик (как в примере Android Snake), чтобы отправить сообщение в приложение каждые 125 миллисекунд, в котором ему предлагается проверить, имеет ли свиток и прошло ли более 100 миллисекунд со времени последнего события прокрутки.

Кажется, что это работает очень хорошо, но если кто-то может увидеть какие-либо недостатки или возможные улучшения, я должен быть благодарен за них.

Соответствующий код находится в классе MyView:

public class MyView extends android.view.View {

...

private long timeCheckInterval = 125; // milliseconds
private long scrollEndInterval = 100;
public long latestScrollEventTime;
public boolean scrollInProgress = false;

public MyView(Context context) {
    super(context);
}

private timeCheckHandler mTimeCheckHandler = new timeCheckHandler();

class timeCheckHandler extends Handler{

        @Override
        public void handleMessage(Message msg) {
        long now = System.currentTimeMillis();
        if (scrollInProgress && (now>latestScrollEventTime+scrollEndInterval)) {
                    scrollInProgress = false;

//Прокрутка завершена, поэтому вставьте код здесь

//который вызывает метод doDrawing()

//для перерисовки растрового изображения с повторной центровкой, где заканчивается прокрутка

                    [ layout or view ].invalidate();
        }
        this.sleep(timeCheckInterval);
        }

        public void sleep(long delayMillis) {
            this.removeMessages(0);
            sendMessageDelayed(obtainMessage(0), delayMillis);
            }
    }
}

@Override protected void onDraw(Canvas canvas){
        super.onDraw(canvas);

//код для рисования большого растрового изображения буфера на холст представления // позиционируется для учета любого прокрутки, который выполняется.

}

public void doDrawing() {

//код для выполнения детального (и трудоемкого) рисования // в растровое изображение большого буфера

//следующая команда сбрасывает часы проверки времени // часы начинаются, когда // основное действие вызывает этот метод при запуске приложения

        mTimeCheckHandler.sleep(timeCheckInterval);
}

//остаток класса MyView

}

и в классе MyGestureDetector

public class MyGestureDetector extends SimpleOnGestureListener {

@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
        float distanceY) {

    [MyView].scrollInProgress = true;
        long now = System.currentTimeMillis();  
    [MyView].latestScrollEventTime =now;

    [MyView].scrollX += (int) distanceX;
    [MyView].scrollY += (int) distanceY;

//следующая команда вызывает вызов метода View onDraw // который отображает растровое изображение буфера на экран // сдвигается, чтобы учесть прокрутку

    [MyView].invalidate();

}

//остаток класса MyGestureDetector

}

Ответ 4

SimpleOnGestureListener.onFling() 

Кажется, что это происходит, когда заканчивается свиток (т.е. пользователь позволяет пальцу), что я использую, и он отлично работает для меня.

Ответ 5

Я изучал эту же проблему. Я видел, как Акос Чж ответил на ваш вопрос. Я создал что-то похожее, но с моей версией я заметил, что он работал только для обычного свитка - это значит, что он не генерирует бросок. Но если бросок действительно сгенерировался - независимо от того, обработал ли я или нет, тогда он НЕ обнаружил "ACTION_UP" в "onTouchEvent". Теперь, возможно, это было что-то с моей реализацией, но если бы это было, я не мог понять, почему.

После дальнейших исследований я заметил, что во время перелета "ACTION_UP" каждый раз проходил "onFling" в "e2". Поэтому я понял, что, возможно, именно поэтому в "экземплярах onTouchEvent" он не обрабатывался.

Чтобы заставить меня работать для меня, мне нужно было вызвать метод обработки "ACTION_UP" в "onFling" , а затем он работал для обоих типов прокрутки. Ниже приведены точные шаги, которые я предпринял для реализации в своем приложении:

-инициализируется "gestureScrolling" с логическим значением "false" в конструкторе.

-I установите значение "true" в "onScroll"

-создал метод обработки события "ACTION_UP" . Внутри этого события я reset "gestureCrolling" до false, а затем остальная часть обработки мне нужно было сделать.

-in "onTouchEvent", если обнаружено "ACTION_UP" и "gestureScrolling" = true, то я вызвал мой метод для обработки "ACTION_UP"

-И часть, которую я сделал, была другой: я также назвал мой метод обработкой "ACTION_UP" внутри "onFling" .

Ответ 6

Я уверен, что для вас слишком поздно, однако, похоже, я нашел правильное решение вашего первоначального вопроса и не нуждаюсь в намерении.

Если вы используете Scroller/OverScroller Object для прокрутки, вы должны проверить возвращаемое значение из следующей функции.

public boolean computeScrollOffset() 

пользоваться

harvinder

Ответ 7

Я не делал этого сам, но, глядя на onTouch(), вы всегда получаете последовательность 0 < 2 > 1, поэтому конец должен быть 1 для подъема пальца.

Ответ 8

Я не знаю Android, но, глядя на документацию, кажется, что Роб прав: Android ACTION_UP constant Попробуйте проверить ACTION_UP с getAction()?

Изменить: что показывает e1.getAction()? Возвращает ли он ACTION_UP? В документации говорится, что он содержит начальное событие down, поэтому, возможно, оно также сообщит, когда указатель вверх

Изменить: Только две вещи, о которых я могу думать. Вы возвращаетесь в любой момент? Это может предотвратить ACTION_UP

Единственное, что я попробую, это иметь отдельное событие, возможно, onDown, и установить флаг внутри onScroll, например isScrolling. Когда ACTION_UP присваивается onDown и isScrolling задается, вы можете делать все, что хотите, и reset isScrolling - false. То есть, предполагая, что onDown вызывается вместе с onScroll, и getAction вернет ACTION_UP во время onDown

Ответ 9

Я не пробовал/не использовал это, а идею для подхода:

остановить/прервать перерисовку холста на КАЖДОМ прокрутке событий ждать 1 с, а затем начать перерисовывать холст на КАЖДОЙ прокрутке.

это приведет к выполнению перерисовки только в конце прокрутки, так как только последняя прокрутка будет фактически непрерывной для завершения перерисовки.

надеюсь, эта идея поможет вам:)

Ответ 10

Извлечь из события onScroll из API GestureListener: текст ссылки

публичный абстракционизм boolean onScroll (MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) С: API Уровень 1

Возвращает     * true, если событие потребляется, иначе false

Возможно, после того, как событие было уничтожено, действие закончено, и пользователь отложил палец от экрана или, по крайней мере, закончил это действие onScroll

Затем вы можете использовать это в инструкции IF для сканирования == true, а затем начать со следующего действия.

Ответ 11

Я думаю, что это будет работать так, как вам нужно

protected class SnappingGestureDetectorListener extends SimpleOnGestureListener{

    @Override
    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY){
        boolean result = super.onScroll(e1, e2, distanceX, distanceY);

        if(!result){
            //Do what you need to do when the scrolling stop here
        }

        return result;
    }

}

Ответ 12

Это то, что сработало для меня.

Я обогатил существующий GestureDetector.OnGestureListener с помощью метода onFingerUp(). Этот слушатель делает все как встроенный GestureDetector, и он также может прослушивать событие finger up (это не onFling(), поскольку это вызывается только тогда, когда палец поднимается вместе с быстрым ударом).

import android.content.Context;
import android.os.Handler;
import android.view.GestureDetector;
import android.view.MotionEvent;

public class FingerUpGestureDetector extends GestureDetector {
    FingerUpGestureDetector.OnGestureListener fListener;
    public FingerUpGestureDetector(Context context, OnGestureListener listener) {
        super(context, listener);
        fListener = listener;
    }

    public FingerUpGestureDetector(Context context, GestureDetector.OnGestureListener listener, OnGestureListener fListener) {
        super(context, listener);
        this.fListener = fListener;
    }

    public FingerUpGestureDetector(Context context, GestureDetector.OnGestureListener listener, Handler handler, OnGestureListener fListener) {
        super(context, listener, handler);
        this.fListener = fListener;
    }

    public FingerUpGestureDetector(Context context, GestureDetector.OnGestureListener listener, Handler handler, boolean unused, OnGestureListener fListener) {
        super(context, listener, handler, unused);
        this.fListener = fListener;
    }

    public interface OnGestureListener extends GestureDetector.OnGestureListener {
        boolean onFingerUp(MotionEvent e);
    }

    public static class SimpleOnGestureListener extends GestureDetector.SimpleOnGestureListener implements FingerUpGestureDetector.OnGestureListener {
        @Override
        public boolean onFingerUp(MotionEvent e) {
            return false;
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        if (super.onTouchEvent(ev)) return true;
        if (ev.getAction() == MotionEvent.ACTION_UP) {
            return fListener.onFingerUp(ev);
        }
        return false;
    }
}