You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在React Native中指定x/y坐标触发Tap事件?

Got it, let's walk through how to make this work. Simulating a tap event on a specific view using NativeEventEmitter is totally doable, but you’re right—you need to build a custom native module first because React Native doesn’t expose this functionality out of the box. Here's a step-by-step breakdown tailored to your needs:

How to Simulate Tap Events with NativeEventEmitter in React Native

1. Core Concept

You can’t use NativeEventEmitter with a pre-existing native module for this—you’ll need to create a custom native module that can:

  • Locate the target view using its React Native tag
  • Programmatically dispatch touch/tap events to that view
  • (Optionally) emit a confirmation event back to React Native once the tap is simulated

2. Step-by-Step Implementation

Part A: iOS Custom Module (Swift)

First, add this code to your iOS project (create a new file like TapSimulatorModule.swift):

import UIKit
import React

@objc(TapSimulatorModule)
class TapSimulatorModule: NSObject, RCTEventEmitter {
  static func moduleName() -> String! {
    return "TapSimulatorModule"
  }

  static func requiresMainQueueSetup() -> Bool {
    return true
  }

  // Simulate tap on a view by its React tag
  @objc func simulateTap(onViewWithTag tag: NSNumber) {
    DispatchQueue.main.async {
      guard let targetView = RCTSharedApplication()?.window?.viewWithTag(tag.intValue) else {
        print("Couldn't find view with tag \(tag)")
        return
      }

      // Create and dispatch touch down/up events
      let touchDown = UITouchEvent(type: .touchDown, location: targetView.center, view: targetView)
      let touchUp = UITouchEvent(type: .touchUpInside, location: targetView.center, view: targetView)
      
      UIApplication.shared.sendEvent(touchDown)
      UIApplication.shared.sendEvent(touchUp)

      // Emit confirmation event to RN
      self.sendEvent(withName: "TapSimulated", body: ["viewTag": tag])
    }
  }

  // Define supported events for the emitter
  override func supportedEvents() -> [String]! {
    return ["TapSimulated"]
  }
}

Don’t forget to update your bridging header to include:

#import <React/RCTEventEmitter.h>

Part B: Android Custom Module (Kotlin)

Create a file TapSimulatorModule.kt in your Android project’s src/main/java/com/your-app-name/ directory:

package com.your_app_name

import android.view.MotionEvent
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReactContextBaseJavaModule
import com.facebook.react.bridge.ReactMethod
import com.facebook.react.uimanager.NativeViewHierarchyManager
import com.facebook.react.uimanager.UIManagerModule

class TapSimulatorModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {
    override fun getName(): String = "TapSimulatorModule"

    @ReactMethod
    fun simulateTap(viewTag: Int) {
        val uiManager = reactApplicationContext.getNativeModule(UIManagerModule::class.java)
        uiManager?.addUIBlock { hierarchyManager: NativeViewHierarchyManager ->
            val targetView = hierarchyManager.resolveView(viewTag)
            targetView?.let { view ->
                // Create touch down/up events
                val downEvent = MotionEvent.obtain(
                    System.currentTimeMillis(),
                    System.currentTimeMillis(),
                    MotionEvent.ACTION_DOWN,
                    view.width / 2f,
                    view.height / 2f,
                    0
                )
                val upEvent = MotionEvent.obtain(
                    System.currentTimeMillis(),
                    System.currentTimeMillis(),
                    MotionEvent.ACTION_UP,
                    view.width / 2f,
                    view.height / 2f,
                    0
                )

                // Dispatch events to the view
                view.dispatchTouchEvent(downEvent)
                view.dispatchTouchEvent(upEvent)

                // Emit confirmation to RN
                reactApplicationContext
                    .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
                    .emit("TapSimulated", mapOf("viewTag" to viewTag))

                // Clean up events
                downEvent.recycle()
                upEvent.recycle()
            }
        }
    }
}

Register the module in MainApplication.kt:

override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
    return listOf(TapSimulatorModule(reactContext))
}

Part C: React Native Component Code

Now use the module in your RN app to trigger taps and listen for events:

import React, { useRef, useEffect } from 'react';
import { View, Text, TouchableOpacity, NativeEventEmitter, NativeModules, findNodeHandle } from 'react-native';

// Import our custom native module
const { TapSimulatorModule } = NativeModules;
const tapEventEmitter = new NativeEventEmitter(TapSimulatorModule);

const App = () => {
  const targetViewRef = useRef(null);

  // Listen for tap confirmation events
  useEffect(() => {
    const subscription = tapEventEmitter.addListener('TapSimulated', (data) => {
      console.log(`Successfully simulated tap on view tag: ${data.viewTag}`);
    });

    return () => subscription.remove();
  }, []);

  // Trigger the simulated tap
  const triggerTap = () => {
    if (targetViewRef.current) {
      const viewTag = findNodeHandle(targetViewRef.current);
      if (viewTag) TapSimulatorModule.simulateTap(viewTag);
    }
  };

  return (
    <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
      {/* The view we want to simulate taps on */}
      <TouchableOpacity
        ref={targetViewRef}
        onPress={() => console.log('User tapped me normally!')}
        style={{ padding: 20, backgroundColor: '#87CEEB' }}
      >
        <Text>Target View</Text>
      </TouchableOpacity>

      {/* Button to trigger simulated tap */}
      <TouchableOpacity
        onPress={triggerTap}
        style={{ marginTop: 30, padding: 20, backgroundColor: '#90EE90' }}
      >
        <Text>Simulate Tap on Target</Text>
      </TouchableOpacity>
    </View>
  );
};

export default App;

3. Key Details to Remember

  • Native Module Name: We named our module TapSimulatorModule—this is what you import from NativeModules.
  • Emitted Events: We defined a TapSimulated event to confirm the tap ran successfully. You can add more events (like TapFailed) if you want to handle errors.
  • View Tags: findNodeHandle converts a React ref into a native view tag, which the module uses to locate the target view.
  • Main Thread: All UI operations (like dispatching touch events) must run on the main thread—we handle this with DispatchQueue.main.async (iOS) and UIBlock (Android).

内容的提问来源于stack exchange,提问作者Joe Beuckman

火山引擎 最新活动