I have main activity with fragments and one of the fragments is Map Fragment. Now, when i tap there, every time it opens slow. I am using singleton instance of fragment, but it still doesnt work as expected. Here is a good of that class:
public MapsFragment() {
gson = new Gson();
}
public static MapsFragment getInstance() {
if (mInstance == null)
mInstance = new MapsFragment();
return mInstance;
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable final Bundle savedInstanceState) {
final View v = inflater.inflate(R.layout.fragment_maps, container, false);
mOnSavedinstance = savedInstanceState;
mMapView = (MapView) v.findViewById(R.id.map);
mMapWrapperLayout = (MapWrapperLayout) v.findViewById(R.id.map_relative_layout);
mMapView.getMapAsync(MapsFragment.this);
mMapView.onCreate(savedInstanceState);
mMapView.requestFocus();
MapsInitializer.initialize(getActivity());
return v;
}
#Override
public void onMapReady(GoogleMap googleMap) {
mGoogleMap = googleMap;
// MapWrapperLayout initialization
// 39 - default marker height
// 20 - offset between the default InfoWindow bottom edge and it's content bottom edge
mMapWrapperLayout.init(mGoogleMap, Constants.MARKER_HEIGHT);
if (ActivityCompat.checkSelfPermission(getActivity(), Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(getActivity(), Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
mGoogleMap.setMyLocationEnabled(false);
}
mGoogleMap.setMapType(GoogleMap.MAP_TYPE_NORMAL);
setUpMap();
}
private void setUpMap() {
new Thread(new Runnable() {
#Override
public void run() {
final ArrayList<MarkerOptions> markerOptionses = new ArrayList<MarkerOptions>();
int counter = 0;
for (final Places places : Model.getInstance().getPlaces()) {
LatLng location = new LatLng(Double.parseDouble(places.getLat()), Double.parseDouble(places.getLon()));
final MarkerOptions options = new MarkerOptions();
options.snippet(gson.toJson(places));
options.position(location);
options.title(String.valueOf(counter));
markerOptionses.add(options);
counter++;
}
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
for (MarkerOptions markerOptionse : markerOptionses) {
mGoogleMap.addMarker(markerOptionse).setIcon(BitmapDescriptorFactory.fromResource(Model.getInstance().getPlaces()
.get(Integer.parseInt(markerOptionse.getTitle())).getMapsRes(getActivity())));
}
}
});
final CameraPosition cameraPosition = new CameraPosition.Builder()
.target(new LatLng(45.4654, 9.1859)) // Sets the center of the map to location user
.zoom(Constants.CAMERA_ZOOM_LOCATION) // Sets the zoom
.build(); // Creates a CameraPosition from the builder
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
mGoogleMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));
mGoogleMap.setMapType(GoogleMap.MAP_TYPE_NORMAL);
mGoogleMap.setOnMapClickListener(MapsFragment.this);
// mGoogleMap.setOnInfoWindowClickListener(mInfoListener);
}
});
}
}).start();
}
#Override
public void onResume() {
mMapView.onResume();
super.onResume();
}
#Override
public void onDestroy() {
super.onDestroy();
mGoogleMap.clear();
mMapView.onDestroy();
}
#Override
public void onLowMemory() {
super.onLowMemory();
mMapView.onLowMemory();
}
You can try to add markers only if it would be visible? I mean: first move camera to start position and set zoomLevel and just after that add Markers but only if it's in visible sector. And update markers on camera move.
I have two FrameLayout in an activity, I just replace fragments inside those FrameLayout.
It works great with everyfragment BUT the one containing the MapFragment for the GoogleMap.
When I first launch the app it works and I see the map (everything works) but when I replace the fragment and replace it back I just get a blank space like nothing is loaded.
public class MapFragment extends CustomFragment {
private GoogleMap mMap;
private Marker currentMarker;
private OnFragmentInteractionListener mListener;
public static final String ID = "MAP_FRAGMENT";
private static View view;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = null;
try {
view = inflater.inflate(R.layout.fragment_map, container, false);
} catch (InflateException e) {
Log.d("","");
}finally{
setUpMapIfNeeded();
if(view!=null)this.view=view;
return this.view;
}
}
#Override
public void onDestroyView() {
super.onDestroyView();
Fragment f = getFragmentManager().findFragmentById(R.id.map);
if (f != null)getActivity().getFragmentManager().beginTransaction().remove(f).commit();
mMap=null;
currentMarker=null;
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
mListener = (OnFragmentInteractionListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement OnFragmentInteractionListener");
}
}
#Override
public void onDetach() {
super.onDetach();
mListener = null;
}
private void setUpMapIfNeeded() {
// Do a null check to confirm that we have not already instantiated the map.
if (mMap == null) {
// Try to obtain the map from the SupportMapFragment.
mMap = ((com.google.android.gms.maps.MapFragment)getFragmentManager().findFragmentById(R.id.map)).getMap();
// Check if we were successful in obtaining the map.
if (mMap != null) {
setUpMap();
}
}
}
private void setUpMap() {
}
#Override
public void onPause() {
super.onPause();
}
#Override
public void update(FragmentMessage message) {
if(mMap!=null) {
Location loc = (Location) message.getMessages()[0];
if (currentMarker != null)
currentMarker.remove();
currentMarker = mMap.addMarker(new MarkerOptions().position(new LatLng(loc.getLatitude(), loc.getLongitude())).title("Marker"));
CameraPosition cameraPosition = new CameraPosition.Builder()
.target(new LatLng(loc.getLatitude(), loc.getLongitude())) // Sets the center of the map to Mountain View
.zoom(15) // Sets the zoom
.build(); // Creates a CameraPosition from the builder
mMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));
}
}
}
The mMap is never null, so the MapFragment is working I guess but I don't get anything on the screen (see screenshots below).
Does anyone have an idea ? I really don't know what is wrong.
I've the next problem, I'm trying to show some markers in a Google Map, but only show the first marker, not the others. It don't throw any exception, but only shows the first marker (the phone position).
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
context = getActivity().getApplicationContext();
v = inflater.inflate(R.layout.fragment_map, container, false);
Bundle b = getArguments();
receiveExtraData(b);
MapsInitializer.initialize(getActivity());
mMapView = (MapView) v.findViewById(R.id.map);
mMapView.onCreate(mBundle);
setUpMapIfNeeded(v);
return v;
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
listener = (FragmentsListener) activity;
} catch (ClassCastException e) {}
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mBundle = savedInstanceState;
}
private void setUpMapIfNeeded(View inflatedView) {
if (mMap == null) {
mMap = ((MapView) inflatedView.findViewById(R.id.map)).getMap();
if (mMap != null) {
setUpMap();
}
}
}
private void setUpMap() {
GPSPoint point = new GPSPoint(context);
LatLng latLng = point.getCurrentPositionLatLng();
MarkerOptions phoneMarker = new MarkerOptions().position(latLng).title("Marker");
mMap.addMarker(phoneMarker);
phoneMarker.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_GREEN));
CameraPosition cameraPosition = new CameraPosition.Builder().target(latLng).zoom(20).build();
mMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));
addDevicesMarkers(connectedDevices);
}
#Override
public void onResume() {
super.onResume();
mMapView.onResume();
}
#Override
public void onPause() {
super.onPause();
mMapView.onPause();
}
#Override
public void onDestroy() {
mMapView.onDestroy();
super.onDestroy();
}
private void receiveExtraData(Bundle b){
connectedDevices = b.getParcelableArrayList(BundlesKeys.BLUETOOTH_DEVICES_ARRAY);
}
private void getDevicePosition(BluetoothDevice bDevice){
listener.getDeviceLocation(bDevice);
}
private void addDevicesMarkers(ArrayList<BluetoothDevice> list){
if (!list.isEmpty()){
BluetoothDevice device = list.get(0);
getDevicePosition(device);
}
}
public void addMarker(LatLng pos){
MarkerOptions deviceMarker = new MarkerOptions().position(pos).title("Device");
mMap.addMarker(deviceMarker);
}
Somebody knows the fix?? Somebody knows how to program a listener for devices location events?
You just have to use mMap.addMarker() as many times as the number of markers that you want to show. If you are using it only one time, like you are doing inside setUpMap(), you will only see one marker.
Don't forget to change your MarkerOptions attributes for each marker.
I'm getting problems using MapFragment + ListFragment in an Activity,
when I use show() and hide() method, everything works ok, but when I have my application in background and I return, I get the GoogleMap stunned or blocked, and I don't know what to do to solve that. The only solution I got to work fine, is using replace transactions, but I don't like this way, because in every transaction we should initiate all map place balloons, and it doesn't keep your last camera location, so... I don't know what to do.
PS: I use SherlockActionBar
Thanks in advance:
Here is my code:
Activity:
...
#Override
public void onCreate(Bundle savedInstanceState) {
BugSenseHandler.initAndStartSession(this, "f8013578");
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_route_place);
setViews();
setData();
doStuff();
}
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.route_place_list:
analyticTracker.sendView("/RoutePlaceActivity/List");
isMap = true;
changeFragments();
break;
case R.id.route_place_map:
analyticTracker.sendView("/RoutePlaceActivity/Home");
isMap = false;
changeFragments();
break;
default:
break;
}
}
#Override
public void onRouteMapPlaceClick(Place place) {
goToDetails(place);
}
#Override
public void onRouteListPlaceClick(Place place) {
goToDetails(place);
}
#Override
public void onShowMessage(String message, Message type) {
showMessage(message, type);
}
...
private void setData() {
route = getIntent().getExtras().getParcelable("route");
analyticTracker = GoogleAnalytics.getInstance(this).getTracker(Config.GOOGLE_ANALYTICS_ID);
final FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
/*
* If the activity is killed while in BG, it's possible that the
* fragment still remains in the FragmentManager, so, we don't need to
* add it again.
*/
if (mapFragment == null) {
Log.v("RoutePlaceActivity", "mapFragment = null");
mapFragment = new RoutePlaceMapFragment();
ft.add(R.id.route_place_container, mapFragment);
}
ft.hide(mapFragment);
if (listFragment == null) {
Log.v("RoutePlaceActivity", "listFragment = null");
listFragment = RoutePlaceListFragment.newInstance();
ft.add(R.id.route_place_container, listFragment);
}
ft.hide(listFragment);
ft.commit();
}
private void doStuff() {
changeFragments();
sendItineraryPlacesRequest();
}
private void changeFragments() {
final FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out);
if (isMap) {
ft.hide(listFragment);
ft.show(mapFragment);
switcherView.setDisplayedChild(VIEW_LIST);
} else {
ft.hide(mapFragment);
ft.show(listFragment);
switcherView.setDisplayedChild(VIEW_MAP);
}
ft.commit();
}
private void sendItineraryPlacesRequest() {
... {
...
#Override
public void onSuccess(JSONObject response) {
super.onSuccess(response);
Places places = JSONObjectAdapter.getPlaces(response);
mapFragment.addPlaces(places);
listFragment.addPlaces(places);
}
...
});
}
MapFragment:
/********************* Constructors **********************/
...
/********************* Class Methods *********************/
...
#Override
public void onCreate(Bundle savedInstanceState) {
Log.v(CLASS_TAG, "onCreate");
super.onCreate(savedInstanceState);
setData(savedInstanceState);
setUpMapIfNeeded();
}
#Override
public void onResume() {
super.onResume();
setUpMapIfNeeded();
}
...
#Override
public void onDestroyView() {
((ViewGroup)getView()).removeAllViews();
super.onDestroyView();
}
/******************** Public Methods ********************/
public void addPlaces(Places places) {
mMap.clear();
placeMap.clear();
Builder builder = new LatLngBounds.Builder();
for (Place place : places) {
LatLng placePos = new LatLng(place.getLatitude(), place.getLongitude());
builder.include(placePos);
Marker m = mMap.addMarker(new MarkerOptions().position(placePos).title(place.getName()).draggable(false));
placeMap.put(m, place);
}
if (places.size() > 1) {
final LatLngBounds bounds = builder.build();
try {
mMap.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 50));
} catch (Exception e) {
// layout not yet initialized
final View mapView = getView();
if (mapView.getViewTreeObserver().isAlive()) {
mapView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#SuppressWarnings("deprecation")
#SuppressLint("NewApi")
// We check which build version we are using.
#Override
public void onGlobalLayout() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
mapView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
} else {
mapView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
// mMap.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 50));
}
});
}
}
} else if (places.size() != 0) {
final CameraPosition cameraPosition = new CameraPosition.Builder().zoom(17).target(new LatLng(places.get(0).getLatitude(), places.get(0).getLongitude())).tilt(25).build();
try {
mMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition), 4000, null);
} catch (Exception e) {
// layout not yet initialized
final View mapView = getView();
if (mapView.getViewTreeObserver().isAlive()) {
mapView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#SuppressWarnings("deprecation")
#SuppressLint("NewApi")
// We check which build version we are using.
#Override
public void onGlobalLayout() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
mapView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
} else {
mapView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
mMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition), 4000, null);
}
});
}
}
} else {
myListener.onShowMessage("No se han encontrado sitios cercanos", Message.INFO);
}
}
/******************** Private Methods ********************/
private void setData(Bundle savedInstanceState) {
placeMap = new HashMap<Marker, Place>();
mMap = null;
}
private void setUpMapIfNeeded() {
// Do a null check to confirm that we have not already instantiated the map.
if (mMap == null) {
mMap = getMap();
// Check if we were successful in obtaining the map.
if (mMap != null) {
setUpMap();
}
}
}
private void setUpMap() {
mMap.setMyLocationEnabled(true);
mMap.setOnInfoWindowClickListener(this);
// mMap.setOnMarkerClickListener(this);
UiSettings uiSettings = mMap.getUiSettings();
uiSettings.setCompassEnabled(false);
Places places = getArguments().getParcelable("places");
if (places != null) {
addPlaces(places);
}
}
I was also facing the same problem in the new maps api(Maps V2 android).
But i solved it by overriding onSaveInstance and onRestoreInstance method in the container activity..
and not calling super.onSaveInstance(). and super.onRestoreInstance().
This is just a temporary hack.. but i think u will be up and running with your beautiful app..
I want to geocode address as soon as map center has been changed.
How can I handle map moveend with new Google Maps for Android V2? (I'm talking about the case then user drags map by finger)
Check out new maps 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
Here is a possible workaround for determining drag start and drag end events:
You have to extend SupportMapFragment or MapFragment. In onCreateView you have to wrap your MapView in a customized FrameLayout (in example below it is the class "TouchableWrapper"), in which you intercepts touch events and recognizes whether the map is tapped or not. If your "onCameraChange" gets called, just check whether the map view is pressed or not (in example below this is the variable "mMapIsTouched").
Example code:
UPDATE 1:
return original created view in getView()
use dispatchTouchEvent() instead of 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 your customized 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 your camera change callback method:
private final OnCameraChangeListener mOnCameraChangeListener =
new OnCameraChangeListener() {
#Override
public void onCameraChange(CameraPosition cameraPosition) {
if (!mMapIsTouched) {
refreshClustering(false);
}
}
};
OUTDATED Use the new maps API instead. See answer from punksta.
After using AZ13's solution above, and running into the problem mentioned in the comments, I created the following solution, that solves the issue more reliably. However, since I am using a timer after the onRelease event to determine whether the map is still animating, there is a slight delay in this solution.
The code can be found on Github via this link: https://github.com/MadsFrandsen/MapStateListener
My solution can be used from an activity in the following way:
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
}
};
I would try a onCameraChangeListener. The listener is called every time a movement of the camera is finished. The listener will also give you the new location. In my tests the listener was called pretty often during dragging maybe there is a better solution.
The simplest way is to use setOnCameraIdleListener method to handle your move end state of touch listener on your map fragment.
see the example below:
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);
}
});
Starting with play-services-maps 9.4.0 you can simply use GoogleMap.OnCameraMoveStartedListener, GoogleMap.OnCameraMoveListener and GoogleMap.OnCameraIdleListener.
If, for some reason, you want to use the older API which is now deprecated you can use onCameraChangeListener. But you have to be aware of two things:
onCameraChange() might be called many times while you drag the map OR only once (when dragging stopped).
The camera position in the last call of onCameraChange() can be slightly different from the final camera position.
The following code takes both issues into account:
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);
}
});
}
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.
}
});
I have to animate my marker to center as long as the user drag the map. I have implemented it using Stas Shakirov answer
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);
}
}
}
});
}
}
where 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>
where
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>
#Override
public boolean onTouchEvent(MotionEvent event, MapView mapView){
if(event.getAction() == MotionEvent.ACTION_MOVE)
return true;
return false;
}
I think the event onclick in the map is: map.setOnMapClick...
But event drag is: map.onCameraChangeListener because I call a log.e in both of that functions and it shown like onClick view and onDrag view . So just using them for you.
Enhanced solution with an Handler inner Class in Xamarin Android, based on Tobus answer:
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);
}
With the following inner Class:
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
}
}
}
}
}