Enable mix-blend-mode on ReactRootView so blending works with app background (#47336)

Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/47336

Before mix-blend-mode would not blend with the background color due to it not being a ViewGroup. This adds the mix-blend-mode logic to ReactRootView so blending works

Changelog:
[Android][Fixed] - Enable mix-blend-mode on ReactRootView so blending works with app background

Reviewed By: NickGerleman

Differential Revision: D65156543

fbshipit-source-id: b3628b667573d0b56c74214e40d21c656fda495a
This commit is contained in:
Jorge Cabiedes Acosta
2024-11-24 15:33:35 -08:00
committed by Facebook GitHub Bot
parent 30e75a51f3
commit 24b0ded3cf
4 changed files with 41 additions and 13 deletions
@@ -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;
@@ -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.RootView;
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 @@ public class ReactRootView extends FrameLayout implements RootView, ReactRoot {
}
}
@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()) {
@@ -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 }
}
@@ -37,6 +37,7 @@ import com.facebook.react.touch.OnInterceptTouchEventListener;
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 @@ public class ReactViewGroup extends ViewGroup
}
}
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 class ReactViewGroup extends ViewGroup
@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 class ReactViewGroup extends ViewGroup
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 @@ public class ReactViewGroup extends ViewGroup
}
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();