I'm trying to create an android widget based on a ViewSwitcher with a MapView and StreetViewPanoramaView inside. Users can switch from street to map view with a overlayed control bar.
So far all is working fine for the map part, but not for the street view part.
On first display it seems to work but when i tried to move inside (pan and/or zoom) StreetViewPanorama crash (become black).
this is how i create my StreetViewPanorama :
final StreetViewPanoramaOptions options = new StreetViewPanoramaOptions();
options.streetNamesEnabled(true)
.zoomGesturesEnabled(true)
.panningGesturesEnabled(true);
mStreetView = new StreetViewPanoramaView(getActivity(), options);
mStreetView.onCreate(savedInstanceState);
And how I defiened the location for both (map and street view)
// Set map to position and add a new marker
final GoogleMap map = mMapView.getMap();
map.moveCamera(CameraUpdateFactory.newLatLngZoom(mLocation, DEFAULT_ZOOM_LEVEL));
if (mMarker != null){
mMarker.remove();
}
mMarker = map.addMarker(new MarkerOptions().draggable(false).position(mLocation));
// Set street view to location.
final StreetViewPanorama street = mStreetView.getStreetViewPanorama();
street.setPosition(mLocation, 50);
// Hide by default controls and street view button.
// I will be enabled only if it's a valid street view location
mControlsView.setVisibility(View.GONE);
mStreetBtnContainer.setVisibility(View.GONE);
switchToMapView();
// we must add a delay to check if the new street view location is valid.
// see : http://stackoverflow.com/a/23785186
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
mHasStreetView = street.getLocation() != null;
if (mHasStreetView){
mStreetBtnContainer.setVisibility(View.VISIBLE);
}
showControlView();
}
}, STREET_DELAY_MILLIS);
Did someone encounter the same problem ?
Cheers.
Related
I am using Xamarin to render map with pins in Android. I am able Load the map and show custom Info window for my app, but I want a button or a link inside my Info window which when clicked do some computation and start a new activity.
This is what I am looking for:
On click of links (link1 and link2) I would like to redirect user to new screen based on some value. My concern here is how can I get a click function for the links.
Also I want the Info Window to have rounded corners and have a pointer pointing to the pin.
Here is my code for above example:
Activity
public class MainActivity : Activity, IOnMapReadyCallback
{
private GoogleMap GMap;
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.Main);
SetUpMap();
}
private void SetUpMap()
{
if (GMap == null)
{
FragmentManager.FindFragmentById<MapFragment>(Resource.Id.googlemap).GetMapAsync(this);
}
}
public void OnMapReady(GoogleMap googleMap)
{
this.GMap = googleMap;
GMap.UiSettings.ZoomControlsEnabled = true;
LatLng latlng = new LatLng(Convert.ToDouble(15.354942), Convert.ToDouble(73.929845));
MarkerOptions markerOpt = new MarkerOptions();
markerOpt.SetPosition(latlng).SetTitle("test")
.SetSnippet("test")
.SetIcon(BitmapDescriptorFactory.DefaultMarker(BitmapDescriptorFactory.HueOrange));
MapAdapter adapter = new MapAdapter(this);
GMap.SetInfoWindowAdapter(adapter);
GMap.AddMarker(markerOpt).ShowInfoWindow();
GMap.MoveCamera(CameraUpdateFactory.NewLatLngZoom(
new LatLng(15.354942, 73.929845), 20));
}
}
Adapter
public class MapAdapter : Java.Lang.Object, GoogleMap.IInfoWindowAdapter
{
private IOnTouchListener infoButtonListener;
LayoutInflater inflater;
private Activity context;
public MapAdapter(Activity context)
{
this.context = context;
}
public View GetInfoContents(Marker marker)
{
return null;
}
public View GetInfoWindow(Marker marker)
{
View view = context.LayoutInflater.Inflate(Resource.Layout.CustomWindow, null);
TextView Link1 = (TextView)view.FindViewById(Resource.Id.Link1);
TextView Link2 = (TextView)view.FindViewById(Resource.Id.Link2);
String udata = "Link1";
SpannableString content = new SpannableString(udata);
content.SetSpan(new UnderlineSpan(), 0, udata.Length, 0);
gotoLink.SetText(content,TextView.BufferType.Normal);
String udataPickup = "Link2";
SpannableString contentPickup = new SpannableString(udataPickup);
contentPickup.SetSpan(new UnderlineSpan(), 0, udataPickup.Length, 0);
pickupLink.SetText(contentPickup, TextView.BufferType.Normal);
return view;
}
}
Actually, it is not possible, Google Map renders the content of your custom InfoWindow into an image and displays it to you as an Image. Therefore you set a click Listener only to the whole window and not to the Views inside it.
Your only choice is to set the ClickListener to the whole InfoWindow and popup a Dialog with clickable content you want, and not do it directly inside the InfoWindow.
From Google Docs: https://developers.google.com/maps/documentation/android/marker#info_windows
Note: The info window that is drawn is not a live view. The view is rendered as an image (using View.draw(Canvas)) at the time it is returned. This means that any subsequent changes to the view will not be reflected by the info window on the map. To update the info window later (e.g., after an image has loaded), call showInfoWindow(). Furthermore, the info window will not respect any of the interactivity typical for a normal view such as touch or gesture events. However, you can listen to a generic click event on the whole info window as described in the section below.`
Here is the problem:
I want to show the user the best route to reach a marker after the user presses a button inside the infoWindow. The problem is, I can't get the marker's Location data due to some problem with Latlng and Position classes. (I am using the Mapbox example to get the route, so I need two Position values)
So basically, I need to update the variable Destination with the marker's position by clicking a button inside the custom infowindow. Yet I have no idea how can I do that even though going through a lot on searching Google and Stack Overflow. Can someone help me? (Cammack!) Thanks a bunch for the help!
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//...
// Origem: Rodoviaria
final Position origin = Position.fromCoordinates(-47.882645, -15.794082);
// Destino: Reitoria
final Position destination = Position.fromCoordinates(-47.866611, -15.762604);
//...
mapboxMap.setInfoWindowAdapter(new MapboxMap.InfoWindowAdapter() {
#Nullable
#Override
public View getInfoWindow(#NonNull Marker marker) {
//...
final Marker marcador = marker;
botaoIr = (Button)v.findViewById(R.id.botaoIr);
botaoIr.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//I have been trying the method below, but I am having trouble with LatLng and Position classes!
//LatLng ponto = marcador.getPosition();
//destination = new Position(ponto.getLongitude(),ponto.getLatitude());
try {
getRoute(origin, destination);
} catch (ServicesException servicesException) {
servicesException.printStackTrace();
}
}
});
}
});
//...
}
To create a position call fromCoordinates().
destination = Position.fromCoordinates(ponto.getLongitude(),ponto.getLatitude());
If you want to remove all annotations on your map, you can call "removeAnnotations()" on your MapBoxMap-Instance:
mapboxMap.removeAnnotations();
..or you can call "remove()" on any annotation from MapBox itself (Polygon, Polyline or Marker)
Polygon pol = new Polygon();
pol.remove();
If it's still existing and you just want to change & update an annotation, call "updatePolygon()", "updateMarker" or "updatePolyline() like this:
mapboxMap.updatePolygon(myPolgonInstance);
mapboxMap.updateMarker(myMarkerInstance);
...
I want to show the info window on the top right corner of the map when user click on the any marker in google map.
Its info window should always show on the top right corner of the map.
// Setting a custom info window adapter for the google map
mMap.setInfoWindowAdapter(new GoogleMap.InfoWindowAdapter() {
// Use default InfoWindow frame if return null
// else inflate any layout
#Override
public View getInfoWindow(Marker marker) {
/*showAlertToChooseImage();*/
if (!marker.getTitle().equalsIgnoreCase("Helipad")) {
// Getting view from the layout file info_window_layout
View view = getActivity().getLayoutInflater().inflate(R.layout.windowlayout, null);
// Getting reference to the TextView to set latitude
TextView tvGroupName = (TextView) view.findViewById(R.id.txtViewGroupName);
tvGroupName.setText(modelItemMarker.getIncidentGropName());
// Getting reference to the TextView to set longitude
TextView tvAddress = (TextView) view.findViewById(R.id.txtViewAddress);
tvAddress.setText(modelItemMarker.getLOCATION_ADDRESS());
TextView tvDateTime = (TextView) view.findViewById(R.id.txtViewDateTime);
tvDateTime.setText(modelItemMarker.getIncidentDateTimeField());
// Returning the view containing InfoWindow contents
return view;
} else {
return null;
}
}
// Defines the contents of the InfoWindow
#Override
public View getInfoContents(Marker arg0) {
return null;
}
});
// Handle click of the infoWindow.
mMap.setOnInfoWindowClickListener(new GoogleMap.OnInfoWindowClickListener() {
#Override
public void onInfoWindowClick(Marker marker) {
Bundle bundle = new Bundle();
bundle.putString(Constants.CallNO, modelItemMarker.getCadIdField());
SideMenuFragment_Left.IsChangePwd = false;
SideMenuFragment_Right.IsAgencies = false;
Intent intent = new Intent(mContext, IncidentDetailActivity.class);
intent.putExtra(Constants.INCIDENT_DETAIL, bundle);
startActivity(intent);
}
});
Can anybody please help.
Thanks in advance
try on this way .
public class MarkerDemoActivity extends AppCompatActivity implements
OnInfoWindowClickListener,
OnMapReadyCallback {
private GoogleMap mMap;
#Override
public void onMapReady(GoogleMap map) {
mMap = map;
// Add markers to the map and do other map setup.
...
// Set a listener for info window events.
mMap.setOnInfoWindowClickListener(this);
}
#Override
public void onInfoWindowClick(Marker marker) {
Toast.makeText(this, "Info window clicked",
Toast.LENGTH_SHORT).show();
}
}
As the above code you will get the info window top of the marker.But if you want to do top left corner i am thinking you need to take one more layout and you have to add this layout to maps activity on clicking of marker.
Current functions from Google Maps API for Android cannot achieve that. Try implementing it to your Android App using the Google Maps Javascript API via a WebFrame instead.
You can use the InfoWindowOptions to customize the position by using the pixelOffset positions option
More info on that here.
I haven't found any complete explanation on how to use this technique on Android so I decided to create a Q&A thread.
If your app has to show a large amount of markers on a google map and clustering them is not enough to prevent your app from working too slow, then one of your best choices is to use this Viewport Marker Management technique. You can read the theoretical explanation here: https://developers.google.com/maps/articles/toomanymarkers
I wrote a short guide below...
1°--- In the activity where map is created you have to set the OnCameraChangeListener and get the bounds of your screen like this:
mMap.setOnCameraChangeListener(new OnCameraChangeListener() {
#Override
public void onCameraChange(CameraPosition arg0) {
LatLngBounds bounds = mapa.getProjection().getVisibleRegion().latLngBounds;
}
2°--- This step may vary depending how you fetch the markers data. Basically, what you have to do is to calculate if the lat and long of each of your markers are inside the screen bounds. I will show you how to do it by fetching the data from a SQLite data base storing latitud and longitude in two different DOUBLE clomuns inside the markers table.
mMap.setOnCameraChangeListener(new OnCameraChangeListener() {
#Override
public void onCameraChange(CameraPosition arg0) {
LatLngBounds bounds = mMap.getProjection().getVisibleRegion().latLngBounds;
LatLng northeast = bounds.northeast;
String boundLat = String.valueOf(northeast.latitude);
String boundLong = String.valueOf(northeast.longitude);
LatLng southwest = bounds.southwest;
String boundLat2 = String.valueOf(southwest.latitude);
String boundLong2 = String.valueOf(southwest.longitude);
//Remove all markers from map
mMap.clear(); // or if your a using cluster manager:
//mClusterManager.clearItems();
String[] fields = new String[] { "name", "latitude", "longitude" };
String[] args = new String[] {boundLat, boundLong, boundLat2, boundLong2,};
Cursor markers = dataBase.query("markers", fields, "latitude<=? AND longitude<=? AND latitude>=? AND longitude>=?");
if (markers.moveToFirst()) {
do {
mMap.addMarker(new MarkerOptions()
.position(new LatLng(marker.getDouble(1), marker.getDouble(2)))
.title(marker.getString(0)) );
// or if you are using cluster manager create and add the items as you normaly do.
} while (c.moveToNext());
//if using cluster manager add :
//mClusterManager.cluster();
}
}
});
The idea is pretty easy, just have in mind that your markers lat and longi have to be smaller than the northeast position of your screen and bigger than the southwest corner, or just use the LatLngBounds.contains function.
EDITED:
To avoid InfoWindow getting closed when clicking on a marker which is not already in the center of the screen, you can change the marker click listener default action, removing the camera move.
map.setOnMarkerClickListener(new OnMarkerClickListener() {
#Override
public boolean onMarkerClick(Marker arg0) {
arg0.showInfoWindow();
return true; //must be true, if not, it will execute the default code after yours
}
});
I need display >100 markers on the map and show info window after click on marker.
I have used android google maps api v2, but it is lagging with such number of markers. So I decided switch to android-maps-extensions. After I did it info window has stopped appear after click on marker.
Here is my code:
private void prepareMap() {
SupportMapFragment fragment = (SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.locationsMap);
map = fragment.getExtendedMap();
ClusteringSettings settings = new ClusteringSettings();
settings.clusterOptionsProvider(new ClusterOptionsProvider() {
#Override
public ClusterOptions getClusterOptions(List<Marker> markers) {
float hue = BitmapDescriptorFactory.HUE_RED;
BitmapDescriptor icon = BitmapDescriptorFactory.defaultMarker(hue);
return new ClusterOptions().icon(icon);
}
});
settings.clusterSize(100);
settings.addMarkersDynamically(true);
map.setClustering(settings);
map.setOnMarkerClickListener(new GoogleMap.OnMarkerClickListener() {
#Override
public boolean onMarkerClick(Marker marker) {
// breakpoint here are working. marker is not null
marker.showInfoWindow();
return false;
}
});
}
public void onLoadFinished(android.support.v4.content.Loader loader, Cursor cursor) {
if (map != null) {
cursor.moveToFirst();
int count = cursor.getCount();
for (int i = 0; i < count; i++) {
Location location = new Location(cursor);
MarkerOptions options = new MarkerOptions();
options.position( new LatLng(location.getLatitude(), location.getLongitude()));
options.title(location.getTitle()); // title is not null
options.snippet(location.getAddress()); // address is not null
options.data(location);
map.addMarker(options);
cursor.moveToNext();
}
}
}
Any suggestions? I have try use own InfoWindowAdapter, but it is not scaling this window for appropriate content, however I am using wrap_content attribute in xml.
As there is no support for setting title on ClusterOptions (yet), you will have to use InfoWindowAdapter (you may put a separate question on why it is not working as you want it) or add title to ClusterOptions yourself and make sure its value is forwarded to virtual marker that represents cluster. This sounds complicated but is fairly easy. You may also want to add issue on GitHub regarding title not being supported for clustrers.
After that you may use something like:
return new ClusterOptions().icon(icon).title("Test");
Then you will have to figure out what title will best fit your needs. You will most likely want to use List<Marker> to calculate title for this group of markers.
Note: If you zoom enough to show separate markers, you can click on them to show title. If not, there is something more wrong in your code.