首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >将GWT单击事件转换为Touch事件

将GWT单击事件转换为Touch事件
EN

Stack Overflow用户
提问于 2012-03-07 06:27:35
回答 4查看 11.1K关注 0票数 12

我正在做一个大项目,我编写了很多GWT代码。现在,我正致力于使该项目与iPad和安卓平板电脑等平板电脑完全兼容。

作为其中的一部分,我注意到触摸设备需要300 As的延迟来处理单击事件。在这个项目中,再次编写触摸事件是一项非常乏味的任务。我在这方面做了很多研究,并发现Google语音应用程序中使用的Google快速按钮API。我试过了,而且它很好用,但是需要大量的编码和JSNI。

我的问题是,在你所知的范围内还有什么其他东西可以轻易克服这种拖延吗?

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2012-03-08 07:56:45

以下是快速button.It的纯java实现,它不包括JNSI的一行

代码语言:javascript
复制
package com.apollo.tabletization.shared.util;

import java.util.Date;

import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.dom.client.Touch;
import com.google.gwt.event.dom.client.HasAllTouchHandlers;
import com.google.gwt.event.dom.client.HasClickHandlers;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.Widget;

/** Implementation of Google FastButton {@link http://code.google.com/mobile/articles/fast_buttons.html} */
public class FastButton extends Composite {

  private boolean touchHandled = false;
  private boolean clickHandled = false;
  private boolean touchMoved = false;
  private int startY;
  private int startX;
  private int timeStart;

  public FastButton(Widget child) {
    // TODO - messages
    assert (child instanceof HasAllTouchHandlers) : "";
      assert (child instanceof HasClickHandlers) : "";
        initWidget(child);
        sinkEvents(Event.TOUCHEVENTS | Event.ONCLICK);
  }

  @Override
  public Widget getWidget() {
    return super.getWidget();
  }

  @Override
  public void onBrowserEvent(Event event) {
    timeStart = getUnixTimeStamp();
    switch (DOM.eventGetType(event)) {
      case Event.ONTOUCHSTART:
        {
          onTouchStart(event);
          break;
        }
      case Event.ONTOUCHEND:
        {
          onTouchEnd(event);
          break;
        }
      case Event.ONTOUCHMOVE:
        {
          onTouchMove(event);
          break;
        }
      case Event.ONCLICK:
        {
          onClick(event);
          return;
        }
    }

    super.onBrowserEvent(event);
  }

  private void onClick(Event event) {
    event.stopPropagation();

    int timeEnd = getUnixTimeStamp();
    if(touchHandled) {
      //Window.alert("click via touch: "+ this.toString() + "..." +timeStart+"---"+timeEnd);
      touchHandled = false;
      clickHandled = true;
      super.onBrowserEvent(event);
    }
    else {  
      if(clickHandled) {

        event.preventDefault();
      }
      else {
        clickHandled = false;
        //Window.alert("click nativo: "+ this.toString()+ "..." +(timeStart-timeEnd)+"==="+timeStart+"---"+timeEnd);
        super.onBrowserEvent(event);
      }
    }
  }

  private void onTouchEnd(Event event)  {
    if (!touchMoved) {
      touchHandled = true;
      fireClick();
    }
  }

  private void onTouchMove(Event event)  {
    if (!touchMoved) {
      Touch touch = event.getTouches().get(0);
      int deltaX = Math.abs(startX - touch.getClientX()); 
      int deltaY = Math.abs(startY - touch.getClientY());

      if (deltaX > 5 || deltaY > 5) {
        touchMoved = true;
      }
    }
  }

  private void onTouchStart(Event event) {
    Touch touch = event.getTouches().get(0);
    this.startX = touch.getClientX();
    this.startY = touch.getClientY();               
    touchMoved = false;
  }

  private void fireClick() {
    NativeEvent evt = Document.get().createClickEvent(1, 0, 0, 0, 0, false,
        false, false, false);
    getElement().dispatchEvent(evt);
  }

  private int getUnixTimeStamp() {
    Date date = new Date();
    int iTimeStamp = (int) (date.getTime() * .001);
    return iTimeStamp;
  }
}  
票数 14
EN

Stack Overflow用户

发布于 2012-12-16 22:32:47

我试着用上面的答案和评论来尝试这个实现。

我还提供了一个示例GWT项目,它可以用于简单的比较:

http://gwt-fast-touch-press.appspot.com/

https://github.com/ashtonthomas/gwt-fast-touch-press

请注意,只有在移动设备(或处理触摸事件的设备,而不只是返回到onClick上)时,您才能看到节省的时间。

我增加了3个快速按钮和3个普通按钮。在较旧的移动设备上,有时在较新的设备上,你很容易看到改进(三星Galaxy只显示了大约100 1st的延迟,而第一代iPad几乎每次都超过400 1st)。最大的改进是尝试快速连续地单击这些框(这里不是真正的按钮,而是可以调整)。

