risposta

25

Ecco una possibile soluzione per determinare gli eventi di inizio e di fine di trascinamento di trascinamento:

Bisogna estendere SupportMapFragment o MapFragment. In onCreateView devi avvolgere il tuo MapView in un FrameLayout personalizzato (nell'esempio qui sotto è la classe "TouchableWrapper"), in cui intercetti gli eventi tattili e riconosce se la mappa viene toccata o meno. Se il tuo "onCameraChange" viene chiamato, controlla se la vista della mappa è stata premuta o meno (nell'esempio sotto questa è la variabile "mMapIsTouched").

codice

Esempio:

UPDATE 1:

  • ritorno originale creato vista in GetView()
  • uso dispatchTouchEvent() anziché onInterceptTouchEvent()

misura FrameLayout:

private class TouchableWrapper extends FrameLayout { 

    @Override 
    public boolean dispatchTouchEvent(MotionEvent ev) { 

     switch (ev.getAction()) { 
      case MotionEvent.ACTION_DOWN: 
       mMapIsTouched = true; 
       break; 
      case MotionEvent.ACTION_UP: 
       mMapIsTouched = false; 
       break; 
     } 

     return super.dispatchTouchEvent(ev); 

    } 

} 

Nel vostro MapFragment personalizzato:

@Override 
public View onCreateView(LayoutInflater inflater, ViewGroup parent, 
     Bundle savedInstanceState) { 
    mOriginalContentView = super.onCreateView(inflater, parent, 
      savedInstanceState); 

    mTouchView = new TouchableWrapper(getActivity()); 
    mTouchView.addView(mOriginalContentView); 

    return mTouchView; 
} 

@Override 
public View getView() { 
    return mOriginalContentView; 
} 

nella fotocamera metodo di cambiamento di callback:

private final OnCameraChangeListener mOnCameraChangeListener = 
     new OnCameraChangeListener() { 

    @Override 
    public void onCameraChange(CameraPosition cameraPosition) { 
     if (!mMapIsTouched) { 
      refreshClustering(false); 
     } 
    } 
}; 
+1

Ehi @Alexey Zakharov, per favore dai un'occhiata anche al thread sul tracker dei problemi di Google Maps: http://code.google.com/p/gmaps-api-issues/issues/detail?id=4636&q=apitype%3DAndroid2&sort= -modified & colspec = ID% 20Type% 20Status% 20Introduced% 20Fixed% 20Summary% 20Stars% 20ApiType% 20Internal% 20Modified – AZ13

+0

È vero. Non è garantito che onCameraChanged() venga richiamato dopo aver determinato l'evento ACTION_UP. Comunque, spero anche che Google risolverà questo problema molto presto! – AZ13

+0

risposta molto utile, grazie –

8

Proverei a onCameraChangeListener. L'ascoltatore viene chiamato ogni volta che un movimento della telecamera è terminato. L'ascoltatore ti darà anche la nuova posizione. Nei miei test l'ascoltatore è stato chiamato piuttosto spesso durante il trascinamento, forse c'è una soluzione migliore.

+2

Ho trovato questo metodo, ma come hai detto, spara molto spesso. Ho solo bisogno di geocodificare quando l'utente interrompe il trascinamento della mappa e toglie il dito. Quindi sembra che anche io debba gestire il tatto da solo. Ma non c'è un gestore onTouch nella nuova mappa API. –

+0

@AlexeyZakharov ha ragione, è attivato troppo spesso causando l'animazione per bloccata la risposta di AZ13 è davvero buona. – TypingPanda

23

Dopo aver utilizzato la soluzione di AZ13 sopra, e in esecuzione nel problema menzionato nei commenti, ho creato la seguente soluzione, questo risolve il problema in modo più affidabile. Tuttavia, poiché sto utilizzando un timer dopo l'evento onRelease per determinare se la mappa è ancora in animazione, c'è un leggero ritardo in questa soluzione.

