Интерпретировать "двойное касание" из потока TouchLocation с помощью Rx - PullRequest
2 голосов
/ 16 июля 2011

У меня есть IObservable , и я пытаюсь извлечь из него жесты.

Мне удалось вытащить нажатие, но у меня возникли проблемы с наведением головы на Двойное нажатие.

Расположение TouchLocationи State, так что Tap получен путем агрегации их на основе двух критериев.

1) Позиция не перемещается более чем на 10 пикселей в любом направлении (допускается некоторое движение, но не перетаскивание)

2) Начинается для события с состоянием «Нажатие» и продолжается до тех пор, пока не произойдет вход с состоянием «Отпущено»

3) Где «отпускание» происходит в течение 1 секунды после «нажатия»

Теперь у меня есть IObservable(результат агрегации) и нужно произвести двойное касание.

Как я могу вернуть первый жест (чтобы произвести первоначальное касание), а затем заменить второй жестом двойного касания, но только если второйТап получен до 1 секунды таймера.Если получено после таймера, то это также должен быть простой Tap.

Ответы [ 3 ]

3 голосов
/ 16 июля 2011

Забавная проблема.

// Setup some parameters
var singleTapInterval = TimeSpan.FromSeconds(0.5);
var dblTapInterval = TimeSpan.FromSeconds(1);

// the source of touches
IObservable<TouchLocation> txs; 

// Get single taps
                               // 0, 1, but no more than 
                               // 2 touches in the timespan
IObservable<TouchLocation> taps = txs.Buffer(2,singleTapInterval) 
                                    .Where(touches => touches.Count == 2 &&
                                           touches[0].state == "Pressed" &&
                                           touches[1].state == "Released" &&
                                           distance(touches) < distanceThreshold)
                                    .Select(touches => touches[1]);

// now detect double taps
IObservable<TapKind> dbltaps = taps.Buffer(2,dblTapInterval)
                                   .Where(taps => taps.Count == 2)
                                   .Select(_ => TapKind.SingleTap);

// detect single taps - taps must be _slower_ than the dblTapInterval 
// for this to work
IObservable<TapKind> singletaps = taps.Throttle(dblTapInterval)
                                      .Select(_ => TapKind.SingleTap);

// compbine the two, so we see either tap condition
IObservable<TapKind> interestingTaps = dbltaps.Merge(singletaps);
2 голосов
/ 18 июля 2011

Вернулся к началу и понял, что я сильно усложнил это ... спасибо Скотту за помощь в сбрасывании моего подхода!

"target" - это IObservable (в методе расширения).

Эта настройка обрабатывает отправку первого события (поэтому мы не теряем первое событие, пока почтовый индекс находится в режиме ожидания), игнорирует удержания (касание, которое занимает более 1 секунды), заменяет второе касание двойным касанием(но только если это произошло в течение 1 секунды после касания) и охватывает новый запрос, который я раньше не осознавал, который гарантирует, что двойное касание следует только за касанием, а не еще одно двойное нажатие (4 касания производили Tap | DTap | DTap| DTap и теперь это Tap | DTap | Tap | DTap, что является правильным поведением)

Возможно, это еще больше упростит это, но это предварительная рабочая версия:

var grouped = (from t in target
                group t by t.Id into groups
                select groups);

var presses = (from g in grouped
                from t in g
                where t.State == TouchLocationState.Pressed
                select t).Timestamp();

var releases = (from g in grouped
                from t in g
                where t.State == TouchLocationState.Released
                select t).Timestamp();

var pressAndRelease = presses.Zip(releases, (press, release) =>
    {
        return new { Press = press, Release = release };
    })
    .Where(pr =>
        {
            var delta = (pr.Release.Timestamp - pr.Press.Timestamp).TotalSeconds;

            return delta < 1;
        })
    .Timestamp();

var zipped = pressAndRelease.Zip(pressAndRelease.Skip(1), (prev, cur) =>
    {
        return new { Previous = prev, Current = cur };
    });

pressAndRelease.TakeUntil(zipped).Subscribe(a =>
    {
        Debug.WriteLine("FIRST TAP!");
    });

var wasDoubleTap = false;
zipped.Subscribe(a =>
    {
        var delta = (a.Current.Timestamp - a.Previous.Timestamp).TotalSeconds;

        if (wasDoubleTap || delta > 1)
        {
            Debug.WriteLine("TAP");
            wasDoubleTap = false;
        }
        else
        {
            Debug.WriteLine("DOUBLE TAP!");
            wasDoubleTap = true;
        }
    });
2 голосов
/ 16 июля 2011

Я думаю, что лучший ответ, который я могу вам дать, это " не делайте этого ".

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

См. этот документ в MSDN для объяснения того, как использовать встроенную функциональность жестов.,Это в основном случай установки TouchPanel.EnabledGestures, опроса IsGestureAvailable, а затем вызова ReadGesture.

...