代码语言:javascript
复制
package io.ashton.fastpress.client.fast;

import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.RepeatingCommand;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.dom.client.Touch;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.Widget;

/**
 *
 * GWT Implementation influenced by Google's FastPressElement:
 * https://developers.google.com/mobile/articles/fast_buttons
 *
 * Using Code examples and comments from:
 * http://stackoverflow.com/questions/9596807/converting-gwt-click-events-to-touch-events
 *
 * The FastPressElement is used to avoid the 300ms delay on mobile devices (Only do this if you want
 * to ignore the possibility of a double tap - The browser waits to see if we actually want to
 * double top)
 *
 * The "press" event will occur significantly fast (around 300ms faster). However the biggest
 * improvement is from enabling fast consecutive touches.
 *
 * If you try to rapidly touch one or more FastPressElements, you will notice a MUCH great
 * improvement.
 *
 * NOTE: Different browsers will handle quick swipe or long hold/drag touches differently.
 * This is an edge case if the user is long pressing or pressing while dragging the finger
 * slightly (but staying on the element) - The browser may or may not fire the event. However,
 * the browser will always fire the regular tap/press very quickly.
 *
 * TODO We should be able to embed fastElements and have the child fastElements NOT bubble the event
 * So we can embed the elements if needed (???)
 *
 * @author ashton
 *
 */
public abstract class FastPressElement extends Composite implements HasPressHandlers {

  private boolean touchHandled = false;
  private boolean clickHandled = false;
  private boolean touchMoved = false;
  private boolean isEnabled = true;
  private int touchId;
  private int flashDelay = 75; // Default time delay in ms to flash style change

  public FastPressElement() {
    // Sink Click and Touch Events
    // I am not going to sink Mouse events since
    // I don't think we will gain anything

    sinkEvents(Event.ONCLICK | Event.TOUCHEVENTS); // Event.TOUCHEVENTS adds all (Start, End,
                                                   // Cancel, Change)

  }

  public FastPressElement(int msDelay) {
    this();
    if (msDelay >= 0) {
      flashDelay = msDelay;
    }
  }

  public void setEnabled(boolean enabled) {
    if (enabled) {
      onEnablePressStyle();
    } else {
      onDisablePressStyle();
    }
    this.isEnabled = enabled;
  }

  /**
   * Use this method in the same way you would use addClickHandler or addDomHandler
   *
   */
  @Override
  public HandlerRegistration addPressHandler(PressHandler handler) {
    // Use Widget's addHandler to ensureHandlers and add the type/return handler
    // We don't use addDom/BitlessHandlers since we aren't sinkEvents
    // We also aren't even dealing with a DomEvent
    return addHandler(handler, PressEvent.getType());
  }

  /**
   *
   * @param event
   */
  private void firePressEvent(Event event) {
    // This better verify a ClickEvent or TouchEndEvent
    // TODO might want to verify
    // (hitting issue with web.bindery vs g.gwt.user package diff)
    PressEvent pressEvent = new PressEvent(event);
    fireEvent(pressEvent);
  }

  /**
   * Implement the handler for pressing but NOT releasing the button. Normally you just want to show
   * some CSS style change to alert the user the element is active but not yet pressed
   *
   * ONLY FOR STYLE CHANGE - Will briefly be called onClick
   *
   * TIP: Don't make a dramatic style change. Take note that if a user is just trying to scroll, and
   * start on the element and then scrolls off, we may not want to distract them too much. If a user
   * does scroll off the element,
   *
   */
  public abstract void onHoldPressDownStyle();

  /**
   * Implement the handler for release of press. This should just be some CSS or Style change.
   *
   * ONLY FOR STYLE CHANGE - Will briefly be called onClick
   *
   * TIP: This should just go back to the normal style.
   */
  public abstract void onHoldPressOffStyle();

  /**
   * Change styling to disabled
   */
  public abstract void onDisablePressStyle();

  /**
   * Change styling to enabled
   *
   * TIP:
   */
  public abstract void onEnablePressStyle();

  @Override
  public Widget getWidget() {
    return super.getWidget();
  }

  @Override
  public void onBrowserEvent(Event event) {
    switch (DOM.eventGetType(event)) {
      case Event.ONTOUCHSTART: {
        if (isEnabled) {
          onTouchStart(event);
        }
        break;
      }
      case Event.ONTOUCHEND: {
        if (isEnabled) {
          onTouchEnd(event);
        }
        break;
      }
      case Event.ONTOUCHMOVE: {
        if (isEnabled) {
          onTouchMove(event);
        }
        break;
      }
      case Event.ONCLICK: {
        if (isEnabled) {
          onClick(event);
        }
        return;
      }
      default: {
        // Let parent handle event if not one of the above (?)
        super.onBrowserEvent(event);
      }
    }

  }