Il codice può essere trovato su Github tramite questo link: https://github.com/MadsFrandsen/MapStateListener

La mia soluzione può essere utilizzata da un'attività nel modo seguente:

new MapStateListener(mMap, mMapFragment, this) { 
    @Override 
    public void onMapTouched() { 
    // Map touched 
    } 

    @Override 
    public void onMapReleased() { 
    // Map released 
    } 

    @Override 
    public void onMapUnsettled() { 
    // Map unsettled 
    } 

    @Override 
    public void onMapSettled() { 
    // Map settled 
    } 
}; 
+3

sembra funzionare bene per me, è un bel miglioramento per la soluzione @ Az13 :) – Javier

+0

@Mads Frandsen, possibile condividere come si combinerebbe la libreria con Marker setOnMarkerClickListener? Ho provato a usare la libreria e quando si preme su un qualsiasi marker, il codice verrà sempre eseguito su fapSettled fuction –

+0

@simeh Sì, penso che qualsiasi tocco verrà conteggiato come non risolto. Probabilmente potresti controllare se ci fosse qualche movimento prima di impostarlo come non settato - questo eviterebbe la chiamata onMapSettled quando è solo un clic. Potrei essere in grado di passare attraverso il mio (vecchio) pezzo di codice, questo fine settimana o il prossimo, se non hai già risolto il problema per allora. –

0
@Override 
public boolean onTouchEvent(MotionEvent event, MapView mapView){ 

    if(event.getAction() == MotionEvent.ACTION_MOVE) 
     return true; 

    return false; 
} 
+2

Questa domanda è contrassegnata come di bassa qualità perché se la sua lunghezza e il contenuto. Potresti aggiungere una spiegazione su come questo risolve il problema e forse citare le funzioni utilizzate? – Popnoodles

0

penso che l'evento onclick nella mappa è: map.setOnMapClick ... Ma evento trascinamento è: map.onCameraChangeListener perché chiamo un log.e in entrambe le funzioni e viene visualizzato come onClick view e onDrag view. Quindi usali per te.

4

A partire da mappe di giochi di gioco 9.4.0 puoi semplicemente usare GoogleMap.OnCameraMoveStartedListener, GoogleMap.OnCameraMoveListener e GoogleMap.OnCameraIdleListener.

Se, per qualche motivo, si desidera utilizzare l'API precedente, che ora è deprecata, è possibile utilizzare onCameraChangeListener. Ma bisogna essere consapevoli di due cose:

  1. onCameraChange() potrebbe essere chiamato molte volte mentre si trascina la mappa o solo una volta (quando si trascinano fermato).
  2. La posizione della videocamera nell'ultima chiamata di onCameraChange() può essere leggermente diversa dalla posizione della videocamera finale.

Il codice seguente prende entrambe le questioni in considerazione:

private static final int MESSAGE_ID_SAVE_CAMERA_POSITION = 1; 
private static final int MESSAGE_ID_READ_CAMERA_POSITION = 2; 
private CameraPosition lastCameraPosition; 
private Handler handler; 
private GoogleMap googleMap; 

public void onMapReady(GoogleMap theGoogleMap) { 
    googleMap = theGoogleMap; 

    handler = new Handler() { 
     public void handleMessage(Message msg) { 
      if (msg.what == MESSAGE_ID_SAVE_CAMERA_POSITION) { 
       lastCameraPosition = googleMap.getCameraPosition(); 
      } else if (msg.what == MESSAGE_ID_READ_CAMERA_POSITION) { 
       if (lastCameraPosition.equals(googleMap.getCameraPosition())) { 
        Log.d(LOG, "Camera position stable"); 
       } 
      } 
     } 
    }; 

    googleMap.setOnCameraChangeListener(new GoogleMap.OnCameraChangeListener() { 
     @Override 
     public void onCameraChange(CameraPosition cameraPosition) { 
      handler.removeMessages(MESSAGE_ID_SAVE_CAMERA_POSITION); 
      handler.removeMessages(MESSAGE_ID_READ_CAMERA_POSITION); 
      handler.sendEmptyMessageDelayed(MESSAGE_ID_SAVE_CAMERA_POSITION, 300); 
      handler.sendEmptyMessageDelayed(MESSAGE_ID_READ_CAMERA_POSITION, 600); 
     } 
    }); 
} 
0

