diff --git a/modules/android/hippy_support/src/main/java/com/tencent/mtt/hippy/annotation/HippyControllerProps.java b/modules/android/hippy_support/src/main/java/com/tencent/mtt/hippy/annotation/HippyControllerProps.java index 38f34ada924..b8c6d0c96a3 100644 --- a/modules/android/hippy_support/src/main/java/com/tencent/mtt/hippy/annotation/HippyControllerProps.java +++ b/modules/android/hippy_support/src/main/java/com/tencent/mtt/hippy/annotation/HippyControllerProps.java @@ -35,7 +35,7 @@ String ARRAY = "array"; String MAP = "map"; - String name() default "name"; + String name(); /* * defaultType Number boolean string else default is do not check diff --git a/renderer/native/android/src/main/java/com/tencent/mtt/hippy/uimanager/ControllerUpdateManger.java b/renderer/native/android/src/main/java/com/tencent/mtt/hippy/uimanager/ControllerUpdateManger.java index 7beb146bb8d..aa024048ae3 100644 --- a/renderer/native/android/src/main/java/com/tencent/mtt/hippy/uimanager/ControllerUpdateManger.java +++ b/renderer/native/android/src/main/java/com/tencent/mtt/hippy/uimanager/ControllerUpdateManger.java @@ -129,15 +129,27 @@ private static void initComponentPropsMap() { HippyControllerProps controllerProps = method .getAnnotation(HippyControllerProps.class); if (controllerProps != null) { - if (!sComponentPropsMethodMap.containsKey(controllerProps.name())) { - sTextPropsSet.add(controllerProps.name()); + String name = controllerProps.name(); + if (!isComponentProps(name)) { + sTextPropsSet.add(name); } - sRenderPropsList.add(controllerProps.name()); + sRenderPropsList.add(name); } } Collections.addAll(sRenderPropsList, sLayoutStyleList); } + private static boolean isComponentProps(String name) { + if (sComponentPropsMethodMap.containsKey(name)) { + return true; + } + // Special case: property "opacity" in TextVirtualNode also need to process in HippyViewController + if (NodeProps.OPACITY.equals(name)) { + return true; + } + return false; + } + void findViewPropsMethod(Class cls, @NonNull Map methodHolderMap) { if (cls != HippyViewController.class) { diff --git a/renderer/native/android/src/main/java/com/tencent/renderer/component/text/TextImageSpan.java b/renderer/native/android/src/main/java/com/tencent/renderer/component/text/TextImageSpan.java index 680ad01550c..8d27a76b768 100644 --- a/renderer/native/android/src/main/java/com/tencent/renderer/component/text/TextImageSpan.java +++ b/renderer/native/android/src/main/java/com/tencent/renderer/component/text/TextImageSpan.java @@ -98,6 +98,7 @@ public class TextImageSpan extends ImageSpan { @Nullable private Paint mBackgroundPaint = null; private int mTintColor; + private final int mAlpha; public TextImageSpan(Drawable drawable, String source, @NonNull ImageVirtualNode node, @NonNull NativeRender nativeRenderer) { @@ -123,6 +124,8 @@ public TextImageSpan(Drawable drawable, String source, @NonNull ImageVirtualNode mBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mBackgroundPaint.setColor(node.getBackgroundColor()); } + // multiple alpha bits (0xFF) to convert opacity into alpha + mAlpha = Math.round(node.getFinalOpacity() * 255); setUrl(source); } @@ -177,7 +180,7 @@ public void draw(@NonNull Canvas canvas, CharSequence text, int start, int end, if (mMeasuredWidth == 0 || mMeasuredHeight == 0) { return; } - canvas.save(); + int count = canvas.save(); int transY; assert mVerticalAlign != null; switch (mVerticalAlign) { @@ -197,6 +200,9 @@ public void draw(@NonNull Canvas canvas, CharSequence text, int start, int end, } canvas.translate(x + mMarginLeft, transY); + if (mAlpha < 255) { + canvas.saveLayerAlpha(0, 0, mMeasuredWidth, mMeasuredHeight, mAlpha); + } if (mBackgroundPaint != null) { canvas.drawRect(0, 0, mMeasuredWidth, mMeasuredHeight, mBackgroundPaint); } @@ -215,7 +221,7 @@ public void draw(@NonNull Canvas canvas, CharSequence text, int start, int end, canvas.scale(scaleX, scaleY, 0, 0); drawable.draw(canvas); } - canvas.restore(); + canvas.restoreToCount(count); } private void legacyDraw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, diff --git a/renderer/native/android/src/main/java/com/tencent/renderer/node/ImageVirtualNode.java b/renderer/native/android/src/main/java/com/tencent/renderer/node/ImageVirtualNode.java index 7ab62c59c21..f60827c171e 100644 --- a/renderer/native/android/src/main/java/com/tencent/renderer/node/ImageVirtualNode.java +++ b/renderer/native/android/src/main/java/com/tencent/renderer/node/ImageVirtualNode.java @@ -307,6 +307,11 @@ public void setVerticalAlign(String align) { markDirty(); } + @HippyControllerProps(name = NodeProps.OPACITY, defaultType = HippyControllerProps.NUMBER, defaultNumber = 1f) + public void setOpacity(float opacity) { + super.setOpacity(opacity); + } + @SuppressWarnings("unused") @HippyControllerProps(name = "defaultSource", defaultType = HippyControllerProps.STRING) public void setDefaultSource(String defaultSource) { diff --git a/renderer/native/android/src/main/java/com/tencent/renderer/node/TextVirtualNode.java b/renderer/native/android/src/main/java/com/tencent/renderer/node/TextVirtualNode.java index bc5b6826bd8..5dc68570856 100644 --- a/renderer/native/android/src/main/java/com/tencent/renderer/node/TextVirtualNode.java +++ b/renderer/native/android/src/main/java/com/tencent/renderer/node/TextVirtualNode.java @@ -398,8 +398,8 @@ protected void createChildrenSpanOperation(@NonNull List ops, } } - protected TextForegroundColorSpan createForegroundColorSpan() { - return new TextForegroundColorSpan(mColor); + protected TextForegroundColorSpan createForegroundColorSpan(float opacity) { + return new TextForegroundColorSpan(colorWithOpacity(mColor, opacity)); } protected void createSpanOperationImpl(@NonNull List ops, @@ -416,9 +416,13 @@ protected void createSpanOperationImpl(@NonNull List ops, TextVerticalAlignSpan span = new TextVerticalAlignSpan(verticalAlign); ops.add(new SpanOperation(start, end, span, SpanOperation.PRIORITY_LOWEST)); } - ops.add(new SpanOperation(start, end, createForegroundColorSpan())); + float opacity = getFinalOpacity(); + ops.add(new SpanOperation(start, end, createForegroundColorSpan(opacity))); if (mBackgroundColor != Color.TRANSPARENT && mParent != null) { - ops.add(new SpanOperation(start, end, new BackgroundColorSpan(mBackgroundColor))); + int color = colorWithOpacity(mBackgroundColor, opacity); + if (color != Color.TRANSPARENT) { + ops.add(new SpanOperation(start, end, new BackgroundColorSpan(color))); + } } if (mLetterSpacing != 0) { ops.add(new SpanOperation(start, end, @@ -437,9 +441,11 @@ protected void createSpanOperationImpl(@NonNull List ops, ops.add(new SpanOperation(start, end, new StrikethroughSpan())); } if (mShadowOffsetDx != 0 || mShadowOffsetDy != 0) { - ops.add(new SpanOperation(start, end, - new TextShadowSpan(mShadowOffsetDx, mShadowOffsetDy, mShadowRadius, - mShadowColor))); + int color = colorWithOpacity(mShadowColor, opacity); + if (color != Color.TRANSPARENT) { + ops.add(new SpanOperation(start, end, + new TextShadowSpan(mShadowOffsetDx, mShadowOffsetDy, mShadowRadius, color))); + } } if (mEventTypes != null && mEventTypes.size() > 0) { TextGestureSpan span = new TextGestureSpan(mId); @@ -463,6 +469,16 @@ protected void createSpanOperationImpl(@NonNull List ops, } } + public static int colorWithOpacity(int color, float opacity) { + if (opacity >= 1) { + return color; + } else if (opacity > 0) { + int alpha = (color >> 24) & 0xFF; + return (Math.round(alpha * opacity) << 24) | (color & 0xFFFFFF); + } + return Color.TRANSPARENT; + } + protected float getLineSpacingMultiplier() { return mLineSpacingMultiplier <= 0 ? 1.0f : mLineSpacingMultiplier; } @@ -781,4 +797,13 @@ public String getVerticalAlign() { } return null; } + + @HippyControllerProps(name = NodeProps.OPACITY, defaultType = HippyControllerProps.NUMBER, defaultNumber = 1f) + public void setOpacity(float opacity) { + // top-level opacity will be handled by HippyViewController, so only sub-level opacity needs to be considered + if (mParent != null) { + super.setOpacity(opacity); + } + } + } diff --git a/renderer/native/android/src/main/java/com/tencent/renderer/node/VirtualNode.java b/renderer/native/android/src/main/java/com/tencent/renderer/node/VirtualNode.java index 644b0e0c2eb..f5c9cc72ba4 100644 --- a/renderer/native/android/src/main/java/com/tencent/renderer/node/VirtualNode.java +++ b/renderer/native/android/src/main/java/com/tencent/renderer/node/VirtualNode.java @@ -39,6 +39,7 @@ public abstract class VirtualNode { protected VirtualNode mParent; @Nullable protected List mEventTypes; + protected float mOpacity = 1f; public VirtualNode(int rootId, int id, int pid, int index) { mRootId = rootId; @@ -137,6 +138,18 @@ public int getChildCount() { return mChildren.size(); } + public void setOpacity(float opacity) { + opacity = Math.min(Math.max(0, opacity), 1); + if (opacity != mOpacity) { + mOpacity = opacity; + markDirty(); + } + } + + public float getFinalOpacity() { + return mParent == null ? mOpacity : mParent.getFinalOpacity() * mOpacity; + } + protected static class SpanOperation { public static final int PRIORITY_DEFAULT = 1;