  private void onClick(Event event) {
    event.stopPropagation();

    if (touchHandled) {
      // if the touch is already handled, we are on a device
      // that supports touch (so you aren't in the desktop browser)

      touchHandled = false;// reset for next press
      clickHandled = true;//

      super.onBrowserEvent(event);

    } else {
      if (clickHandled) {
        // Not sure how this situation would occur
        // onClick being called twice..
        event.preventDefault();
      } else {
        // Press not handled yet

        // We still want to briefly fire the style change
        // To give good user feedback
        // Show HoldPress when possible
        Scheduler.get().scheduleDeferred(new ScheduledCommand() {
          @Override
          public void execute() {
            // Show hold press
            onHoldPressDownStyle();

            // Now schedule a delay (which will allow the actual
            // onTouchClickFire to executed
            Scheduler.get().scheduleFixedDelay(new RepeatingCommand() {
              @Override
              public boolean execute() {
                // Clear the style change
                onHoldPressOffStyle();
                return false;
              }
            }, flashDelay);
          }
        });

        clickHandled = false;
        firePressEvent(event);

      }
    }
  }

  private void onTouchStart(Event event) {

    onHoldPressDownStyle(); // Show style change

    // Stop the event from bubbling up
    event.stopPropagation();

    // Only handle if we have exactly one touch
    if (event.getTargetTouches().length() == 1) {
      Touch start = event.getTargetTouches().get(0);
      touchId = start.getIdentifier();
      touchMoved = false;
    }

  }

  /**
   * Check to see if the touch has moved off of the element.
   *
   * NOTE that in iOS the elasticScroll may make the touch/move cancel more difficult.
   *
   * @param event
   */
  private void onTouchMove(Event event) {

    if (!touchMoved) {
      Touch move = null;

      for (int i = 0; i < event.getChangedTouches().length(); i++) {
        if (event.getChangedTouches().get(i).getIdentifier() == touchId) {
          move = event.getChangedTouches().get(i);
        }
      }

      // Check to see if we moved off of the original element

      // Use Page coordinates since we compare with widget's absolute coordinates
      int yCord = move.getPageY();
      int xCord = move.getPageX();

      boolean yTop = getWidget().getAbsoluteTop() > yCord; // is y above element
      boolean yBottom = (getWidget().getAbsoluteTop() + getWidget().getOffsetHeight()) < yCord; // y
                                                                                                // below
      boolean xLeft = getWidget().getAbsoluteLeft() > xCord; // is x to the left of element
      boolean xRight = (getWidget().getAbsoluteLeft() + getWidget().getOffsetWidth()) < xCord; // x
                                                                                               // to
                                                                                               // the
                                                                                               // right
      if (yTop || yBottom || xLeft || xRight) {
        touchMoved = true;
        onHoldPressOffStyle();// Go back to normal style
      }

    }

  }

  private void onTouchEnd(Event event) {
    if (!touchMoved) {
      touchHandled = true;
      firePressEvent(event);
      event.preventDefault();
      onHoldPressOffStyle();// Change back the style
    }
  }

}
票数 10
EN

Stack Overflow用户

发布于 2012-11-05 19:17:16

我认为前面所写的答案中的代码有一些问题,特别是当它们是多重接触时。

(注意:我正在使用元素库作为引用编写代码,因此在用户库中有些调用可能是不同的)。

( a)代码不过滤针对按钮的触摸;它调用TouchEvent.getTouches()。您希望在touchstart和touchmove上调用TouchEvent.getTargetTouches(),以便只为您的按钮获取触摸。您希望在touchend上调用TouchEvent.getChangedTouches()以获得end触摸。

( b)该守则没有考虑到多点接触。在触屏启动时,您可以检查单个触摸是否可用,如果有多个触摸,则可以退出。另外,在touchstart上,保存触摸的id,然后在touchmove和touchend中使用这个id来在返回的数组中找到您的触摸id(以防用户稍后碰了另一个手指)。你也可以简化和检查多个触摸在触地移动和触摸和保释再次那里。

我相信你需要在触屏时打电话给stopPropagation,因为你在处理这个事件。我不知道他们在touchstart事件上调用event.stopPropagation的位置--您可以看到,这种情况发生在单击处理程序中,而不是touchstart。这将防止浏览器将触摸自动转换为单击,这将导致多次单击。

还有一种更简单的方法。如果您不关心在按钮上拖动启动,那么只需在touchstart事件中调用单击逻辑(并确保检查单个触摸,并调用event.stopPropagation ),而忽略touchmove和touchend。所有的触摸移动和触摸的东西是处理的情况下,允许拖动启动按钮。

票数 6
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/9596807

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档