mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Ensure elevated views are not changing drawing order
Summary: Uses `enableZ` trick to ensure that Skia doesn't try to reorder the views based on elevation. Unfortunately, it only helps for some cases, but the proper fix would require reimplementing `ViewGroup` completely to remove its internal logic of reordering based on Z coordinate. Changelog: [Android][Fixed] Ensure elevated views are behind sticky header in FlatList Reviewed By: cortinico Differential Revision: D30700566 fbshipit-source-id: d2c59b22332922c610f4f2d415df34e81f5a33c5
This commit is contained in:
committed by
Facebook GitHub Bot
parent
4e8da9b28f
commit
4090195122
@@ -95,4 +95,6 @@ public class ReactFeatureFlags {
|
||||
public static boolean enableLockFreeEventDispatcher = false;
|
||||
|
||||
public static boolean enableAggressiveEventEmitterCleanup = false;
|
||||
|
||||
public static boolean insertZReorderBarriersOnViewGroupChildren = true;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* 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.views.view;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.graphics.Canvas;
|
||||
import android.os.Build;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Copied from <a
|
||||
* href="https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/CanvasUtils.android.kt;drc=3b2dde134afab8d58b9c39ad4820eaf9a6e014a9">
|
||||
* Compose canvas utils </a>
|
||||
*/
|
||||
public class CanvasUtil {
|
||||
private CanvasUtil() {}
|
||||
|
||||
private @Nullable static Method mReorderBarrierMethod = null;
|
||||
private @Nullable static Method mInorderBarrierMethod = null;
|
||||
private static boolean mOrderMethodsFetched = false;
|
||||
|
||||
/**
|
||||
* Enables Z support for the Canvas. The method is publicly available starting from API 29 and was
|
||||
* hidden before, so we have to resort to reflection tricks to ensure we can use this API.
|
||||
*/
|
||||
@SuppressLint({"SoonBlockedPrivateApi", "PrivateApi"})
|
||||
public static void enableZ(Canvas canvas, boolean enable) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 29) {
|
||||
if (enable) {
|
||||
canvas.enableZ();
|
||||
} else {
|
||||
canvas.disableZ();
|
||||
}
|
||||
} else {
|
||||
fetchOrderMethods();
|
||||
try {
|
||||
if (enable && mReorderBarrierMethod != null) {
|
||||
mReorderBarrierMethod.invoke(canvas);
|
||||
}
|
||||
if (!enable && mInorderBarrierMethod != null) {
|
||||
mInorderBarrierMethod.invoke(canvas);
|
||||
}
|
||||
} catch (IllegalAccessException | InvocationTargetException ignore) {
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void fetchOrderMethods() {
|
||||
if (!mOrderMethodsFetched) {
|
||||
try {
|
||||
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.P) {
|
||||
// use double reflection to avoid grey list on P
|
||||
Method getDeclaredMethod =
|
||||
Class.class.getDeclaredMethod("getDeclaredMethod", String.class, Class[].class);
|
||||
mReorderBarrierMethod =
|
||||
(Method) getDeclaredMethod.invoke(Canvas.class, "insertReorderBarrier", new Class[0]);
|
||||
mInorderBarrierMethod =
|
||||
(Method) getDeclaredMethod.invoke(Canvas.class, "insertInorderBarrier", new Class[0]);
|
||||
} else {
|
||||
mReorderBarrierMethod = Canvas.class.getDeclaredMethod("insertReorderBarrier");
|
||||
mInorderBarrierMethod = Canvas.class.getDeclaredMethod("insertInorderBarrier");
|
||||
}
|
||||
|
||||
if (mReorderBarrierMethod == null || mInorderBarrierMethod == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
mReorderBarrierMethod.setAccessible(true);
|
||||
mInorderBarrierMethod.setAccessible(true);
|
||||
} catch (IllegalAccessException ignore) {
|
||||
// Do nothing
|
||||
} catch (InvocationTargetException ignore) {
|
||||
// Do nothing
|
||||
} catch (NoSuchMethodException ignore) {
|
||||
// Do nothing
|
||||
}
|
||||
mOrderMethodsFetched = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,6 +33,7 @@ import com.facebook.react.bridge.ReactNoCrashSoftException;
|
||||
import com.facebook.react.bridge.ReactSoftExceptionLogger;
|
||||
import com.facebook.react.bridge.UiThreadUtil;
|
||||
import com.facebook.react.common.annotations.VisibleForTesting;
|
||||
import com.facebook.react.config.ReactFeatureFlags;
|
||||
import com.facebook.react.modules.i18nmanager.I18nUtil;
|
||||
import com.facebook.react.touch.OnInterceptTouchEventListener;
|
||||
import com.facebook.react.touch.ReactHitSlopView;
|
||||
@@ -759,6 +760,23 @@ public class ReactViewGroup extends ViewGroup
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
|
||||
boolean drawWithZ =
|
||||
child.getElevation() > 0 && ReactFeatureFlags.insertZReorderBarriersOnViewGroupChildren;
|
||||
|
||||
if (drawWithZ) {
|
||||
CanvasUtil.enableZ(canvas, false);
|
||||
}
|
||||
|
||||
boolean result = super.drawChild(canvas, child, drawingTime);
|
||||
|
||||
if (drawWithZ) {
|
||||
CanvasUtil.enableZ(canvas, true);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void dispatchOverflowDraw(Canvas canvas) {
|
||||
if (mOverflow != null) {
|
||||
switch (mOverflow) {
|
||||
|
||||
Reference in New Issue
Block a user