3

My goal is for an ImageView to appear if the camera is moving, but only when the user is moving the map, not when zooming in or out. How can i differentiate between the two?

I tried using the OnCameraMove and OnCameraIdle Listeners but the zoom is registered as a move, and the OnCameraMoveStartedListener only can register if the move was user related or not.

Also when zooming i need the zoom to be centered. Hope this makes sense.

Marko Grbic
  • 91
  • 1
  • 1
  • 7
  • Answer i was looking for can be found [here](https://stackoverflow.com/questions/47961908/centered-zooming-a-google-map-through-a-wrapper). – Marko Grbic Dec 26 '17 at 22:08

1 Answers1

0

Unforuntilly there is no way to be able to distinguish between zooming and moving the map straight away. However, there is a hacky way to be able to accomplish what you are looking for. You can create a wrapper class which extends a FrameLayout. Inside the wrapper override the dispatchTouchEvent method and implemented the logic for handling zoom/drag or whatever needed to distinguish between user interaction. For example, to detect zoom we simply want to make sure that there are 2 fingers, they have moved beyond a certain threshold and that they are moving in opposite directions. To do so we have to override dispatchTouchEvent

switch (ev.getAction()) {
case MotionEvent.ACTION_MOVE:
boolean isPrimMoving = isScrollGesture(event, 0, mPrimStartTouchEventX,
mPrimStartTouchEventY);
boolean isSecMoving = (mPtrCount > 1 && isScrollGesture(event, 1,
mSecStartTouchEventX, mSecStartTouchEventY));
if (mPtrCount > 1 && isPinchGesture(event)) {
Log.d("TAG", "PINCH! OUCH!");
} else if (isPrimMoving || isSecMoving) {
    // A 1 finger or 2 finger scroll.
    if (isPrimMoving && isSecMoving) {
      Log.d("TAG", "Two finger scroll");
    } else {
      Log.d("TAG", "One finger scroll");
    }}
break;

This class will declare an interface which will update the activity based on the user interaction.Then, Initiate this class inside your fragment

mTouchView = new myWrapperClass(getActivity());
mTouchView.addView(mOriginalContentView);
return mTouchView;

You need to override onTouch

private float mPrimStartTouchEventX = -1;
private float mPrimStartTouchEventY = -1;
private float mSecStartTouchEventX = -1;
private float mSecStartTouchEventY = -1;
private float mPrimSecStartTouchDistance = 0;
...
@Override
public boolean onTouch(View v, MotionEvent event) {
  int action = (event.getAction() & MotionEvent.ACTION_MASK);

  switch (action) {
    case MotionEvent.ACTION_POINTER_DOWN:
    case MotionEvent.ACTION_DOWN:
      mPtrCount++;
      if (mPtrCount == 1) {
        mPrimStartTouchEventX = event.getX(0);
        mPrimStartTouchEventY = event.getY(0);
        Log.d("TAG", String.format("POINTER ONE X = %.5f, Y = %.5f", mPrimStartTouchEventX, mPrimStartTouchEventY));
      }
      if (mPtrCount == 2) {
        // Starting distance between fingers
        mSecStartTouchEventX = event.getX(1);
        mSecStartTouchEventY = event.getY(1);
        mPrimSecStartTouchDistance = distance(event, 0, 1);
        Log.d("TAG", String.format("POINTER TWO X = %.5f, Y = %.5f", mSecStartTouchEventX, mSecStartTouchEventY));
      }

      break;
    case MotionEvent.ACTION_POINTER_UP:
    case MotionEvent.ACTION_UP:
      mPtrCount--;
      if (mPtrCount < 2) {
        mSecStartTouchEventX = -1;
        mSecStartTouchEventY = -1;
      }
      if (mPtrCount < 1) {
        mPrimStartTouchEventX = -1;
        mPrimStartTouchEventY = -1;
      }
      break;

    case MotionEvent.ACTION_MOVE:
      Log.d("TAG", "Move " + mPtrCount);
      break;

  }

  return true;
}

EDIT: below the implementation of the isPinchGesture & isScrollGesture

private boolean isPinchGesture(MotionEvent event) {
  if (event.getPointerCount() == 2) {
    final float distanceCurrent = distance(event, 0, 1);
    final float diffPrimX = mPrimStartTouchEventX - event.getX(0);
    final float diffPrimY = mPrimStartTouchEventY - event.getY(0);
    final float diffSecX = mSecStartTouchEventX - event.getX(1);
    final float diffSecY = mSecStartTouchEventY - event.getY(1);

    if (Math.abs(distanceCurrent - mPrimSecStartTouchDistance) > mViewScaledTouchSlop
            // and the fingers are moving in opposing directions
            && (diffPrimY * diffSecY) <= 0
            && (diffPrimX * diffSecX) <= 0) {
      return true;
    }
  }

  return false;
}
private boolean isScrollGesture(MotionEvent event, int ptrIndex, float originalX, float originalY){
  float moveX = Math.abs(event.getX(ptrIndex) - originalX);
  float moveY = Math.abs(event.getY(ptrIndex) - originalY);

  if (moveX > mViewScaledTouchSlop || moveY > mViewScaledTouchSlop) {
   return true;
  }
  return false;
}

EDIT 2: you can detect a pinch gesture in Android using an OnTouchListener using ScaleGestureDetector. for more info: https://developer.android.com/training/gestures/scale.html

static class MyPinchListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
  @Override
  public boolean onScale(ScaleGestureDetector detector) {
    Log.d("TAG", "PINCH! OUCH!");
    return true;
  }
}
/* Using it */
ScaleGestureDetector mScaleDetector = 
             new ScaleGestureDetector(this, new MyPinchListener());
mGestureView.setOnTouchListener(new View.OnTouchListener() {
  @Override
  public boolean onTouch(View v, MotionEvent event) {
    mScaleDetector.onTouchEvent(event);
    return true;
  }
});
mhdtouban
  • 851
  • 2
  • 11
  • 20
  • Can you give me a more detailed explanation, like how are isPinchGesture() and isScrollGesture() implemented? And what exactly mPtrCount, mPrim and mSec vars are? – Marko Grbic Dec 24 '17 at 15:49
  • ok, @mente86 I'll edit my post and add the implementation of those two methods. Please accept my answer. – mhdtouban Dec 25 '17 at 15:09
  • Well i got zooming working on the map, but now moving the map does not work since the onTouch event of the mGestureView eats it up. How can i prevent this, and move the map if only one finger is touching it, and let the mScaleDetector work if it's two fingers? – Marko Grbic Dec 25 '17 at 20:20
  • inside onTouch method detect the type of the event if it's just scrolling return false. – mhdtouban Dec 25 '17 at 22:34
  • That did not work because detecting what type of event it is requires the onTouch method to be called multiple times (like ACTION_DOWN + ACTION_MOVE for drag), but the first time i return true on that event, the maps drag event gets eaten. If only i could run that switch statement in a while loop to detect what type of event it is, and then return false or true depending if it's a drag or not. – Marko Grbic Dec 25 '17 at 23:28
  • I managed to do it, will be posting a full answer [here](https://stackoverflow.com/questions/47961908/centered-zooming-a-google-map-through-a-wrapper). Thanks for the help. – Marko Grbic Dec 26 '17 at 17:56
  • Good to hear this, if my answer helped you figure it out, feel free to accept it. – mhdtouban Dec 26 '17 at 17:58
  • Yes it did partly, the ScaleGestureDetector was used for centered pinch zooming, but it's not the full answer i needed to implement and i don't want to confuse future people looking for an answer to this. Nevertheless, thank you very much. – Marko Grbic Dec 26 '17 at 20:01