/** * Copyright (c) 2015-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. */ package com.facebook.react.uimanager; import javax.annotation.Nullable; import java.util.Map; import android.view.View; import com.facebook.react.bridge.BaseJavaModule; import com.facebook.react.bridge.ReadableArray; import com.facebook.react.touch.JSResponderHandler; import com.facebook.react.touch.ReactInterceptingViewGroup; import com.facebook.react.uimanager.annotations.ReactProp; import com.facebook.react.uimanager.annotations.ReactPropGroup; import com.facebook.react.uimanager.annotations.ReactPropertyHolder; /** * Class responsible for knowing how to create and update catalyst Views of a given type. It is also * responsible for creating and updating CSSNodeDEPRECATED subclasses used for calculating position and size * for the corresponding native view. */ @ReactPropertyHolder public abstract class ViewManager<T extends View, C extends ReactShadowNode> extends BaseJavaModule { public final void updateProperties(T viewToUpdate, ReactStylesDiffMap props) { ViewManagerPropertyUpdater.updateProps(this, viewToUpdate, props); onAfterUpdateTransaction(viewToUpdate); } /** * Creates a view and installs event emitters on it. */ public final T createView( ThemedReactContext reactContext, JSResponderHandler jsResponderHandler) { T view = createViewInstance(reactContext); addEventEmitters(reactContext, view); if (view instanceof ReactInterceptingViewGroup) { ((ReactInterceptingViewGroup) view).setOnInterceptTouchEventListener(jsResponderHandler); } return view; } /** * @return the name of this view manager. This will be the name used to reference this view * manager from JavaScript in createReactNativeComponentClass. */ public abstract String getName(); /** * This method should return a subclass of {@link ReactShadowNode} which will be then used for * measuring position and size of the view. In mose of the cases this should just return an * instance of {@link ReactShadowNode} */ public abstract C createShadowNodeInstance(); /** * This method should return {@link Class} instance that represent type of shadow node that this * manager will return from {@link #createShadowNodeInstance}. * * This method will be used in the bridge initialization phase to collect properties exposed using * {@link ReactProp} (or {@link ReactPropGroup}) annotation from the {@link ReactShadowNode} * subclass specific for native view this manager provides. * * @return {@link Class} object that represents type of shadow node used by this view manager. */ public abstract Class<? extends C> getShadowNodeClass(); /** * Subclasses should return a new View instance of the proper type. * @param reactContext */ protected abstract T createViewInstance(ThemedReactContext reactContext); /** * Called when view is detached from view hierarchy and allows for some additional cleanup by * the {@link ViewManager} subclass. */ public void onDropViewInstance(T view) { } /** * Subclasses can override this method to install custom event emitters on the given View. You * might want to override this method if your view needs to emit events besides basic touch events * to JS (e.g. scroll events). */ protected void addEventEmitters(ThemedReactContext reactContext, T view) { } /** * Callback that will be triggered after all properties are updated in current update transaction * (all @ReactProp handlers for properties updated in current transaction have been called). If * you want to override this method you should call super.onAfterUpdateTransaction from it as * the parent class of the ViewManager may rely on callback being executed. */ protected void onAfterUpdateTransaction(T view) { } /** * Subclasses can implement this method to receive an optional extra data enqueued from the * corresponding instance of {@link ReactShadowNode} in * {@link ReactShadowNode#onCollectExtraUpdates}. * * Since css layout step and ui updates can be executed in separate thread apart of setting * x/y/width/height this is the recommended and thread-safe way of passing extra data from css * node to the native view counterpart. * * TODO(7247021): Replace updateExtraData with generic update props mechanism after D2086999 */ public abstract void updateExtraData(T root, Object extraData); /** * Subclasses may use this method to receive events/commands directly from JS through the * {@link UIManager}. Good example of such a command would be {@code scrollTo} request with * coordinates for a {@link ScrollView} or {@code goBack} request for a {@link WebView} instance. * * @param root View instance that should receive the command * @param commandId code of the command * @param args optional arguments for the command */ public void receiveCommand(T root, int commandId, @Nullable ReadableArray args) { } /** * Subclasses of {@link ViewManager} that expect to receive commands through * {@link UIManagerModule#dispatchViewManagerCommand} should override this method returning the * map between names of the commands and IDs that are then used in {@link #receiveCommand} method * whenever the command is dispatched for this particular {@link ViewManager}. * * As an example we may consider {@link ReactWebViewManager} that expose the following commands: * goBack, goForward, reload. In this case the map returned from {@link #getCommandsMap} from * {@link ReactWebViewManager} will look as follows: * { * "goBack": 1, * "goForward": 2, * "reload": 3, * } * * Now assuming that "reload" command is dispatched through {@link UIManagerModule} we trigger * {@link ReactWebViewManager#receiveCommand} passing "3" as {@code commandId} argument. * * @return map of string to int mapping of the expected commands */ public @Nullable Map<String, Integer> getCommandsMap() { return null; } /** * Returns a map of config data passed to JS that defines eligible events that can be placed on * native views. This should return bubbling directly-dispatched event types and specify what * names should be used to subscribe to either form (bubbling/capturing). * * Returned map should be of the form: * { * "onTwirl": { * "phasedRegistrationNames": { * "bubbled": "onTwirl", * "captured": "onTwirlCaptured" * } * } * } */ public @Nullable Map<String, Object> getExportedCustomBubblingEventTypeConstants() { return null; } /** * Returns a map of config data passed to JS that defines eligible events that can be placed on * native views. This should return non-bubbling directly-dispatched event types. * * Returned map should be of the form: * { * "onTwirl": { * "registrationName": "onTwirl" * } * } */ public @Nullable Map<String, Object> getExportedCustomDirectEventTypeConstants() { return null; } /** * Returns a map of view-specific constants that are injected to JavaScript. These constants are * made accessible via UIManager.<ViewName>.Constants. */ public @Nullable Map<String, Object> getExportedViewConstants() { return null; } public Map<String, String> getNativeProps() { return ViewManagerPropertyUpdater.getNativeProps(getClass(), getShadowNodeClass()); } }