masked-view icon indicating copy to clipboard operation
masked-view copied to clipboard

Android broken background with react-native 0.75.1

Open MartinInAction opened this issue 1 year ago • 12 comments

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?

MartinInAction avatar Aug 19 '24 13:08 MartinInAction

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

maksim-v avatar Aug 19 '24 15:08 maksim-v

@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!

maksim-v avatar Aug 20 '24 13:08 maksim-v

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

MartinInAction avatar Aug 21 '24 07:08 MartinInAction

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

jasimchz avatar Aug 21 '24 08:08 jasimchz

@jasimchz are you sure the patch is applied?

MartinInAction avatar Aug 21 '24 08:08 MartinInAction

@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

dembeEdward avatar Aug 21 '24 17:08 dembeEdward

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

jasimchz avatar Aug 22 '24 13:08 jasimchz

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

efstathiosntonas avatar Aug 26 '24 13:08 efstathiosntonas

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

lovegaoshi avatar Aug 26 '24 15:08 lovegaoshi

Anyone available to approve the PRs fixing this bug?

kelvinkioko avatar Oct 01 '24 07:10 kelvinkioko

`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

AkilUnik avatar Oct 08 '24 11:10 AkilUnik

@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?

rafaelmaeuer avatar Oct 26 '24 14:10 rafaelmaeuer

https://github.com/react-native-masked-view/masked-view/pull/228 this merge will fix this issue. I've tested it

samueljim avatar Nov 03 '24 02:11 samueljim

this works 👍. hope its fixed

musasoftlabx avatar Dec 11 '24 15:12 musasoftlabx