Antwort

25

ist eine mögliche Abhilfe für die Bestimmung Drag Start- und Reibungs Endereignisse:

Sie haben SupportMapFragment oder MapFragment zu verlängern. In onCreateView müssen Sie Ihr MapView in ein benutzerdefiniertes FrameLayout einbinden (im Beispiel unten ist es die Klasse "TouchableWrapper"), in dem Sie Touch-Events abfangen und erkennen, ob die Map angetippt ist oder nicht. Wenn Ihr "onCameraChange" aufgerufen wird, überprüfen Sie einfach, ob die Kartenansicht gedrückt ist oder nicht (im Beispiel darunter ist die Variable "mMapIsTouched").

Beispielcode:

UPDATE 1:

  • return ursprüngliche erzeugte Ansicht in getView()
  • Verwendung dispatchTouchEvent() statt onInterceptTouchEvent()

Customized 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); 

    } 

} 

In Ihrem individuellen MapFragment:

@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; 
} 

In Ihrer Kamera ändern Callback-Methode:

private final OnCameraChangeListener mOnCameraChangeListener = 
     new OnCameraChangeListener() { 

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

Hey @Alexey Zakharov, bitte werfen Sie auch einen Blick in den Thread bei Google Maps Issue Tracker: http://code.google.com/p/gmaps-api-issues/issues/detail?id=4636&q=apitype%3DAndroid2&sort= -modified & colspec = ID% 20Typ% 20Status% 20Eingeführt% 20Fixed% 20Zusammenfassung% 20Stars% 20ApiType% 20Internal% 20Modifiziert – AZ13

+0

Das stimmt. Es ist nicht garantiert, dass onCameraChanged() aufgerufen wird, nachdem Sie das Ereignis ACTION_UP ermittelt haben. Wie auch immer, ich hoffe auch, dass Google dieses Problem sehr bald beheben wird! – AZ13

+0

sehr hilfreiche Antwort, danke –

8

Ich würde versuchen, eine onCameraChangeListener. Der Hörer wird jedes Mal aufgerufen, wenn eine Bewegung der Kamera beendet ist. Der Zuhörer gibt Ihnen auch den neuen Standort. In meinen Tests wurde der Hörer beim Ziehen oft ziemlich oft gerufen, vielleicht gibt es eine bessere Lösung. Hier

+2

Ich fand diese Methode, aber wie Sie sagten, es feuert sehr oft. Ich muss nur geocodieren, wenn der Nutzer aufhört, die Karte zu ziehen und seinen Finger ablegt. Es scheint also, dass ich mich auch selbst anfassen muss. Aber in der neuen Map-API gibt es keinen onTouch-Handler. –

+0

@AlexeyZakharov hat recht, es wird zu oft ausgelöst, wodurch die Animation stecken bleibt Die Antwort von AZ13 ist wirklich gut. – TypingPanda

23

Nach oben AZ13-Lösung verwendet wird, und in das Problem in den Kommentaren erwähnt ausgeführt wird, habe ich die folgende Lösung, Das löst das Problem zuverlässiger. Da ich jedoch nach dem onRelease-Ereignis einen Timer verwende, um festzustellen, ob die Map noch animiert, gibt es bei dieser Lösung eine leichte Verzögerung.

Der Code kann über diesen Link auf Github zu finden: https://github.com/MadsFrandsen/MapStateListener

Meine Lösung kann auf die folgende Art und Weise aus einer Tätigkeit verwendet werden:

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

scheint gut für mich, es ist eine nette Verbesserung zu @ Az13 Lösung :) – Javier

+0

@Mads Frandsen, möglich zu teilen, wie würden Sie Ihre Bibliothek mit Marker setOnMarkerClickListener kombinieren? Ich habe versucht, Ihre Bibliothek zu verwenden, und wenn Sie auf einen Marker drücken, wird der Code immer ausgeführt onMapSettled fuction –

+0

@simeh Ja, ich denke, jede Berührung wird als unbegründet zählen. Sie könnten wahrscheinlich prüfen, ob es eine Bewegung gab, bevor Sie es als unbestimmt setzen - das würde den onMapSettled-Aufruf vermeiden, wenn es nur ein Klick ist. Ich könnte vielleicht mein (altes) Stück Code an diesem oder jenem Wochenende durchgehen, wenn Sie das Problem bis dahin noch nicht gelöst haben. –

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

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

    return false; 
} 
+2

Diese Frage wird wegen ihrer Länge und ihres Inhalts als minderwertig markiert. Könnten Sie eine Erklärung hinzufügen, wie dies das Problem löst und vielleicht die verwendete (n) Funktion (en) anführen? – Popnoodles

0

denke ich, das Ereignis Onclick in der Karte ist: map.setOnMapClick ... Aber Ereignis ziehen ist: map.onCameraChangeListener, weil ich eine log.e in beiden Funktionen aufrufen und es wie onClick anzeigen und onDrag anzeigen. Also benutze sie nur für dich.

4

Starten mit play-services-maps 9.4.0 können Sie einfach GoogleMap.OnCameraMoveStartedListener, GoogleMap.OnCameraMoveListener und GoogleMap.OnCameraIdleListener verwenden.

Wenn Sie aus irgendeinem Grund die ältere API verwenden möchten, die jetzt veraltet ist, können Sie onCameraChangeListener verwenden. Aber Sie haben bewusst zwei Dinge sein:

  1. onCameraChange() könnten oft genannt werden, während Sie die Karte ziehen oder nur einmal (wenn gestoppt Schleppen).
  2. Die Kameraposition beim letzten Anruf von onCameraChange() kann geringfügig von der endgültigen Kameraposition abweichen.

Der folgende Code nimmt beiden Probleme zu berücksichtigen:

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

Verbesserte Lösung mit einer Handler inneren Klasse in Xamarin Android, basierend auf tobus Antwort:

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); 

} 

Mit der folgenden inneren Klasse :

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

Check out neue 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

Perfekte Lösung! – Blablablabli

1

Ich habe meinen Marker zu animieren, solange der Benutzer die Karte ziehen zu zentrieren. Ich habe es Stas Shakirov Antwort

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); 
        } 
       } 
      } 
     }); 
    } 
} 

wo 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> 
mit implementiert

wo 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

Der einfachste Weg ist setOnCameraIdleListener Methode zu verwenden, um Ihren Umzug Endstatus Berührungs Zuhörer auf der Karte Fragment zu behandeln. siehe das Beispiel unten:

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); 
    } 
});