soluzione avanzata con una classe interna Handler in Xamarin Android, basata su Tobus risposta:

public void OnMapReady(GoogleMap googleMap) 
{ 
     _googleMap = googleMap; 

     if (_googleMap != null) 
     { 
      _cameraPositionHandler = new CameraPositionlHandler(_googleMap); 

      _googleMap.CameraChange += OnCameraChanged; 

     } 
} 

void OnCameraChanged (object sender, GoogleMap.CameraChangeEventArgs e) 
{ 
    _cameraPositionHandler.RemoveMessages(MESSAGE_ID_SAVE_CAMERA_POSITION); 
    _cameraPositionHandler.RemoveMessages(MESSAGE_ID_READ_CAMERA_POSITION);     
    _cameraPositionHandler.SendEmptyMessageDelayed(MESSAGE_ID_SAVE_CAMERA_POSITION, 300); 
    _cameraPositionHandler.SendEmptyMessageDelayed(MESSAGE_ID_READ_CAMERA_POSITION, 600); 

} 

Con la seguente classe interna :

private class CameraPositionlHandler : Handler 
    { 
     private CameraPosition _lastCameraPosition; 
     private GoogleMap _googleMap; 

     public CameraPositionlHandler (GoogleMap googleMap) 
     { 
      _googleMap = googleMap; 
     } 

     public override void HandleMessage(Message msg) 
     { 
      if (_googleMap != null) 
      { 
       if (msg.What == MESSAGE_ID_SAVE_CAMERA_POSITION) { 
        _lastCameraPosition = _googleMap.CameraPosition; 
       } else if (msg.What == MESSAGE_ID_READ_CAMERA_POSITION) { 
        if (_lastCameraPosition.Equals(_googleMap.CameraPosition)) { 
         Console.WriteLine("Camera position stable"); 
         //do what you want 
        } 
       } 
      } 
     } 
    } 
26

Scopri nuovo m aps api.

@Override 
public void onMapReady(GoogleMap map) { 
    mMap = map; 

    mMap.setOnCameraIdleListener(this); 
    mMap.setOnCameraMoveStartedListener(this); 
    mMap.setOnCameraMoveListener(this); 
    mMap.setOnCameraMoveCanceledListener(this); 

    // Show Sydney on the map. 
    mMap.moveCamera(CameraUpdateFactory 
      .newLatLngZoom(new LatLng(-33.87365, 151.20689), 10)); 
} 

@Override 
public void onCameraMoveStarted(int reason) { 

    if (reason == OnCameraMoveStartedListener.REASON_GESTURE) { 
     Toast.makeText(this, "The user gestured on the map.", 
         Toast.LENGTH_SHORT).show(); 
    } else if (reason == OnCameraMoveStartedListener 
          .REASON_API_ANIMATION) { 
     Toast.makeText(this, "The user tapped something on the map.", 
         Toast.LENGTH_SHORT).show(); 
    } else if (reason == OnCameraMoveStartedListener 
          .REASON_DEVELOPER_ANIMATION) { 
     Toast.makeText(this, "The app moved the camera.", 
         Toast.LENGTH_SHORT).show(); 
    } 
} 

@Override 
public void onCameraMove() { 
    Toast.makeText(this, "The camera is moving.", 
        Toast.LENGTH_SHORT).show(); 
} 

@Override 
public void onCameraMoveCanceled() { 
    Toast.makeText(this, "Camera movement canceled.", 
        Toast.LENGTH_SHORT).show(); 
} 

