16

I'd like to use OpenStreetMap tiles in my React Native app on Android, so I'm trying to wrap the OSMDroid native UI component as described here. For the most part it's working fine, but I'm having trouble figuring out how to correctly handle events, specifically onScroll and onZoom.

With OSMDroid, you set a DelayedMapListener to handle the events, which is pretty straightforward. I've confirmed that events are being handle correctly Java-side up until the point where the JS code should be triggered. However, they're not triggering my JavaScript code.

Based on the documentation, I've implemented the event handlers in Java in the createViewInstance method of my view manager:

map.setMapListener(new DelayedMapListener(new MapListener() {
    public boolean onScroll(ScrollEvent event) {
        WritableMap eventData = Arguments.createMap();

        // Fill in eventData; details not important

        ReactContext reactContext = (ReactContext)map.getContext();
        reactContext.getJSModule(RCTEventEmitter.class).receiveEvent(
            map.getId(),
            "topChange",
             eventData);

        return true;
    }

    public boolean onZoom(ZoomEvent event) {
        // Basically the same as above
    }
}, 100));

The relevant part of my JS code is basically identical to the code in the documentation linked above:

class OSMDroidMapView extends Component {
    constructor(props) {
        super(props);
        this._onChange = this._onChange.bind(this);
    }

    _onChange(event: Event) {
        console.log(event);
        // Handle event data
    }

    render() {
        return <OSMDroidMapView {...this.props} onChange={this._onChange}/>
    }
}

There are no errors or any signs that anything is wrong. The Java event handler code is called correctly. There's just no sign of anything happening in the JS code when an event occurs. Does anyone know how to do this correctly? I feel like I must be missing some basic concept here.

2

1 Answer 1

9

To send custom events to Javascript you can use RCTDeviceEventEmitter:

In the Native side:

import com.facebook.react.modules.core.DeviceEventManagerModule;

...

public boolean onScroll(ScrollEvent event) {
    WritableMap eventData = Arguments.createMap();

    // Fill in eventData; details not important

    ReactContext reactContext = (ReactContext)map.getContext();
    reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
                .emit("YouCustomEventName", eventData);

    return true;
}

Inside your JS module you just have to register a listener using DeviceEventEmitter:

class OSMDroidMapView extends Component {
    constructor(props) {
        super(props);
        this._onChange = this._onChange.bind(this);
    }

    componentWillMount() {
        DeviceEventEmitter.addListener('YouCustomEventName', this._onChange);
    }

    componentWillUnmount() {
        DeviceEventEmitter.removeListener('YouCustomEventName', this._onChange);
    }

    _onChange(event: Event) {
        console.log(event);
        // Handle event data
    }

    render() {
        return <OSMDroidMapView {...this.props} onChange={this._onChange}/>
    }
}

Hope it helps.

Sign up to request clarification or add additional context in comments.

3 Comments

This will trigger the event for everyone that os listening to it, not call a callback of an specific instance.
(ReactContext)map.getContext(); what is this ? AndroidStudio throws an error on this line
map is your custom Android UI component here

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.