diff --git a/packages/react-native/ReactAndroid/api/ReactAndroid.api b/packages/react-native/ReactAndroid/api/ReactAndroid.api index fb8e6614e9cab2..7b8c47654476e3 100644 --- a/packages/react-native/ReactAndroid/api/ReactAndroid.api +++ b/packages/react-native/ReactAndroid/api/ReactAndroid.api @@ -377,6 +377,7 @@ public class com/facebook/react/ReactRootView : android/widget/FrameLayout, com/ protected fun dispatchJSPointerEvent (Landroid/view/MotionEvent;Z)V protected fun dispatchJSTouchEvent (Landroid/view/MotionEvent;)V public fun dispatchKeyEvent (Landroid/view/KeyEvent;)Z + protected fun drawChild (Landroid/graphics/Canvas;Landroid/view/View;J)Z protected fun finalize ()V public fun getAppProperties ()Landroid/os/Bundle; public fun getCurrentReactContext ()Lcom/facebook/react/bridge/ReactContext; diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactRootView.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactRootView.java index 90c638e64f3b67..a42933c8a5afd1 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactRootView.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactRootView.java @@ -8,13 +8,16 @@ package com.facebook.react; import static com.facebook.infer.annotation.ThreadConfined.UI; +import static com.facebook.react.uimanager.BlendModeHelper.needsIsolatedLayer; import static com.facebook.react.uimanager.common.UIManagerType.DEFAULT; import static com.facebook.react.uimanager.common.UIManagerType.FABRIC; import static com.facebook.systrace.Systrace.TRACE_TAG_REACT_JAVA_BRIDGE; import android.content.Context; +import android.graphics.BlendMode; import android.graphics.Canvas; import android.graphics.Insets; +import android.graphics.Paint; import android.graphics.Point; import android.graphics.Rect; import android.os.Build; @@ -65,6 +68,7 @@ import com.facebook.react.uimanager.RootViewUtil; import com.facebook.react.uimanager.UIManagerHelper; import com.facebook.react.uimanager.common.UIManagerType; +import com.facebook.react.uimanager.common.ViewUtil; import com.facebook.react.uimanager.events.EventDispatcher; import com.facebook.systrace.Systrace; import java.util.concurrent.atomic.AtomicInteger; @@ -290,6 +294,30 @@ protected void dispatchDraw(Canvas canvas) { } } + @Override + protected boolean drawChild(Canvas canvas, View child, long drawingTime) { + + BlendMode mixBlendMode = null; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q + && ViewUtil.getUIManagerType(this) == UIManagerType.FABRIC + && needsIsolatedLayer(this)) { + mixBlendMode = (BlendMode) child.getTag(R.id.mix_blend_mode); + if (mixBlendMode != null) { + Paint p = new Paint(); + p.setBlendMode(mixBlendMode); + canvas.saveLayer(0, 0, getWidth(), getHeight(), p); + } + } + + boolean result = super.drawChild(canvas, child, drawingTime); + + if (mixBlendMode != null) { + canvas.restore(); + } + + return result; + } + @Override public boolean dispatchKeyEvent(KeyEvent ev) { if (!hasActiveReactContext() || !isViewAttachedToReactInstance()) { diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/BlendModeHelper.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/BlendModeHelper.kt index 80ccd6f6bf3290..25d789dd5c2c0e 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/BlendModeHelper.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/BlendModeHelper.kt @@ -10,6 +10,9 @@ package com.facebook.react.uimanager import android.annotation.TargetApi import android.graphics.BlendMode import android.os.Build +import android.view.ViewGroup +import androidx.core.view.children +import com.facebook.react.R @TargetApi(29) internal object BlendModeHelper { @@ -41,4 +44,8 @@ internal object BlendModeHelper { else -> throw IllegalArgumentException("Invalid mix-blend-mode name: $mixBlendMode") } } + + @JvmStatic + public fun needsIsolatedLayer(view: ViewGroup): Boolean = + view.children.any { it.getTag(R.id.mix_blend_mode) != null } } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewGroup.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewGroup.java index 3facc8236b5b02..f38c460370804e 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewGroup.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewGroup.java @@ -37,6 +37,7 @@ import com.facebook.react.touch.ReactHitSlopView; import com.facebook.react.touch.ReactInterceptingViewGroup; import com.facebook.react.uimanager.BackgroundStyleApplicator; +import com.facebook.react.uimanager.BlendModeHelper; import com.facebook.react.uimanager.LengthPercentage; import com.facebook.react.uimanager.LengthPercentageType; import com.facebook.react.uimanager.MeasureSpecAssertions; @@ -747,16 +748,6 @@ private void removeFromArray(int index) { } } - private boolean needsIsolatedLayer() { - for (int i = 0; i < getChildCount(); i++) { - if (getChildAt(i).getTag(R.id.mix_blend_mode) != null) { - return true; - } - } - - return false; - } - @Override public @Nullable Rect getHitSlopRect() { return mHitSlopRect; @@ -793,7 +784,7 @@ public void setOverflow(@Nullable String overflow) { @Override public void setOverflowInset(int left, int top, int right, int bottom) { - if (needsIsolatedLayer() + if (BlendModeHelper.needsIsolatedLayer(this) && (mOverflowInset.left != left || mOverflowInset.top != top || mOverflowInset.right != right @@ -823,7 +814,7 @@ public Rect getOverflowInset() { public void draw(Canvas canvas) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && ViewUtil.getUIManagerType(this) == UIManagerType.FABRIC - && needsIsolatedLayer()) { + && BlendModeHelper.needsIsolatedLayer(this)) { // Check if the view is a stacking context and has children, if it does, do the rendering // offscreen and then composite back. This follows the idea of group isolation on blending @@ -859,7 +850,8 @@ protected boolean drawChild(Canvas canvas, View child, long drawingTime) { } BlendMode mixBlendMode = null; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && needsIsolatedLayer()) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q + && BlendModeHelper.needsIsolatedLayer(this)) { mixBlendMode = (BlendMode) child.getTag(R.id.mix_blend_mode); if (mixBlendMode != null) { Paint p = new Paint();