@Override 
public void onCameraIdle() { 
    Toast.makeText(this, "The camera has stopped moving.", 
        Toast.LENGTH_SHORT).show(); 
} 

developers.google.com sample

+0

Soluzione perfetta! – Blablablabli

1

devo animare il mio contrassegno per centrare fino a quando l'utente trascina la mappa. Ho implementato usando Stas Shakirov risposta

MapDragListenerFragment.class

public class MapDragListenerFragment extends Fragment implements OnMapReadyCallback, GoogleMap.OnMapLoadedCallback { 

    private Context mContext; 
    private SupportMapFragment supportMapFragment; 
    private Marker centerMarker; 
    private LatLng mapCenterLatLng; 
    private TextView tvLocationName; 
    private Button btnFinalizeDestination; 
    private GoogleMap mGoogleMap; 

    @Nullable 
    @Override 
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 
     return inflater.inflate(R.layout.fragment_map_drag_listener, container, false); 
    } 

    @Override 
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { 
     super.onViewCreated(view, savedInstanceState); 
     mContext = getActivity(); 

     tvLocationName = (TextView) view.findViewById(R.id.tv_location_name); 
    } 

    @Override 
    public void onActivityCreated(@Nullable Bundle savedInstanceState) { 
     super.onActivityCreated(savedInstanceState); 

     FragmentManager fm = getActivity().getSupportFragmentManager();//getChildFragmentManager();// 
     supportMapFragment = (SupportMapFragment) fm.findFragmentById(R.id.map_container); 
     if (supportMapFragment == null) { 
      //// FIXME: 2/13/2017 crashes at casting to TouchableMapFragment 
      supportMapFragment = SupportMapFragment.newInstance(); 
      fm.beginTransaction().replace(R.id.map_container, supportMapFragment).commit(); 
     } 
     supportMapFragment.getMapAsync(this); 

    } 

    @Override 
    public void onMapReady(GoogleMap googleMap) { 

     if (googleMap != null) { 
      mGoogleMap = googleMap; 

      centerMarker = mGoogleMap.addMarker(new MarkerOptions().position(mGoogleMap.getCameraPosition().target) 
        .title("Center of Map") 
        .icon(BitmapDescriptorFactory.fromResource(R.drawable.end_green))); 

      mGoogleMap.setOnCameraIdleListener(new GoogleMap.OnCameraIdleListener() { 
       @Override 
       public void onCameraIdle() { 
        mapCenterLatLng = mGoogleMap.getCameraPosition().target; 

        animateMarker(centerMarker,mapCenterLatLng,false); 

        Toast.makeText(mContext, "The camera has stopped moving.", 
          Toast.LENGTH_SHORT).show(); 

        String address = getCompleteAddressString(mapCenterLatLng.longitude,mapCenterLatLng.longitude); 
        tvLocationName.setText(address); 
       } 
      }); 
      mGoogleMap.setOnCameraMoveStartedListener(new GoogleMap.OnCameraMoveStartedListener() { 
       @Override 
       public void onCameraMoveStarted(int reason) { 
        if (reason == GoogleMap.OnCameraMoveStartedListener.REASON_GESTURE) { 
         ///tvLocationName.setText("Lat " + mapCenterLatLng.latitude + " Long :" + mapCenterLatLng.longitude); 
         Toast.makeText(mContext, "The user gestured on the map.", 
           Toast.LENGTH_SHORT).show(); 
        } else if (reason == GoogleMap.OnCameraMoveStartedListener 
          .REASON_API_ANIMATION) { 
         Toast.makeText(mContext, "The user tapped something on the map.", 
           Toast.LENGTH_SHORT).show(); 
        } else if (reason == GoogleMap.OnCameraMoveStartedListener 
          .REASON_DEVELOPER_ANIMATION) { 
         Toast.makeText(mContext, "The app moved the camera.", 
           Toast.LENGTH_SHORT).show(); 
        } 
       } 
      }); 
      mGoogleMap.setOnCameraMoveListener(new GoogleMap.OnCameraMoveListener() { 
       @Override 
       public void onCameraMove() { 
        Toast.makeText(mContext, "The camera is moving.", 
          Toast.LENGTH_SHORT).show(); 
       } 
      }); 
      mGoogleMap.setOnCameraMoveCanceledListener(new GoogleMap.OnCameraMoveCanceledListener() { 
       @Override 
       public void onCameraMoveCanceled() { 
        Toast.makeText(mContext, "Camera movement canceled.", 
          Toast.LENGTH_SHORT).show(); 
       } 
      }); 

      mapCenterLatLng = mGoogleMap.getCameraPosition().target;// it should be done on MapLoaded. 

      if (ActivityCompat.checkSelfPermission(getActivity(), Manifest.permission.ACCESS_FINE_LOCATION) != 
        PackageManager.PERMISSION_GRANTED && 
        ActivityCompat.checkSelfPermission(getActivity(), Manifest.permission.ACCESS_COARSE_LOCATION) != 
          PackageManager.PERMISSION_GRANTED) { 
       return; 
      } 
      mGoogleMap.setMyLocationEnabled(true); 
      mGoogleMap.animateCamera(CameraUpdateFactory.zoomTo(15)); 

      mGoogleMap.setOnMapLoadedCallback(this); 
      mGoogleMap.setOnCameraMoveListener(new GoogleMap.OnCameraMoveListener() { 
       @Override 
       public void onCameraMove() { 

       } 
      }); 
     } 
    } 

    public void animateMarker(final Marker marker, final LatLng toPosition, 
           final boolean hideMarker) { 
     final Handler handler = new Handler(); 
     final long start = SystemClock.uptimeMillis(); 
     Projection proj = mGoogleMap.getProjection(); 
     Point startPoint = proj.toScreenLocation(marker.getPosition()); 
     final LatLng startLatLng = proj.fromScreenLocation(startPoint); 
     final long duration = 500; 
     final Interpolator interpolator = new LinearInterpolator(); 
     handler.post(new Runnable() { 
      @Override 
      public void run() { 
       long elapsed = SystemClock.uptimeMillis() - start; 
       float t = interpolator.getInterpolation((float) elapsed 
         /duration); 
       double lng = t * toPosition.longitude + (1 - t) 
         * startLatLng.longitude; 
       double lat = t * toPosition.latitude + (1 - t) 
         * startLatLng.latitude; 
       marker.setPosition(new LatLng(lat, lng)); 
       if (t < 1.0) { 
        // Post again 16ms later. 
        handler.postDelayed(this, 16); 
       } else { 
        if (hideMarker) { 
         marker.setVisible(false); 
        } else { 
         marker.setVisible(true); 
        } 
       } 
      } 
     }); 
    } 
} 

