From bf598647d2ff7aad1d29c7c5d28d816c01941c1c Mon Sep 17 00:00:00 2001 From: Krzysztof Magiera Date: Fri, 25 Sep 2015 03:12:20 -0700 Subject: [PATCH] Convert View to @ReactProp. Differential Revision: D2479795 committer: Service User --- .../react/uimanager/BaseViewManager.java | 160 +++++++++++++++++ .../uimanager/BaseViewPropertyApplicator.java | 2 + .../react/uimanager/SimpleViewManager.java | 15 +- .../com/facebook/react/uimanager/UIProp.java | 2 + .../react/uimanager/ViewGroupManager.java | 7 +- .../react/views/view/ReactViewManager.java | 164 ++++++++---------- 6 files changed, 245 insertions(+), 105 deletions(-) create mode 100644 ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManager.java diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManager.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManager.java new file mode 100644 index 000000000..ff5587d02 --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManager.java @@ -0,0 +1,160 @@ +// Copyright 2004-present Facebook. All Rights Reserved. + +package com.facebook.react.uimanager; + +import android.graphics.Color; +import android.os.Build; +import android.view.View; + +import com.facebook.react.bridge.ReadableMap; + +/** + * Base class that should be suitable for the majority of subclasses of {@link ViewManager}. + * It provides support for base view properties such as backgroundColor, opacity, etc. + */ +public abstract class BaseViewManager + extends ViewManager { + + private static final String PROP_BACKGROUND_COLOR = ViewProps.BACKGROUND_COLOR; + private static final String PROP_DECOMPOSED_MATRIX = "decomposedMatrix"; + private static final String PROP_DECOMPOSED_MATRIX_ROTATE = "rotate"; + private static final String PROP_DECOMPOSED_MATRIX_SCALE_X = "scaleX"; + private static final String PROP_DECOMPOSED_MATRIX_SCALE_Y = "scaleY"; + private static final String PROP_DECOMPOSED_MATRIX_TRANSLATE_X = "translateX"; + private static final String PROP_DECOMPOSED_MATRIX_TRANSLATE_Y = "translateY"; + private static final String PROP_OPACITY = "opacity"; + private static final String PROP_RENDER_TO_HARDWARE_TEXTURE = "renderToHardwareTextureAndroid"; + private static final String PROP_ACCESSIBILITY_LABEL = "accessibilityLabel"; + private static final String PROP_ACCESSIBILITY_COMPONENT_TYPE = "accessibilityComponentType"; + private static final String PROP_ACCESSIBILITY_LIVE_REGION = "accessibilityLiveRegion"; + private static final String PROP_IMPORTANT_FOR_ACCESSIBILITY = "importantForAccessibility"; + + // DEPRECATED + private static final String PROP_ROTATION = "rotation"; + private static final String PROP_SCALE_X = "scaleX"; + private static final String PROP_SCALE_Y = "scaleY"; + private static final String PROP_TRANSLATE_X = "translateX"; + private static final String PROP_TRANSLATE_Y = "translateY"; + + /** + * Used to locate views in end-to-end (UI) tests. + */ + public static final String PROP_TEST_ID = "testID"; + + + @ReactProp(name = PROP_BACKGROUND_COLOR, defaultInt = Color.TRANSPARENT, customType = "Color") + public void setBackgroundColor(T view, int backgroundColor) { + view.setBackgroundColor(backgroundColor); + } + + @ReactProp(name = PROP_DECOMPOSED_MATRIX) + public void setDecomposedMatrix(T view, ReadableMap decomposedMatrix) { + if (decomposedMatrix == null) { + resetTransformMatrix(view); + } else { + setTransformMatrix(view, decomposedMatrix); + } + } + + @ReactProp(name = PROP_OPACITY, defaultFloat = 1.f) + public void setOpacity(T view, float opacity) { + view.setAlpha(opacity); + } + + @ReactProp(name = PROP_RENDER_TO_HARDWARE_TEXTURE) + public void setRenderToHardwareTexture(T view, boolean useHWTexture) { + view.setLayerType(useHWTexture ? View.LAYER_TYPE_HARDWARE : View.LAYER_TYPE_NONE, null); + } + + @ReactProp(name = PROP_TEST_ID) + public void setTestId(T view, String testId) { + view.setTag(testId); + } + + @ReactProp(name = PROP_ACCESSIBILITY_LABEL) + public void setAccessibilityLabel(T view, String accessibilityLabel) { + view.setContentDescription(accessibilityLabel); + } + + @ReactProp(name = PROP_ACCESSIBILITY_COMPONENT_TYPE) + public void setAccessibilityComponentType(T view, String accessibilityComponentType) { + AccessibilityHelper.updateAccessibilityComponentType(view, accessibilityComponentType); + } + + @ReactProp(name = PROP_IMPORTANT_FOR_ACCESSIBILITY) + public void setImportantForAccessibility(T view, String importantForAccessibility) { + if (importantForAccessibility == null || importantForAccessibility.equals("auto")) { + view.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_AUTO); + } else if (importantForAccessibility.equals("yes")) { + view.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES); + } else if (importantForAccessibility.equals("no")) { + view.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO); + } else if (importantForAccessibility.equals("no-hide-descendants")) { + view.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS); + } + } + + @Deprecated + @ReactProp(name = PROP_ROTATION) + public void setRotation(T view, float rotation) { + view.setRotation(rotation); + } + + @Deprecated + @ReactProp(name = PROP_SCALE_X, defaultFloat = 1f) + public void setScaleX(T view, float scaleX) { + view.setScaleX(scaleX); + } + + @Deprecated + @ReactProp(name = PROP_SCALE_Y, defaultFloat = 1f) + public void setScaleY(T view, float scaleY) { + view.setScaleY(scaleY); + } + + @Deprecated + @ReactProp(name = PROP_TRANSLATE_X, defaultFloat = 1f) + public void setTranslateX(T view, float translateX) { + view.setTranslationX(translateX); + } + + @Deprecated + @ReactProp(name = PROP_TRANSLATE_Y, defaultFloat = 1f) + public void setTranslateY(T view, float translateY) { + view.setTranslationY(translateY); + } + + @ReactProp(name = PROP_ACCESSIBILITY_LIVE_REGION) + public void setAccessibilityLiveRegion(T view, String liveRegion) { + if (Build.VERSION.SDK_INT >= 19) { + if (liveRegion == null || liveRegion.equals("none")) { + view.setAccessibilityLiveRegion(View.ACCESSIBILITY_LIVE_REGION_NONE); + } else if (liveRegion.equals("polite")) { + view.setAccessibilityLiveRegion(View.ACCESSIBILITY_LIVE_REGION_POLITE); + } else if (liveRegion.equals("assertive")) { + view.setAccessibilityLiveRegion(View.ACCESSIBILITY_LIVE_REGION_ASSERTIVE); + } + } + } + + private static void setTransformMatrix(View view, ReadableMap matrix) { + view.setTranslationX(PixelUtil.toPixelFromDIP( + (float) matrix.getDouble(PROP_DECOMPOSED_MATRIX_TRANSLATE_X))); + view.setTranslationY(PixelUtil.toPixelFromDIP( + (float) matrix.getDouble(PROP_DECOMPOSED_MATRIX_TRANSLATE_Y))); + view.setRotation( + (float) matrix.getDouble(PROP_DECOMPOSED_MATRIX_ROTATE)); + view.setScaleX( + (float) matrix.getDouble(PROP_DECOMPOSED_MATRIX_SCALE_X)); + view.setScaleY( + (float) matrix.getDouble(PROP_DECOMPOSED_MATRIX_SCALE_Y)); + } + + private static void resetTransformMatrix(View view) { + view.setTranslationX(PixelUtil.toPixelFromDIP(0)); + view.setTranslationY(PixelUtil.toPixelFromDIP(0)); + view.setRotation(0); + view.setScaleX(1); + view.setScaleY(1); + } +} diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewPropertyApplicator.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewPropertyApplicator.java index 2b2b0b0f7..ec9117e97 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewPropertyApplicator.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewPropertyApplicator.java @@ -20,6 +20,8 @@ import com.facebook.react.bridge.ReadableMap; /** * Takes common view properties from JS and applies them to a given {@link View}. + * + * TODO(krzysztof): Blow away this class once refactoring is complete */ public class BaseViewPropertyApplicator { diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/SimpleViewManager.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/SimpleViewManager.java index f338776e9..434013ea9 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/SimpleViewManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/SimpleViewManager.java @@ -12,24 +12,21 @@ package com.facebook.react.uimanager; import android.view.View; /** - * A partial implementation of {@link ViewManager} that applies common properties such as background - * color, opacity and CSS layout. Implementations should make sure to call - * {@code super.updateView()} in order for these properties to be applied. + * Common base class for most of the {@link ViewManager}s. It provides support for most common + * properties through extending {@link BaseViewManager}. It also reduces boilerplate by specifying + * the type of shadow node to be {@link ReactShadowNode} and providing default, empty implementation + * for some of the methods of {@link ViewManager} interface. * * @param the view handled by this manager */ -public abstract class SimpleViewManager extends ViewManager { +public abstract class SimpleViewManager extends + BaseViewManager { @Override public ReactShadowNode createCSSNodeInstance() { return new ReactShadowNode(); } - @Override - public void updateView(T root, CatalystStylesDiffMap props) { - BaseViewPropertyApplicator.applyCommonViewProperties(root, props); - } - @Override public void updateExtraData(T root, Object extraData) { } diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIProp.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIProp.java index 27ccd4b60..c209811a8 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIProp.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIProp.java @@ -25,6 +25,8 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME; * @UIProp(UIProp.Type.BOOLEAN) public static final String PROP_FOO = "foo"; * @UIProp(UIProp.Type.STRING) public static final String PROP_BAR = "bar"; * } + * + * TODO(krzysztof): Kill this class once @ReactProp refactoring is done */ @Target(ElementType.FIELD) @Retention(RUNTIME) diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewGroupManager.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewGroupManager.java index eb0b3ee63..7847aa59c 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewGroupManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewGroupManager.java @@ -16,18 +16,13 @@ import android.view.ViewGroup; * Class providing children management API for view managers of classes extending ViewGroup. */ public abstract class ViewGroupManager - extends ViewManager { + extends BaseViewManager { @Override public ReactShadowNode createCSSNodeInstance() { return new ReactShadowNode(); } - @Override - public void updateView(T root, CatalystStylesDiffMap props) { - BaseViewPropertyApplicator.applyCommonViewProperties(root, props); - } - @Override public void updateExtraData(T root, Object extraData) { } diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewManager.java index a316e799f..33fd63b57 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewManager.java @@ -25,12 +25,12 @@ import com.facebook.react.bridge.ReadableArray; import com.facebook.react.bridge.ReadableMap; import com.facebook.react.common.MapBuilder; import com.facebook.react.common.annotations.VisibleForTesting; -import com.facebook.react.uimanager.BaseViewPropertyApplicator; import com.facebook.react.uimanager.CatalystStylesDiffMap; import com.facebook.react.uimanager.PixelUtil; import com.facebook.react.uimanager.PointerEvents; +import com.facebook.react.uimanager.ReactProp; +import com.facebook.react.uimanager.ReactPropGroup; import com.facebook.react.uimanager.ThemedReactContext; -import com.facebook.react.uimanager.UIProp; import com.facebook.react.uimanager.ViewGroupManager; import com.facebook.react.uimanager.ViewProps; @@ -45,18 +45,82 @@ public class ReactViewManager extends ViewGroupManager { private static final int[] SPACING_TYPES = { Spacing.ALL, Spacing.LEFT, Spacing.RIGHT, Spacing.TOP, Spacing.BOTTOM, }; - private static final String[] PROPS_BORDER_COLOR = { - "borderColor", "borderLeftColor", "borderRightColor", "borderTopColor", "borderBottomColor" - }; private static final int CMD_HOTSPOT_UPDATE = 1; private static final int CMD_SET_PRESSED = 2; private static final int[] sLocationBuf = new int[2]; - @UIProp(UIProp.Type.STRING) public static final String PROP_ACCESSIBLE = "accessible"; - @UIProp(UIProp.Type.NUMBER) public static final String PROP_BORDER_RADIUS = "borderRadius"; - @UIProp(UIProp.Type.STRING) public static final String PROP_BORDER_STYLE = "borderStyle"; - @UIProp(UIProp.Type.STRING) public static final String PROP_POINTER_EVENTS = "pointerEvents"; - @UIProp(UIProp.Type.MAP) public static final String PROP_NATIVE_BG = "nativeBackgroundAndroid"; + @ReactProp(name = "accessible") + public void setAccessible(ReactViewGroup view, boolean accessible) { + view.setFocusable(accessible); + } + + @ReactProp(name = "borderRadius") + public void setBorderRadius(ReactViewGroup view, float borderRadius) { + view.setBorderRadius(PixelUtil.toPixelFromDIP(borderRadius)); + } + + @ReactProp(name = "borderStyle") + public void setBorderStyle(ReactViewGroup view, @Nullable String borderStyle) { + view.setBorderStyle(borderStyle); + } + + @ReactProp(name = "pointerEvents") + public void setPointerEvents(ReactViewGroup view, @Nullable String pointerEventsStr) { + if (pointerEventsStr != null) { + PointerEvents pointerEvents = + PointerEvents.valueOf(pointerEventsStr.toUpperCase(Locale.US).replace("-", "_")); + view.setPointerEvents(pointerEvents); + } + } + + @ReactProp(name = "nativeBackgroundAndroid") + public void setNativeBackground(ReactViewGroup view, @Nullable ReadableMap bg) { + view.setTranslucentBackgroundDrawable(bg == null ? + null : ReactDrawableHelper.createDrawableFromJSDescription(view.getContext(), bg)); + } + + @ReactProp(name = ViewProps.BORDER_WIDTH, defaultFloat = CSSConstants.UNDEFINED) + public void setBorderWidth(ReactViewGroup view, float width) { + if (!CSSConstants.isUndefined(width)) { + width = PixelUtil.toPixelFromDIP(width); + } + view.setBorderWidth(Spacing.ALL, width); + } + + @ReactProp(name = ReactClippingViewGroupHelper.PROP_REMOVE_CLIPPED_SUBVIEWS) + public void setRemoveClippedSubviews(ReactViewGroup view, boolean removeClippedSubviews) { + view.setRemoveClippedSubviews(removeClippedSubviews); + } + + @ReactProp(name = ViewProps.NEEDS_OFFSCREEN_ALPHA_COMPOSITING) + public void setNeedsOffscreenAlphaCompositing( + ReactViewGroup view, + boolean needsOffscreenAlphaCompositing) { + view.setNeedsOffscreenAlphaCompositing(needsOffscreenAlphaCompositing); + } + + @ReactPropGroup(names = { + ViewProps.BORDER_WIDTH, + ViewProps.BORDER_LEFT_WIDTH, + ViewProps.BORDER_RIGHT_WIDTH, + ViewProps.BORDER_TOP_WIDTH, + ViewProps.BORDER_BOTTOM_WIDTH, + }, defaultFloat = CSSConstants.UNDEFINED) + public void setBorderWidth(ReactViewGroup view, int index, float width) { + if (!CSSConstants.isUndefined(width)) { + width = PixelUtil.toPixelFromDIP(width); + } + view.setBorderWidth(SPACING_TYPES[index], width); + } + + @ReactPropGroup(names = { + "borderColor", "borderLeftColor", "borderRightColor", "borderTopColor", "borderBottomColor" + }, customType = "Color") + public void setBorderColor(ReactViewGroup view, int index, Integer color) { + view.setBorderColor( + SPACING_TYPES[index], + color == null ? CSSConstants.UNDEFINED : (float) color); + } @Override public String getName() { @@ -68,86 +132,6 @@ public class ReactViewManager extends ViewGroupManager { return new ReactViewGroup(context); } - @Override - public Map getNativeProps() { - Map nativeProps = super.getNativeProps(); - Map baseProps = BaseViewPropertyApplicator.getCommonProps(); - for (Map.Entry entry : baseProps.entrySet()) { - nativeProps.put(entry.getKey(), entry.getValue().toString()); - } - for (int i = 0; i < SPACING_TYPES.length; i++) { - nativeProps.put(ViewProps.BORDER_WIDTHS[i], UIProp.Type.NUMBER.toString()); - nativeProps.put(PROPS_BORDER_COLOR[i], UIProp.Type.STRING.toString()); - } - return nativeProps; - } - - @Override - public void updateView(ReactViewGroup view, CatalystStylesDiffMap props) { - super.updateView(view, props); - ReactClippingViewGroupHelper.applyRemoveClippedSubviewsProperty(view, props); - - // Border widths - for (int i = 0; i < SPACING_TYPES.length; i++) { - String key = ViewProps.BORDER_WIDTHS[i]; - if (props.hasKey(key)) { - float width = props.getFloat(key, CSSConstants.UNDEFINED); - if (!CSSConstants.isUndefined(width)) { - width = PixelUtil.toPixelFromDIP(width); - } - view.setBorderWidth(SPACING_TYPES[i], width); - } - } - - // Border colors - for (int i = 0; i < SPACING_TYPES.length; i++) { - String key = PROPS_BORDER_COLOR[i]; - if (props.hasKey(key)) { - float color = CSSConstants.UNDEFINED; - if (!props.isNull(PROPS_BORDER_COLOR[i])) { - // Check CatalystStylesDiffMap#getColorInt() to see why this is needed - int colorInt = props.getInt(PROPS_BORDER_COLOR[i], Color.TRANSPARENT); - color = colorInt; - } - view.setBorderColor(SPACING_TYPES[i], color); - } - } - - // Border radius - if (props.hasKey(PROP_BORDER_RADIUS)) { - view.setBorderRadius(PixelUtil.toPixelFromDIP(props.getFloat(PROP_BORDER_RADIUS, 0.0f))); - } - - if (props.hasKey(PROP_BORDER_STYLE)) { - view.setBorderStyle(props.getString(PROP_BORDER_STYLE)); - } - - if (props.hasKey(PROP_POINTER_EVENTS)) { - String pointerEventsStr = props.getString(PROP_POINTER_EVENTS); - if (pointerEventsStr != null) { - PointerEvents pointerEvents = - PointerEvents.valueOf(pointerEventsStr.toUpperCase(Locale.US).replace("-", "_")); - view.setPointerEvents(pointerEvents); - } - } - - // Native background - if (props.hasKey(PROP_NATIVE_BG)) { - ReadableMap map = props.getMap(PROP_NATIVE_BG); - view.setTranslucentBackgroundDrawable(map == null ? - null : ReactDrawableHelper.createDrawableFromJSDescription(view.getContext(), map)); - } - - if (props.hasKey(PROP_ACCESSIBLE)) { - view.setFocusable(props.getBoolean(PROP_ACCESSIBLE, false)); - } - - if (props.hasKey(ViewProps.NEEDS_OFFSCREEN_ALPHA_COMPOSITING)) { - view.setNeedsOffscreenAlphaCompositing( - props.getBoolean(ViewProps.NEEDS_OFFSCREEN_ALPHA_COMPOSITING, false)); - } - } - @Override public Map getCommandsMap() { return MapBuilder.of("hotspotUpdate", CMD_HOTSPOT_UPDATE, "setPressed", CMD_SET_PRESSED);