I have a custom view and I want to make the ability for a user to zoom in and out with pinching (like an image), but it doesn't work. I added the functions from here https://developer.android.com/develop/ui/views/touch-and-input/gestures/scale#basic-scaling-example.
But when I pinch it doesn't scale
I tried to add it to parent activity and it worked but I'm pretty sure ScaleGestureDetector shoud be working in a View itself. Also from what I understood after testing: Everything else works BUT ScaleDetector.onTouchEvent(event) just doesn't detect a pinch and doesn't start a function.
Here's a view class
public class SandboxView extends View {
float scaleFactor = 1.0f;
float canvasPosX = 0, canvasPosY = 0;
Paint paint = new Paint();
ArrayList<Rect> effectors = new ArrayList<>();
public SandboxView(Context context) {
super(context);
onStart(context);
}
public SandboxView(Context context, AttributeSet attrs) {
super(context, attrs);
onStart(context);
}
public SandboxView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
onStart(context);
}
public void onStart(Context context) {
mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
effectors.add(new Rect(0, 0, 10, getHeight()));
effectors.add(new Rect(this.getWidth() - 10, 0, this.getWidth(), this.getHeight()));
effectors.add(new Rect(0, 0, this.getWidth(), 10));
effectors.add(new Rect(0, this.getHeight() - 10, this.getWidth(), this.getHeight()));
}
@Override
protected void onDraw(Canvas canvas) {
canvas.save();
super.onDraw(canvas);
canvas.scale(scaleFactor, scaleFactor, this.getWidth() / 2.0f, this.getHeight() / 2.0f);
for (Rect r : effectors) {
canvas.drawRect(r, paint);
}
canvas.restore();
}
@Override
public boolean onTouchEvent(MotionEvent event) {
mScaleDetector.onTouchEvent(event);
super.onTouchEvent(event);
performClick();
return super.onTouchEvent(event);
}
public void scale(float f_x, float f_y, float factor) {
scaleFactor *= factor;
scaleFactor = Math.max(0.01f, Math.min(50.0f, scaleFactor));
canvasPosX += f_x * (1 - factor);
canvasPosY += f_y * (1 - factor);
invalidate();
}
private ScaleGestureDetector mScaleDetector;
private class ScaleListener
extends ScaleGestureDetector.SimpleOnScaleGestureListener {
@Override
public boolean onScale(ScaleGestureDetector detector) {
Logger.getGlobal().info(detector.getFocusX() + " " + detector.getFocusY());
scale(detector.getFocusX(), detector.getFocusY(), detector.getScaleFactor());
return true;
}
}
@Override
public boolean performClick() {
return super.performClick();
}
}
And an activity layout
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<com.example.a50beees.SandboxView
android:id="@+id/sandbox_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
UPD: For some reason if I call
sandbox_view.mScaleDetector.onTouchEvent(event);
in the parent activity onTouchEvent it starts working, but I have to make Scale Detector public and it is obviously a bad coding still, what am I doing wrong???
UPD2: I checked events that both Activity.onTouchEvent and View.onTouchEvent get and turns out view doesn't recieve any actions apart from ACTION_DOWN, so when the scaling happens it doesn't get any ACTION_MOVE either, WHY????
UPD3: Well.... Turns out, that apparently you need to return true in your custom onTouchEvent to get any other action... Great
If anybody else has this problem in the future: it seems it happens because ScaleGestureDetector needs at least ACTION_MOVE to be triggered at all, so if your onTouchEvent doesn't recieve these, scaling will never "hear" the gesture.
To recieve actions apart from ACTION_DOWN, your onTouchEvent in a custom View should return true (to avoid "double clicking" handle actions seperately with switch/case)
this is how it should look like