Open siddharth-kt opened 2 years ago
@oblador Kindly sort this issue, its getting worse on pressing the key which navigates to another screen. Image gets stuck on the screen and app needs restart to be used again.
Facing the same issue. Can confirm issue is present in android devices.
I have used the Pinchable component like the example below:
<FlatList
data={[
'https://thumbs.dreamstime.com/z/random-square-multicolor-pattern-24853666.jpg',
'https://cdn4.vectorstock.com/i/1000x1000/51/38/random-square-pattern-seamless-background-vector-25695138.jpg',
'https://c8.alamy.com/comp/K0D99R/square-background-random-black-and-white-colored-abstract-digital-K0D99R.jpg',
'https://picsum.photos/800'
]}
renderItem={({ item }) => (
<Pinchable>
<Image
resizeMode="cover"
source={{
uri: item
}}
style={{
height: dimension.screenWidth,
width: dimension.screenWidth
}}
/>
</Pinchable>
)}
ItemSeparatorComponent={() => (<View style={{ width: '100%', height: 10, backgroundColor: 'red' }} />)}
/>
Issue screen record given below
From the moment the issue happens, the image will be present there even after navigating to other pages. Only by closing the app completely the image is dismissed. @oblador
Yeah same
@oblador kindly reply!
@Dein1 ??
@oblador is there any updates? is there any possibility to get a prop to know if pinch is happening, so we can disable the scroll looking into it, or something like that
@oblador ?
Same issue here!
Are you using the library with something like react-native-pager-view
lib as showed here?
I am using it, and that issue happen just if I move line 10
to after the line 12
inside of component, if I declare it as the example shows, declaring AnimatedPagerView
outside of component it works like a charm.
Are you using the library with something like
react-native-pager-view
lib as showed here?I am using it, and that issue happen just if I move
line 10
to after theline 12
inside of component, if I declare it as the example shows, declaringAnimatedPagerView
outside of component it works like a charm.
Hello, can you show simple example?
Has there been any progress on this issue?
When the animation start I am trying to turn off the user interactions on the activity with:
ctx.getCurrentActivity().getWindow().setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE, WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
It works good in most cases but when I keep just one finger it starts to move things from the activity that should be blocked, I am trying to check what is going on
This patch fix the issue:
diff --git a/node_modules/react-native-pinchable/android/src/main/java/com/oblador/pinchable/PinchableView.java b/node_modules/react-native-pinchable/android/src/main/java/com/oblador/pinchable/PinchableView.java
index 9f22074..841967c 100644
--- a/node_modules/react-native-pinchable/android/src/main/java/com/oblador/pinchable/PinchableView.java
+++ b/node_modules/react-native-pinchable/android/src/main/java/com/oblador/pinchable/PinchableView.java
@@ -8,6 +8,8 @@ import android.graphics.drawable.BitmapDrawable;
import android.graphics.Color;
import android.graphics.PointF;
import android.graphics.Bitmap;
+import android.os.Handler;
+import android.os.Looper;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
@@ -15,8 +17,11 @@ import android.view.View.OnTouchListener;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
+import android.view.ViewParent;
+import android.view.WindowManager;
import android.view.animation.DecelerateInterpolator;
+import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.views.view.ReactViewGroup;
public class PinchableView extends ReactViewGroup implements OnTouchListener {
@@ -33,9 +38,10 @@ public class PinchableView extends ReactViewGroup implements OnTouchListener {
private ValueAnimator currentAnimator = null;
private ColorDrawable backdrop = null;
private BitmapDrawable clone = null;
-
+ private ThemedReactContext ctx = null;
public PinchableView(Context context) {
super(context);
+ this.ctx = (ThemedReactContext) context;
this.setOnTouchListener(this);
}
@@ -102,6 +108,8 @@ public class PinchableView extends ReactViewGroup implements OnTouchListener {
}
private void startGesture(View v, MotionEvent event) {
+ ctx.getCurrentActivity().getWindow().setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,
+ WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
if (currentAnimator != null) {
currentAnimator.cancel();
}
@@ -155,14 +163,12 @@ public class PinchableView extends ReactViewGroup implements OnTouchListener {
private void endGesture(MotionEvent event) {
active = false;
- this.getParent().requestDisallowInterceptTouchEvent(false);
if (currentAnimator != null) {
currentAnimator.cancel();
}
-
ValueAnimator animator = ValueAnimator.ofFloat(1, 0);
animator.setDuration(animationDuration);
- animator.setInterpolator(new DecelerateInterpolator(1.5f));
+ animator.setInterpolator(new DecelerateInterpolator(5f));
animator.addUpdateListener(updatedAnimation -> {
float animatedValue = (float)updatedAnimation.getAnimatedValue();
setCloneTransforms(translation.x * animatedValue, translation.y * animatedValue, 1 + (scale - 1) * animatedValue);
@@ -171,13 +177,22 @@ public class PinchableView extends ReactViewGroup implements OnTouchListener {
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
endAnimation();
}
@Override
public void onAnimationCancel(Animator animation) {
+ super.onAnimationCancel(animation);
endAnimation();
}
+
+ @Override
+ public void onAnimationStart(Animator animation) {
+ super.onAnimationStart(animation);
+ ctx.getCurrentActivity().getWindow().setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,
+ WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
+ }
});
animator.start();
currentAnimator = animator;
@@ -195,6 +210,15 @@ public class PinchableView extends ReactViewGroup implements OnTouchListener {
clone = null;
}
setVisibility(View.VISIBLE);
+ ViewParent parent = this.getParent();
+ final Handler handler = new Handler(Looper.getMainLooper());
+ handler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ ctx.getCurrentActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
+ parent.requestDisallowInterceptTouchEvent(false);
+ }
+ }, 100);
}
public void setMinimumZoomScale(float minimumZoomScale) {
This is the entire PinchableView.java File:
package com.oblador.pinchable;
import java.lang.Math;
import android.content.Context;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.Color;
import android.graphics.PointF;
import android.graphics.Bitmap;
import android.os.Handler;
import android.os.Looper;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.view.ViewParent;
import android.view.WindowManager;
import android.view.animation.DecelerateInterpolator;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.views.view.ReactViewGroup;
public class PinchableView extends ReactViewGroup implements OnTouchListener {
private final int animationDuration = 400;
private float minScale = 1f;
private float maxScale = 3f;
private boolean active = false;
private int sourceLocation[] = new int[2];
private int sourceDimensions[] = new int[2];
private PointF initialTouchPoint = new PointF();
private float initialSpacing = 0f;
private PointF translation = new PointF();
private float scale = 1;
private ValueAnimator currentAnimator = null;
private ColorDrawable backdrop = null;
private BitmapDrawable clone = null;
private ThemedReactContext ctx = null;
public PinchableView(Context context) {
super(context);
this.ctx = (ThemedReactContext) context;
this.setOnTouchListener(this);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
// Block touch events on children
return true;
}
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_POINTER_DOWN:
if (!active && event.getPointerCount() >= 2) {
startGesture(v, event);
}
break;
case MotionEvent.ACTION_POINTER_UP:
if (active && event.getPointerCount() < 2) {
endGesture(event);
}
break;
case MotionEvent.ACTION_MOVE:
if (active) {
if (event.getPointerCount() < 2) {
endGesture(event);
} else {
moveGesture(event);
}
}
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
if (active) {
endGesture(event);
}
break;
default:
break;
}
return true;
}
private float spacing(MotionEvent event) {
float x = event.getX(0) - event.getX(1);
float y = event.getY(0) - event.getY(1);
return (float) Math.sqrt(x * x + y * y);
}
private void midPoint(PointF point, MotionEvent event) {
float x = event.getX(0) + event.getX(1);
float y = event.getY(0) + event.getY(1);
point.set(x / 2, y / 2);
}
private void setCloneTransforms(float translateX, float translateY, float scale) {
int x = (int) (sourceLocation[0] + translateX - sourceDimensions[0] * (scale - 1) / 2);
int y = (int) (sourceLocation[1] + translateY - sourceDimensions[1] * (scale - 1) / 2);
int width = (int) (sourceDimensions[0] * scale);
int height = (int) (sourceDimensions[1] * scale);
clone.setBounds(x, y, x + width, y + height);
backdrop.setAlpha((int) (255 * Math.min(scale - 1, 0.7)));
}
private void startGesture(View v, MotionEvent event) {
ctx.getCurrentActivity().getWindow().setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,
WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
if (currentAnimator != null) {
currentAnimator.cancel();
}
View rootView = this.getRootView();
if (clone != null) {
rootView.getOverlay().remove(clone);
clone = null;
}
Bitmap snapshot = null;
v.setDrawingCacheEnabled(true);
v.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_AUTO);
try {
snapshot = Bitmap.createBitmap(v.getDrawingCache(true));
clone = new BitmapDrawable(this.getContext().getResources(), snapshot);
} catch(Exception ex) {
ex.printStackTrace();
return;
}
active = true;
if (backdrop == null) {
backdrop = new ColorDrawable(Color.BLACK);
backdrop.setAlpha(0);
backdrop.setBounds(0, 0, rootView.getWidth(), rootView.getHeight());
rootView.getOverlay().add(backdrop);
}
v.getLocationInWindow(sourceLocation);
sourceDimensions[0] = v.getWidth();
sourceDimensions[1] = v.getHeight();
setCloneTransforms(0, 0, 1);
rootView.getOverlay().add(clone);
setVisibility(View.INVISIBLE);
midPoint(initialTouchPoint, event);
initialSpacing = spacing(event);
translation.set(0, 0);
scale = 1;
this.getParent().requestDisallowInterceptTouchEvent(true);
}
private void moveGesture(MotionEvent event) {
PointF current = new PointF();
midPoint(current, event);
float deltaX = current.x - initialTouchPoint.x;
float deltaY = current.y - initialTouchPoint.y;
scale = Math.min(maxScale, Math.max(minScale, spacing(event) / initialSpacing));
setCloneTransforms(deltaX, deltaY, scale);
translation.set(deltaX, deltaY);
}
private void endGesture(MotionEvent event) {
active = false;
if (currentAnimator != null) {
currentAnimator.cancel();
}
ValueAnimator animator = ValueAnimator.ofFloat(1, 0);
animator.setDuration(animationDuration);
animator.setInterpolator(new DecelerateInterpolator(5f));
animator.addUpdateListener(updatedAnimation -> {
float animatedValue = (float)updatedAnimation.getAnimatedValue();
setCloneTransforms(translation.x * animatedValue, translation.y * animatedValue, 1 + (scale - 1) * animatedValue);
});
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
endAnimation();
}
@Override
public void onAnimationCancel(Animator animation) {
super.onAnimationCancel(animation);
endAnimation();
}
@Override
public void onAnimationStart(Animator animation) {
super.onAnimationStart(animation);
ctx.getCurrentActivity().getWindow().setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,
WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
}
});
animator.start();
currentAnimator = animator;
}
private void endAnimation() {
currentAnimator = null;
View rootView = this.getRootView();
if (backdrop != null) {
rootView.getOverlay().remove(backdrop);
backdrop = null;
}
if (clone != null) {
rootView.getOverlay().remove(clone);
clone = null;
}
setVisibility(View.VISIBLE);
ViewParent parent = this.getParent();
final Handler handler = new Handler(Looper.getMainLooper());
handler.postDelayed(new Runnable() {
@Override
public void run() {
ctx.getCurrentActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
parent.requestDisallowInterceptTouchEvent(false);
}
}, 100);
}
public void setMinimumZoomScale(float minimumZoomScale) {
minScale = minimumZoomScale;
}
public void setMaximumZoomScale(float maximumZoomScale) {
maxScale = maximumZoomScale;
}
}
I will create a pr when I have some time
This pr fix the issue:
https://github.com/oblador/react-native-pinchable/pull/14
@siddharth-kt @oblador @pierroo
Great library !
It would be great if background touch/scroll etc.. events can be stopped until image gets settled down to its original position. Because in few case I have found that user tries to scroll away fast (till that time image was not settled properly to its actual position) and since the actual position of that image gets lost, that image get stuck on the screen. Then user needs to restart the app.
Note : I also confirm this issue exists on android