dove fragment_map_drag_listener.xml

<?xml version="1.0" encoding="utf-8"?> 
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:orientation="vertical"> 

    <RelativeLayout 
     android:layout_width="match_parent" 
     android:layout_height="0dp" 
     android:layout_weight="1"> 

     <fragment 
      android:id="@+id/map_container" 
      android:name="com.google.android.gms.maps.SupportMapFragment" 
      android:layout_width="match_parent" 
      android:layout_height="match_parent" /> 

     <ImageView 
      android:id="@+id/iv_center_overlay" 
      android:layout_width="25dp" 
      android:layout_height="25dp" 
      android:visibility="gone" 
      android:layout_centerInParent="true" 
      android:src="@drawable/start_blue" /> 
    </RelativeLayout> 


    <TextView 
     android:id="@+id/tv_location_name" 
     android:layout_width="match_parent" 
     android:layout_height="wrap_content" 
     android:padding="4dp" 
     android:text="Location Name" /> 
</LinearLayout> 

dove MapDragListenerActivity

public class MapDragListenerActivity extends AppCompatActivity { 

    private Context mContext; 
    private static final String TAG = MapDragListenerFragment.class.getSimpleName(); 
    private MapDragListenerFragment mapDragListenerFragment; 

    private Button selectPlaceBtn; 
    public static final int PLACE_AUTOCOMPLETE_REQUEST_CODE = 1219; 

