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 a strange problem with a com.google.android.gms.maps.MapView.
To check if my App crashes after the garbage collector is doing his job i force my HTC One (4.2.2) to allow only 1 running app in background. If I leave my app(home button) while showing a MapView, start any other app and resume to my app, my MapView is still showing up...but I can not move or zoom the map, it's not responding at all. Other activities are working fine. I really have no clue where the problem might be.
Hope that someone can help me out?
Here is the sourcecode of my fragment that shows the MapView
public class FragmentAdvertlistMap extends Fragment {
com.google.android.gms.maps.MapView m;
GoogleMap mMap;
ArrayList<Advert> ads;
HashMap<Marker, String> myMarker;
public final LatLngBounds.Builder builder= new LatLngBounds.Builder();
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
try {
MapsInitializer.initialize(getActivity());
} catch (GooglePlayServicesNotAvailableException e) {
// TODO handle this situation
}
View inflatedView = inflater.inflate(R.layout.activity_advert_tab2, container, false);
m = (com.google.android.gms.maps.MapView)inflatedView.findViewById(R.id.map_tab);
m.onCreate(savedInstanceState);
myMarker = new HashMap<Marker, String>();
ads= AdvertListActivity.getAdverts();
setUpMapIfNeeded(inflatedView);
mMap.setOnInfoWindowClickListener(new OnInfoWindowClickListener() {
#Override
public void onInfoWindowClick(Marker arg0) {
Intent myIntent = new Intent(getActivity(), AdvertLocationActivity.class);
Advert putadvert = DefaultApplication.dbc.getAdvertForAdvertID(Integer.parseInt(myMarker.get(arg0)));
myIntent.putExtra("advert", putadvert);
startActivity(myIntent);
}
});
return inflatedView;
}
private void setUpMapIfNeeded(View inflatedView) {
if (mMap == null) {
mMap = ((com.google.android.gms.maps.MapView) inflatedView.findViewById(R.id.map_tab)).getMap();
if (mMap != null) {
this.initMarker();
}
}
}
public void initMarker(){
for(int i=0;i<ads.size();i++){
Advert tempAd = ads.get(i);
LatLng tlalo = new LatLng(tempAd.mainLocation.latitude,tempAd.mainLocation.longitude);
builder.include(tlalo);
String address = "";
if(tempAd.mainLocation.contact_street != null){
address = address + tempAd.mainLocation.contact_street;
}
if(tempAd.mainLocation.contact_street_number != null){
address = address + " " + tempAd.mainLocation.contact_street_number;
}
Marker marker = mMap.addMarker(new MarkerOptions()
.position(tlalo)
.anchor(0.5f, 0.5f)
.title(tempAd.name)
.snippet(address)
.icon(BitmapDescriptorFactory.fromResource(R.drawable.androidpin)));
myMarker.put(marker,String.valueOf(tempAd.myid));
}
mMap.setOnCameraChangeListener(new OnCameraChangeListener() {
#Override
public void onCameraChange(CameraPosition arg0) {
mMap.moveCamera(CameraUpdateFactory.newLatLngBounds(builder.build(), 100));
mMap.setOnCameraChangeListener(null);
}
});
}
#Override
public void onResume() {
super.onResume();
m.onResume();
}
#Override
public void onPause() {
super.onPause();
m.onPause();
}
#Override
public void onDestroy() {
super.onDestroy();
m.onDestroy();
}
#Override
public void onLowMemory() {
super.onLowMemory();
m.onLowMemory();
}
}
try add this in onCreateView()
container.removeAllViews();
I found it in another similar question to yours but I lost the original link of the answer...
I want to display multiple markers on google map v2 and also on tap animate marker with a jump animation.
/**
* This shows how to place markers on a map.
*/
public class Map extends FragmentActivity implements
OnMarkerClickListener,
OnInfoWindowClickListener,
OnMarkerDragListener {
static LatLng[] GEOPOINTS;
Map con;
ArrayList<Article> mArticles;
DBHelper helper;
Drawable marker;
Button search, cancel;
EditText search_value;
Button clear_search;
int activity_flag=0;
double slat, vlong;
/**
* Demonstrates customizing the info window and/or its contents.
*/
class CustomInfoWindowAdapter implements InfoWindowAdapter {
private final RadioGroup mOptions;
// These a both viewgroups containing an ImageView with id "badge" and two TextViews with id
// "title" and "snippet".
private final View mWindow;
private final View mContents;
CustomInfoWindowAdapter() {
mWindow = getLayoutInflater().inflate(R.layout.custom_info_window, null);
mContents = getLayoutInflater().inflate(R.layout.custom_info_contents, null);
mOptions = (RadioGroup) findViewById(R.id.custom_info_window_options);
}
#Override
public View getInfoWindow(Marker marker) {
if (mOptions.getCheckedRadioButtonId() != R.id.custom_info_window) {
// This means that getInfoContents will be called.
return null;
}
render(marker, mWindow);
return mWindow;
}
#Override
public View getInfoContents(Marker marker) {
if (mOptions.getCheckedRadioButtonId() != R.id.custom_info_contents) {
// This means that the default info contents will be used.
return null;
}
render(marker, mContents);
return mContents;
}
private void render(Marker marker, View view) {
int badge;
badge = R.drawable.badge_qld;
((ImageView) view.findViewById(R.id.badge)).setImageResource(badge);
String title = marker.getTitle();
TextView titleUi = ((TextView) view.findViewById(R.id.title));
if (title != null) {
// Spannable string allows us to edit the formatting of the text.
SpannableString titleText = new SpannableString(title);
titleText.setSpan(new ForegroundColorSpan(Color.RED), 0, titleText.length(), 0);
titleUi.setText(titleText);
} else {
titleUi.setText("");
}
String snippet = marker.getSnippet();
TextView snippetUi = ((TextView) view.findViewById(R.id.snippet));
if (snippet != null && snippet.length() > 12) {
SpannableString snippetText = new SpannableString(snippet);
snippetText.setSpan(new ForegroundColorSpan(Color.MAGENTA), 0, 10, 0);
snippetText.setSpan(new ForegroundColorSpan(Color.BLUE), 12, snippet.length(), 0);
snippetUi.setText(snippetText);
} else {
snippetUi.setText("");
}
}
}
private GoogleMap mMap;
private Marker[] marks;
private TextView mTopText;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.marker_demo);
con=this;
mTopText = (TextView) findViewById(R.id.top_text);
new loadingTask().execute();
}
#Override
protected void onResume() {
super.onResume();
setUpMapIfNeeded();
}
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 wrap the SupportMapFragment.
mMap = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map)).getPolarisMap();
// Check if we were successful in obtaining the map.
if (mMap != null) {
setUpMap();
}
}
}
private void setUpMap() {
// Hide the zoom controls as the button panel will cover it.
mMap.getUiSettings().setZoomControlsEnabled(false);
// Add lots of markers to the map.
addMarkersToMap();
// Setting an info window adapter allows us to change the both the contents and look of the
// info window.
mMap.setInfoWindowAdapter(new CustomInfoWindowAdapter());
// Set listeners for marker events. See the bottom of this class for their behavior.
mMap.setOnMarkerClickListener(this);
mMap.setOnInfoWindowClickListener(this);
mMap.setOnMarkerDragListener(this);
// Pan to see all markers in view.
// Cannot zoom to bounds until the map has a size.
final View mapView = getSupportFragmentManager().findFragmentById(R.id.map).getView();
if (mapView.getViewTreeObserver().isAlive()) {
mapView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#SuppressWarnings("deprecation") // We use the new method when supported
#SuppressLint("NewApi") // We check which build version we are using.
#Override
public void onGlobalLayout() {
LatLngBounds bounds = new LatLngBounds.Builder().include(GEOPOINTS[0]).build();
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));
}
});
}
}
private void addMarkersToMap() {
// Uses a colored icon.
for (int i=0;i<mArticles.size();i++){
String latt=mArticles.get(i).latitude.trim().replace(",","");
String lonn=mArticles.get(i).longitude.trim();
//set latitude and longitude
GEOPOINTS[i] = new LatLng(Double.valueOf(latt), Double.valueOf(lonn));
marks[i] = mMap.addMarker(new MarkerOptions()
.position(GEOPOINTS[i])
.title(mArticles.get(i).enseigne)
.snippet(mArticles.get(i).type)
.icon(BitmapDescriptorFactory.fromResource(R.drawable.arrow)));
}
}
private boolean checkReady() {
if (mMap == null) {
Toast.makeText(this, R.string.map_not_ready, Toast.LENGTH_SHORT).show();
return false;
}
return true;
}
/**
* Called when the Clear button is clicked.
*/
public void onClearMap(View view) {
if (!checkReady()) {
return;
}
mMap.clear();
}
/**
* Called when the Reset button is clicked.
*/
public void onResetMap(View view) {
if (!checkReady()) {
return;
}
// Clear the map because we don't want duplicates of the markers.
mMap.clear();
addMarkersToMap();
}
//
// Marker related listeners.
//
#Override
public boolean onMarkerClick(final Marker marker) {
// This causes the marker at Perth to bounce into position when it is clicked.
final Handler handler = new Handler();
final long start = SystemClock.uptimeMillis();
Projection proj = mMap.getProjection();
Point startPoint = proj.toScreenLocation(GEOPOINTS[0]);
startPoint.offset(0, -100);
final LatLng startLatLng = proj.fromScreenLocation(startPoint);
final long duration = 1500;
final Interpolator interpolator = new BounceInterpolator();
handler.post(new Runnable() {
#Override
public void run() {
long elapsed = SystemClock.uptimeMillis() - start;
float t = interpolator.getInterpolation((float) elapsed / duration);
double lng = t * GEOPOINTS[0].longitude + (1 - t) * startLatLng.longitude;
double lat = t * GEOPOINTS[0].latitude + (1 - t) * startLatLng.latitude;
marker.setPosition(new LatLng(lat, lng));
if (t < 1.0) {
// Post again 16ms later.
handler.postDelayed(this, 16);
}
}
});
// We return false to indicate that we have not consumed the event and that we wish
// for the default behavior to occur (which is for the camera to move such that the
// marker is centered and for the marker's info window to open, if it has one).
return false;
}
#Override
public void onInfoWindowClick(Marker marker) {
Toast.makeText(getBaseContext(), "Click Info Window", Toast.LENGTH_SHORT).show();
}
#Override
public void onMarkerDragStart(Marker marker) {
mTopText.setText("onMarkerDragStart");
}
#Override
public void onMarkerDragEnd(Marker marker) {
mTopText.setText("onMarkerDragEnd");
}
#Override
public void onMarkerDrag(Marker marker) {
mTopText.setText("onMarkerDrag. Current Position: " + marker.getPosition());
}
class loadingTask extends AsyncTask<Void, Void,Void> {
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
setUpMapIfNeeded();
}
#Override
protected Void doInBackground(Void... params) {
helper = DBHelper.getInstance(con);
mArticles = helper.getArticlesList();
System.out.println(mArticles.toString());
return null;
}
}
}
But my log shows the following error.
java.lang.RuntimeException: Unable to resume activity
{com.example.test/com.example.test.Map}: java.lang.NullPointerException
at android.app.ActivityThread.performResumeActivity(ActivityThread.java:2698)
at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:2726)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2212)
at android.app.ActivityThread.access$600(ActivityThread.java:139)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1205)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4899)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:791)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:558)
at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.NullPointerException
at com.example.test.Map.addMarkersToMap(Map.java:233)
at com.example.test.Map.setUpMap(Map.java:197)
at com.example.test.Map.setUpMapIfNeeded(Map.java:187)
at com.example.test.Map.onResume(Map.java:177)
at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1184)
at android.app.Activity.performResume(Activity.java:5082)
at android.app.ActivityThread.performResumeActivity(ActivityThread.java:2688)
... 12 more
I have two problems, the first one is to display multiple geopoints and the second is to make each marker to jump on tap. can anyone help me.
Update: New Logs after changing onResume()
java.lang.NullPointerException
at com.example.test.Map$1.onGlobalLayout(Map.java:217)
at android.view.ViewTreeObserver.dispatchOnGlobalLayout(ViewTreeObserver.java:646)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1726)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1000)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:4214)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:725)
at android.view.Choreographer.doCallbacks(Choreographer.java:555)
at android.view.Choreographer.doFrame(Choreographer.java:525)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:711)
at android.os.Handler.handleCallback(Handler.java:615)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4899)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:791)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:558)
at dalvik.system.NativeStart.main(Native Method)
Where line 217 is
LatLngBounds bounds = new LatLngBounds.Builder().include(GEOPOINTS[0]).build();
you are getting NullPointerException because mArticles is null when you call setUpMapIfNeeded(); in onResume()
So
Change
#Override
protected void onResume() {
super.onResume();
setUpMapIfNeeded();
}
to
#Override
protected void onResume() {
super.onResume();
new loadingTask().execute();
}
Edit :
your GEOPOINTS[0] is null at line no 217
LatLngBounds bounds = new LatLngBounds.Builder().include(GEOPOINTS[0]).build();
Reason
you have problem inside private void addMarkersToMap(), you are not initializing GEOPOINTS, so initialize GEOPOINT before for loop, for this use : GEOPOINTS=new LatLng[mArticles.size()];
So it Should look like :
GEOPOINTS=new LatLng[mArticles.size()];
for (int i=0;i<mArticles.size();i++){
String latt=mArticles.get(i).latitude.trim().replace(",","");
String lonn=mArticles.get(i).longitude.trim();
//set latitude and longitude
GEOPOINTS[i] = new LatLng(Double.valueOf(latt), Double.valueOf(lonn));
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
}
}
}
}
}