0

I'm developing an app that will lock on a users location, much like in the new Pokemon go app, and I need to find a way to rotate with only one finger. I thought there might be some 'drag' functionality, but I have yet to find anything that will work. Any ideas?

kevinb
  • 17
  • 9
  • Given that this is how people expect the UI to behave: https://material.google.com/patterns/gestures.html#gestures-touch-mechanics you might want to explain what you are thinking of. Unless its just a +/- buttons but for rotation. – Morrison Chang Aug 20 '16 at 20:47
  • do you mean explain in the app or here? – kevinb Aug 20 '16 at 21:20
  • Dragging a map like a piece of paper with one finger is a common gesture. Rotate as you can see in the material UI patterns link uses two fingers. You might want to explain for your app what you mean by 'rotate with only one finger' and how its different than a drag in a circular pattern. – Morrison Chang Aug 20 '16 at 22:28

2 Answers2

2

I had the same issue. what I did was I've added a custom view that extends FrameLayout on top of the map and added a "onTouchEvent" method to it. I calculated the angle between two lines- first line is between first touch point on screen and center of the map. second is between the last place the finger has touched and the center of the map. last thing left to do is to update the bearing of the map object and assign the value first touch variable to the last place the finger moved on screen.

class touch view extends FrameLayout {

...

public boolean onTouchEvent(MotionEvent eve) {
    Point touchPoint = new Point();  //first point on screen the user's finger touches
    final GoogleMap map;
    final int action = MotionEventCompat.getActionMasked(eve);
    int pointerIndex = MotionEventCompat.getActionIndex(eve);
    GestureDetector g = new GestureDetector(getContext(), new GestureDetector.SimpleOnGestureListener());
    g.onTouchEvent(eve);
    switch (action){
        case MotionEvent.ACTION_DOWN:
            // get the point the user's finger touches
            touchPoint.x =(int) MotionEventCompat.getX(eve,pointerIndex);
            touchPoint.y =(int) MotionEventCompat.getY(eve,pointerIndex);
            break;
        case MotionEvent.ACTION_MOVE:
            if(eve.getPointerCount()<2) {   // leave two finger gestures for other actions
                final Point newTouchPoint = new Point();  // the new position of user's finger on screen after movement is detected
                newTouchPoint.x = (int) MotionEventCompat.getX(eve, pointerIndex);
                newTouchPoint.y = (int) MotionEventCompat.getY(eve, pointerIndex);
                Point centerOfMap = getCenterOfMapAsPoint();   // center of map(as Point object) for calculation of angle
                // now you need to calculate the angle betwwen 2 lines with centerOfMap as center:
                //line 1: imaginary line between first touch detection on screen - and the center of the map
                //line 2: imaginary line between last place finger moved on screen - and the center of the map
                final float angle = angleBetweenLines(centerOfMap, touchPoint, centerOfMap, newTouchPoint);
                final LatLng latlng = new LatLng(location.getLatitude(), location.getLongitude());  //set camera movement to that position
                new Handler().post(new Runnable() {
                    @Override
                    public void run() {
                        // move the camera (NOT animateCamera() ) to new position with "bearing" updated
                        map.moveCamera(CameraUpdateFactory.newCameraPosition(CameraPosition.builder().target(latlng).tilt(67.5f).zoom(map.getCameraPosition().zoom).bearing(map.getCameraPosition().bearing - angle).build()));
                    }
                });
                touchPoint = newTouchPoint; // update touchPoint value
                return true;
            }else{
                break;
            }
    }
    return true;
}

// convert center of map from Latln object to Point object
public Point getCurrentLocation(){
    Projection projection = map.getProjection();
    return projection.toScreenLocation(new LatLng(location.getLatitude(), location.getLongitude()));
}

angle calculating class looks like this-

public float angleBetweenLines(Point center,Point endLine1,Point endLine2){
    float a = endLine1.x - center.x;
    float b = endLine1.y - center.y;
    float c = endLine2.x - center.x;
    float d = endLine2.y - center.y;

    float atan1 = (float) Math.atan2(a,b);
    float atan2 = (float) Math.atan2(c,d);

    return (float) ((atan1 - atan2) * 180 / Math.PI);
}
0

Can not make comments yet, but I found Semikunchezzz solution not convenient to use. Instead of separate FrameLayout, we can extend SupportMapFragment as shown by Gaucho: Google Maps Android API v2 - detect touch on map

ehrid
  • 95
  • 8