    @Override 
    protected void onCreate(@Nullable Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.activity_map_drag_listener); 

     mContext = MapDragListenerActivity.this; 

     mapDragListenerFragment = new MapDragListenerFragment(); 
     getSupportFragmentManager().beginTransaction() 
       .replace(R.id.frame_container,//where frame_container is a FrameLayout 
         mapDragListenerFragment, 
         MapyFragment.class.getSimpleName()).commit(); 


     selectPlaceBtn = (Button) findViewById(R.id.btn_select_place); 

     selectPlaceBtn.setOnClickListener(new View.OnClickListener() { 
      @Override 
      public void onClick(View v) { 
       try { 
        Intent intent = new PlaceAutocomplete.IntentBuilder(
          PlaceAutocomplete.MODE_FULLSCREEN).build(MapDragListenerActivity.this); 
        startActivityForResult(intent, PLACE_AUTOCOMPLETE_REQUEST_CODE); 
       } catch (GooglePlayServicesRepairableException e) { 
        e.printStackTrace(); 
       } catch (GooglePlayServicesNotAvailableException e) { 
        e.printStackTrace(); 
       } 
      } 
     }); 
    } 

    @Override 
    protected void onActivityResult(int requestCode, int resultCode, Intent data) { 
     super.onActivityResult(requestCode, resultCode, data); 

     if(requestCode == PLACE_AUTOCOMPLETE_REQUEST_CODE){ 
      if (resultCode == RESULT_OK) { 
       Place place = PlaceAutocomplete.getPlace(mContext, data); 
       if(mapDragListenerFragment != null && mapDragListenerFragment.isVisible()) 
        mapDragListenerFragment.updateMarkerAtPosition(
          place.getLatLng() ,place.getName().toString()); 

       Log.i(TAG, "Place:" + place.toString()); 
      } else if (resultCode == PlaceAutocomplete.RESULT_ERROR) { 
       Status status = PlaceAutocomplete.getStatus(mContext, data); 
       Log.i(TAG, status.getStatusMessage()); 
      } else if (requestCode == RESULT_CANCELED) { 

      } 
     } 
    } 
} 

activity_map_drag_listener.xml

<?xml version="1.0" encoding="utf-8"?> 
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:orientation="vertical"> 

    <Button 
     android:id="@+id/btn_select_place" 
     android:layout_width="match_parent" 
     android:layout_height="wrap_content" 
     android:text="Select Place" /> 

    <FrameLayout 
     android:id="@+id/frame_container" 
     android:layout_width="match_parent" 
     android:layout_height="match_parent" /> 

</LinearLayout> 
0

On camera idle is what you should use now

googleMap.setOnCameraIdleListener(new GoogleMap.OnCameraIdleListener() { 
     @Override 
     public void onCameraIdle() { 
      //Called when camera movement has ended, there are no pending animations and the user has stopped interacting with the map. 
     } 
}); 
0

Il modo più semplice è quello di utilizzare il metodo setOnCameraIdleListener per gestire la vostra mossa stato finale del tatto ascoltatore sulla mappa frammento. vedere l'esempio di seguito:

mMap.setOnCameraMoveStartedListener(new GoogleMap.OnCameraMoveStartedListener() { 
    @Override 
    public void onCameraMoveStarted(int i) { 
     mapPin.startAnimation(animZoomOut); 
    } 
}); 

mMap.setOnCameraIdleListener(new GoogleMap.OnCameraIdleListener() { 
    @Override 
    public void onCameraIdle() { 
     mapPin.startAnimation(animZoomIn); 
    } 
}); 
Problemi correlati