com.facebook.react.uimanager.ViewManager.java Source code

Java tutorial

Introduction

Here is the source code for com.facebook.react.uimanager.ViewManager.java

Source

/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.react.uimanager;

import android.content.Context;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.facebook.react.bridge.BaseJavaModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.config.ReactFeatureFlags;
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;
import com.facebook.yoga.YogaMeasureMode;
import java.util.Map;

/**
 * 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 {

    /**
     * For the vast majority of ViewManagers, you will not need to override this. Only override this
     * if you really know what you're doing and have a very unique use-case.
     *
     * @param viewToUpdate
     * @param props
     * @param stateWrapper
     */
    public void updateProperties(@NonNull T viewToUpdate, ReactStylesDiffMap props) {
        final ViewManagerDelegate<T> delegate;
        if (ReactFeatureFlags.useViewManagerDelegates && (delegate = getDelegate()) != null) {
            ViewManagerPropertyUpdater.updateProps(delegate, viewToUpdate, props);
        } else {
            ViewManagerPropertyUpdater.updateProps(this, viewToUpdate, props);
        }
        onAfterUpdateTransaction(viewToUpdate);
    }

    /**
     * Override this method and return an instance of {@link ViewManagerDelegate} if the props of the
     * view managed by this view manager should be set via this delegate. The provided instance will
     * then get calls to {@link ViewManagerDelegate#setProperty(View, String, Object)} for every prop
     * that must be updated and it's the delegate's responsibility to apply these values to the view.
     *
     * <p>By default this method returns {@code null}, which means that the view manager doesn't have
     * a delegate and the view props should be set internally by the view manager itself.
     *
     * @return an instance of {@link ViewManagerDelegate} if the props of the view managed by this
     *     view manager should be set via this delegate
     */
    @Nullable
    protected ViewManagerDelegate<T> getDelegate() {
        return null;
    }

    /** Creates a view and installs event emitters on it. */
    private final @NonNull T createView(@NonNull ThemedReactContext reactContext,
            JSResponderHandler jsResponderHandler) {
        return createView(reactContext, null, null, jsResponderHandler);
    }

    /** Creates a view with knowledge of props. */
    public @NonNull T createView(@NonNull ThemedReactContext reactContext, @Nullable ReactStylesDiffMap props,
            @Nullable StateWrapper stateWrapper, JSResponderHandler jsResponderHandler) {
        T view = createViewInstance(reactContext, props, stateWrapper);
        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 @NonNull 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 most of the cases this should just return an
     * instance of {@link ReactShadowNode}
     */
    public C createShadowNodeInstance() {
        throw new RuntimeException("ViewManager subclasses must implement createShadowNodeInstance()");
    }

    public @NonNull C createShadowNodeInstance(@NonNull ReactApplicationContext context) {
        return createShadowNodeInstance();
    }

    /**
     * This method should return {@link Class} instance that represent type of shadow node that this
     * manager will return from {@link #createShadowNodeInstance}.
     *
     * <p>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 @NonNull T createViewInstance(@NonNull ThemedReactContext reactContext);

    /**
     * Subclasses should return a new View instance of the proper type. This is an optional method
     * that will call createViewInstance for you. Override it if you need props upon creation of the
     * view.
     *
     * @param reactContext
     */
    protected @NonNull T createViewInstance(@NonNull ThemedReactContext reactContext,
            @Nullable ReactStylesDiffMap initialProps, @Nullable StateWrapper stateWrapper) {
        T view = createViewInstance(reactContext);
        addEventEmitters(reactContext, view);
        if (initialProps != null) {
            updateProperties(view, initialProps);
        }
        if (stateWrapper != null) {
            Object extraData = updateState(view, initialProps, stateWrapper);
            if (extraData != null) {
                updateExtraData(view, extraData);
            }
        }
        return view;
    }

    /**
     * Called when view is detached from view hierarchy and allows for some additional cleanup by the
     * {@link ViewManager} subclass.
     */
    public void onDropViewInstance(@NonNull 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(@NonNull ThemedReactContext reactContext, @NonNull 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(@NonNull 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}.
     *
     * <p>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.
     *
     * <p>TODO T7247021: Replace updateExtraData with generic update props mechanism after D2086999
     */
    public abstract void updateExtraData(@NonNull 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} instance.
     *
     * <p>This method is deprecated use {@link #receiveCommand(View, String, ReadableArray)} instead.
     *
     * @param root View instance that should receive the command
     * @param commandId code of the command
     * @param args optional arguments for the command
     */
    @Deprecated
    public void receiveCommand(@NonNull T root, int commandId, @Nullable ReadableArray args) {
    }

    /**
     * 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 ReactScrollView} 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(@NonNull T root, String 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}.
     *
     * @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).
     *
     * <p>Returned map should be of the form:
     *
     * <pre>
     * {
     *   "onTwirl": {
     *     "phasedRegistrationNames": {
     *       "bubbled": "onTwirl",
     *       "captured": "onTwirlCaptured"
     *     }
     *   }
     * }
     * </pre>
     */
    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.
     *
     * <p>Returned map should be of the form:
     *
     * <pre>
     * {
     *   "onTwirl": {
     *     "registrationName": "onTwirl"
     *   }
     * }
     * </pre>
     */
    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());
    }

    public @Nullable Object updateLocalData(@NonNull T view, ReactStylesDiffMap props,
            ReactStylesDiffMap localData) {
        return null;
    }

    /**
     * Subclasses can implement this method to receive state updates shared between all instances of
     * this component type.
     */
    public @Nullable Object updateState(@NonNull T view, ReactStylesDiffMap props,
            @Nullable StateWrapper stateWrapper) {
        return null;
    }

    public long measure(Context context, ReadableMap localData, ReadableMap props, ReadableMap state, float width,
            YogaMeasureMode widthMode, float height, YogaMeasureMode heightMode) {
        return 0;
    }

    /**
     * Subclasses can override this method to set padding for the given View in Fabric. Since not all
     * components support setting padding, the default implementation of this method does nothing.
     */
    public void setPadding(T view, int left, int top, int right, int bottom) {
    }
}