【问题标题】:Longpress event triggers tap event长按事件触发点击事件
【发布时间】:2018-11-11 11:53:26
【问题描述】:

在列表视图中,我有两个事件发生,一个点击事件和一个长按事件,但长按会触发这两个事件。 在 .html 文件中:

<ListView class="listViewContainer" [items]="contactList">
  <ng-template let-item="item" let-i="index">
   <StackLayout 
    (loaded)="loaded($event)"
    orientation="horizontal"
    class="preview-info-container"
   >
   </StackLayout>
  </ng-template>
</ListView>

然后是 .ts 文件

loaded(args) {
const element = args.object;
element.on("loaded, tap, longPress", (args) => {
  // console.log("Event: " + args.eventName + ", sender: " + args.object);
    if(args.eventName === "tap") {
      this.router.navigate(["card/contact/" + this.contact.id]);
    } else {
      this.togglePreviewOptions = !this.togglePreviewOptions;
    }
  });
}

我的问题是,如何防止在长按特定字段时触发点击事件?

这可能是NativeScript tap & longPress together not working 的重复问题,但是由于没有明确的答案,我想再次提出。

编辑 更多信息: 项目 tns 版本为

$ tns --version 4.3.0-2018-08-31-12160

全球原生脚本版本

nativescript@4.3.0-2018-08-31-12160

模拟器版本:

Iphone 6, iOS 11.3

【问题讨论】:

  • 详细说明您的问题。
  • 我编辑了一个更具体的问题。

标签: angular events nativescript


【解决方案1】:

我无法通过添加两个不同的事件(点击/长按)来解决此问题。 作为解决方案,我使用以下方法

内部.html

   <StackLayout (touch)="onTouch($event)">
      <Contact-Preview [contact]=contactList[i]></Contact-Preview>
   </StackLayout>

内部.ts

onTouch(args: TouchGestureEventData) {
  if(args.action === "down") {
    this.start = new Date().getMilliseconds();
  }
  if(args.action === "up") {
    this.end = new Date().getMilliseconds();
    const duration = Math.abs(this.start - this.end)
    console.log(duration > 150? "long press": "tap")
  }
}

这可以防止同时触发 tap 和 longPress 事件,从而解决我的问题。

【讨论】:

  • 非常感谢您解决这个问题。这是我现在首选的解决方案,而不是 longPress 事件。
【解决方案2】:

我们还遇到了两个事件都在 iOS 上触发的问题。 根据@GeorgeK 的回答,我将他的方法包装在一个服务中。 我还确保您可以使用项目标识符一次按下多个项目。我还为取消和移动操作添加了一个处理程序,以防止在滚动时选择项目:

touch-event.service.ts:

import { Injectable } from "@angular/core";
import { TouchGestureEventData } from "tns-core-modules/ui/gestures";

@Injectable({
    providedIn: "root"
})
export class TouchEventService {

    static longPressTimeout: number = 500;
    private events: Map<string, number> = new Map<string, number>();
    private startY: Map<string, number> = new Map<string, number>();
    private startX: Map<string, number> = new Map<string, number>();

    constructor() {
    }

    onTouch(args: TouchGestureEventData, id: string, item: any, itemTapCallback?: (item: any) => any, longPressCallback?: (item: any) => any) {
        if (args.action === "down") {
            // When someone starts pressing the element.
            // Create a new timeout that will trigger the long press callback
            // after longPressTimeout.
            this.startX.set(id, args.getX());
            this.startY.set(id, args.getY());
            // @ts-ignore
            this.events.set(id, setTimeout(() => {
                this.clearItem(id);
                longPressCallback(item);
            }, TouchEventService.longPressTimeout));
        } else if (args.action === "up" && this.events.has(id)) {
            // When someone stops pressing the element.
            // If we have an existing event, this means the long press did
            // not trigger yet. Remove the timeout and trigger the tap callback.
            clearTimeout(this.events.get(id));
            this.clearItem(id);
            itemTapCallback(item);
        } else if (args.action === "cancel" && this.events.has(id)) {
            // When someone moves away from the element while pressing.
            // Prevents handlers to be called while scrolling.
            clearTimeout(this.events.get(id));
            this.clearItem(id);
        } else if (args.action === "move" && this.events.has(id)) {
            // When someone moves from the element while pressing.
            // Prevents handlers to be called while scrolling.
            // This is mainly for iOS because they do not trigger cancel
            // when scrolling out of view. But also useful for large items.
            const differenceX = Math.abs(args.getX() - this.startX.get(id));
            const differenceY = Math.abs(args.getY() - this.startY.get(id));
            if (differenceX > 30 || differenceY > 10) {
                clearTimeout(this.events.get(id));
                this.clearItem(id);
            }
        }
    }

