react-native-masked-view / masked-view

React Native Masked View Library
MIT License
974 stars 126 forks source link

Android broken background with react-native 0.75.1 #226

Open MartinInAction opened 2 months ago

MartinInAction commented 2 months ago

image

Left is android sim and right is iOS. I cant get rid of the black background on android since upgrading to RN 0.75.1. Prior versions worked great!!

Any ideas on how to fix this?

maksim-v commented 2 months ago

Same here I tried changing the background color in different ways, but nothing helped

maksim-v commented 2 months ago

@MartinInAction I found a temporary solution in case you are using hardware rendering mode. I haven't tested it with software mode. You just need to apply this patch:

diff --git a/node_modules/@react-native-masked-view/masked-view/android/src/main/java/org/reactnative/maskedview/RNCMaskedView.java b/node_modules/@react-native-masked-view/masked-view/android/src/main/java/org/reactnative/maskedview/RNCMaskedView.java
index 2ea0c5e..e8877e0 100644
--- a/node_modules/@react-native-masked-view/masked-view/android/src/main/java/org/reactnative/maskedview/RNCMaskedView.java
+++ b/node_modules/@react-native-masked-view/masked-view/android/src/main/java/org/reactnative/maskedview/RNCMaskedView.java
@@ -41,6 +41,7 @@ public class RNCMaskedView extends ReactViewGroup {

     // draw the mask
     if (mBitmapMask != null) {
+      setLayerType(LAYER_TYPE_HARDWARE, mPaint);
       mPaint.setXfermode(mPorterDuffXferMode);
       canvas.drawBitmap(mBitmapMask, 0, 0, mPaint);
       mPaint.setXfermode(null);

Here I simply set the layer type in the dispatchDraw method, providing a Paint instance that controls how the layer is drawn on the screen. Here is the complete method:

  @Override
  protected void dispatchDraw(Canvas canvas) {
    super.dispatchDraw(canvas);

    if (mBitmapMaskInvalidated) {
      // redraw mask element to support animated elements
      updateBitmapMask();

      mBitmapMaskInvalidated = false;
    }

    // draw the mask
    if (mBitmapMask != null) {
      setLayerType(LAYER_TYPE_HARDWARE, mPaint);
      mPaint.setXfermode(mPorterDuffXferMode);
      canvas.drawBitmap(mBitmapMask, 0, 0, mPaint);
      mPaint.setXfermode(null);
    }
  }

Hope this helps!

MartinInAction commented 2 months ago

@maksim-v Thank you! you saved the day!!

jasimchz commented 2 months ago

I tried this but still, react-native-shimmer and react-native-skelton view has same issue

MartinInAction commented 2 months ago

@jasimchz are you sure the patch is applied?

dembeEdward commented 2 months ago

@MartinInAction I found a temporary solution in case you are using hardware rendering mode. I haven't tested it with software mode. You just need to apply this patch:

diff --git a/node_modules/@react-native-masked-view/masked-view/android/src/main/java/org/reactnative/maskedview/RNCMaskedView.java b/node_modules/@react-native-masked-view/masked-view/android/src/main/java/org/reactnative/maskedview/RNCMaskedView.java
index 2ea0c5e..e8877e0 100644
--- a/node_modules/@react-native-masked-view/masked-view/android/src/main/java/org/reactnative/maskedview/RNCMaskedView.java
+++ b/node_modules/@react-native-masked-view/masked-view/android/src/main/java/org/reactnative/maskedview/RNCMaskedView.java
@@ -41,6 +41,7 @@ public class RNCMaskedView extends ReactViewGroup {

     // draw the mask
     if (mBitmapMask != null) {
+      setLayerType(LAYER_TYPE_HARDWARE, mPaint);
       mPaint.setXfermode(mPorterDuffXferMode);
       canvas.drawBitmap(mBitmapMask, 0, 0, mPaint);
       mPaint.setXfermode(null);

Here I simply set the layer type in the dispatchDraw method, providing a Paint instance that controls how the layer is drawn on the screen. Here is the complete method:

  @Override
  protected void dispatchDraw(Canvas canvas) {
    super.dispatchDraw(canvas);

    if (mBitmapMaskInvalidated) {
      // redraw mask element to support animated elements
      updateBitmapMask();

      mBitmapMaskInvalidated = false;
    }

    // draw the mask
    if (mBitmapMask != null) {
      setLayerType(LAYER_TYPE_HARDWARE, mPaint);
      mPaint.setXfermode(mPorterDuffXferMode);
      canvas.drawBitmap(mBitmapMask, 0, 0, mPaint);
      mPaint.setXfermode(null);
    }
  }

Hope this helps!

Worked like a charm

jasimchz commented 2 months ago

Anyone check if react-native-shimmer still broking?!

efstathiosntonas commented 2 months ago

@maksim-v thanks, this was driving me crazy.

lovegaoshi commented 2 months ago

can confirm this works with software mode rendering mode as well. tysm!

fyi the filename for the patch using patch-package is patches/@react-native-masked-view+masked-view+0.3.1.patch

kelvinkioko commented 3 weeks ago

Anyone available to approve the PRs fixing this bug?

AkilUnik commented 2 weeks ago

`package org.reactnative.maskedview;

import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.view.View;

import com.facebook.react.views.view.ReactViewGroup;

public class RNCMaskedView extends ReactViewGroup { private static final String TAG = "RNCMaskedView";

private Bitmap mBitmapMask = null; private boolean mBitmapMaskInvalidated = false; private Paint mPaint; private PorterDuffXfermode mPorterDuffXferMode;

public RNCMaskedView(Context context) { super(context);

// Default to hardware rendering, androidRenderingMode prop will override
setRenderingMode("hardware");

mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPorterDuffXferMode = new PorterDuffXfermode(PorterDuff.Mode.DST_IN);

}

@Override protected void dispatchDraw(Canvas canvas) { super.dispatchDraw(canvas);

if (mBitmapMaskInvalidated) {
  // redraw mask element to support animated elements
  updateBitmapMask();

  mBitmapMaskInvalidated = false;
}

// draw the mask
if (mBitmapMask != null) {
  setLayerType(LAYER_TYPE_HARDWARE, mPaint);
  mPaint.setXfermode(mPorterDuffXferMode);
  canvas.drawBitmap(mBitmapMask, 0, 0, mPaint);
  mPaint.setXfermode(null);
}

}

@Override public void onDescendantInvalidated(View child, View target) { super.onDescendantInvalidated(child, target);

if (!mBitmapMaskInvalidated) {
  View maskView = getChildAt(0);
  if (maskView != null) {
    if (maskView.equals(child)) {
      mBitmapMaskInvalidated = true;
    }
  }
}

invalidate();

}

@Override protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b);

if (changed) {
  mBitmapMaskInvalidated = true;
}

}

@Override protected void onAttachedToWindow() { super.onAttachedToWindow(); mBitmapMaskInvalidated = true; }

private void updateBitmapMask() { View maskView = getChildAt(0); if (maskView != null) { maskView.setVisibility(View.VISIBLE); if (this.mBitmapMask != null) { this.mBitmapMask.recycle(); } this.mBitmapMask = getBitmapFromView(maskView); maskView.setVisibility(View.INVISIBLE); } }

public static Bitmap getBitmapFromView(final View view) { view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());

if (view.getMeasuredWidth() <= 0 || view.getMeasuredHeight() <= 0) {
  return null;
}

final Bitmap bitmap = Bitmap.createBitmap(view.getMeasuredWidth(),
        view.getMeasuredHeight(), Bitmap.Config.ARGB_8888);

final Canvas canvas = new Canvas(bitmap);

view.draw(canvas);

return bitmap;

}

public void setRenderingMode(String renderingMode) { if (renderingMode.equals("software")) { setLayerType(LAYER_TYPE_SOFTWARE, null); } else { setLayerType(LAYER_TYPE_HARDWARE, null); } } } `

if any one want full file code there it is just copy and change RNCMaskedView.java file

rafaelmaeuer commented 1 day ago

@MartinInAction I found a temporary solution in case you are using hardware rendering mode. I haven't tested it with software mode. You just need to apply this patch:

diff --git a/node_modules/@react-native-masked-view/masked-view/android/src/main/java/org/reactnative/maskedview/RNCMaskedView.java b/node_modules/@react-native-masked-view/masked-view/android/src/main/java/org/reactnative/maskedview/RNCMaskedView.java
index 2ea0c5e..e8877e0 100644
--- a/node_modules/@react-native-masked-view/masked-view/android/src/main/java/org/reactnative/maskedview/RNCMaskedView.java
+++ b/node_modules/@react-native-masked-view/masked-view/android/src/main/java/org/reactnative/maskedview/RNCMaskedView.java
@@ -41,6 +41,7 @@ public class RNCMaskedView extends ReactViewGroup {

     // draw the mask
     if (mBitmapMask != null) {
+      setLayerType(LAYER_TYPE_HARDWARE, mPaint);
       mPaint.setXfermode(mPorterDuffXferMode);
       canvas.drawBitmap(mBitmapMask, 0, 0, mPaint);
       mPaint.setXfermode(null);

Here I simply set the layer type in the dispatchDraw method, providing a Paint instance that controls how the layer is drawn on the screen. Here is the complete method:

  @Override
  protected void dispatchDraw(Canvas canvas) {
    super.dispatchDraw(canvas);

    if (mBitmapMaskInvalidated) {
      // redraw mask element to support animated elements
      updateBitmapMask();

      mBitmapMaskInvalidated = false;
    }

    // draw the mask
    if (mBitmapMask != null) {
      setLayerType(LAYER_TYPE_HARDWARE, mPaint);
      mPaint.setXfermode(mPorterDuffXferMode);
      canvas.drawBitmap(mBitmapMask, 0, 0, mPaint);
      mPaint.setXfermode(null);
    }
  }

Hope this helps!

This patch does solve the Problem on Android, however on slow/old devices it freezes the app for some seconds and causes some really bad hangs/delays (using masked view in an animation). Has anyone experiences something similar with this fix?