    clearItem(id: string) {
        this.events.delete(id);
        this.startX.delete(id);
        this.startY.delete(id);
    }
}

这是一个如何在组件中实现它的示例: example.component.ts

import { Component } from "@angular/core";
import { TouchEventService } from "./touch-event.service";

@Component({
    selector: "Example",
    templateUrl: "./example.component.html"
})
export class ExampleComponent {
    private _itemTapHandler: (item: any) => any;
    private _longPressHandler: (item: any) => any;

    constructor(public touchEventService: TouchEventService) {
        this._itemTapHandler = this.itemTapHandler();
        this._longPressHandler = this.longPressHandler();
    }

    itemTapHandler(): (item: any) => any {
        return (item) => {
            return this.itemTap(item);
        };
    }

    longPressHandler(): (item: any) => any {
        return (item) => {
            return this.longPress(item);
        };
    }

    itemTap(item: any) {
        // Handle the item tap here.
    }

    longPress(item: any) {
        // Handle the long press here.
    }
}

example.component.html

<StackLayout *ngFor="let item of items" (touch)="touchEventService.onTouch($event, item.id, item, _itemTapHandler, _longPressHandler)">
    <!-- Your item content here, you can use the touch in anything, like list views and scroll views -->
</StackLayout>

【讨论】:

    【解决方案3】:

    在模板代码中定义 2 个事件会容易得多:

    <ListView class="listViewContainer" [items]="contactList">
      <ng-template let-item="item" let-i="index">
       <StackLayout 
        (loaded)="loaded($event)"
    
        (tap)="functionWhenTap(item)" 
        (longPress)="functionWhenLongPress(item)" 
    
        orientation="horizontal"
        class="preview-info-container"
       >
       </StackLayout>
      </ng-template>
    </ListView>
    

    然后,在您的 .ts 文件中处理它:

      functionWhenTap(item: any) {
        // your things to do when tapped
      }
    
      functionWhenLongPress(item: any) {
        // your things to do when long pressed
      }
    

    这是一段实际的代码。应该也适合你。

    这是一个工作示例,在我的个人物理设备上进行了测试: https://play.nativescript.org/?template=play-ng&id=XgBfFE

    【讨论】:

    • 感谢您的评论!我已经尝试过这种方式,但我无法阻止在长按结束时触发点击事件,因此当我控制台日志时,我得到两个“长按”事件,然后“点击”
    • 嗯,这很有趣。您是否尝试过清除您的环境。一点点?比如,退出你的模拟器,删除node_moduleshooksplatforms,然后删除npm itns platform add ios/android?也许有些东西被缓存了。也许一些tns build android/ios --clear?从这些开始,我们会看到。
    • 为什么会有 2 次长按和 1 次点击?嗯。
    • 删除了 node_modules、钩子和平台,但仍然遇到完全相同的问题。长按在开始时触发两次,然后点击触发一次。
    • 您是否尝试过定义更高一级的点击事件 - 在 ListView 而不是它的子级上?如果我没记错的话,我相信会有类似(itemTapped) 的东西
    【解决方案4】:

    更清楚的是使用指令:

    import { Directive, HostListener, Output, EventEmitter } from "@angular/core";
    import { TouchAction, TouchGestureEventData } from "@nativescript/core";
    
    @Directive({
        selector: "[press], [pressHold]",
    })
    export class PressDirective {
        @Output() press: EventEmitter<void> = new EventEmitter();
        @Output() pressHold: EventEmitter<void> = new EventEmitter();
    
        private readonly pressHoldTimeout: number = 500;
        private _startPress: number;
        private _timeout: NodeJS.Timeout;
        private _cancel: boolean;
    
        @HostListener("touch", ["$event"])
        touch(event: TouchGestureEventData) {
            switch (event.action) {
                case TouchAction.down:
                    this._cancel = false;
                    this._startPress = Date.now();
                    this._timeout = setTimeout(() => {
                        this.pressHold.emit();
                    }, this.pressHoldTimeout);
    
                    break;
    
                case TouchAction.up:
                    if (
                        !this._cancel &&
                        Date.now() - this._startPress < this.pressHoldTimeout
                    ) {
                        clearTimeout(this._timeout);
                        this.press.emit();
                    }
                    break;
    
                case TouchAction.move:
                case TouchAction.cancel:
                    clearTimeout(this._timeout);
                    this._cancel = true;
                    break;
            }
        }
    }
    

    在模板中:enter code here

    <Button
      (press)="onPress()"
      (pressHold)="onPressHold()"
      text="press && hold"
    >
    </Button>
    

    【讨论】:

      猜你喜欢
      • 2016-07-01
      • 2023-03-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-12-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多