Question
Why does my custom infowindow not 'inflate' after a configuration change?
Summary
I have an app that places a marker on a map using MarkerManager, and sets a custom info-window using setInfoWindowAdapter.
After loading the app and clicking on the marker, the info-window correctly displays my custom layout.
However, after rotating the app so that the configuration changes, selecting the marker no longer displays the custom info-window, and instead only displays the generic 'title'.
What have I missed in all the hours of reading and tutorials that means I'm not correctly implementing this?
Code
Here is a stripped-down version of my app to replicate the issue
MapsActivity
public class MapsActivity extends FragmentActivity implements OnMapReadyCallback {
private GoogleMap mMap;
MarkerManager.Collection mMarkerManagerCollection;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_maps);
SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
.findFragmentById(R.id.map);
mapFragment.getMapAsync(this);
if(savedInstanceState == null){
mapFragment.setRetainInstance(true); // maintains the map fragment on orientation changes
}
}
#Override
public void onMapReady(GoogleMap googleMap) {
mMap = googleMap;
MarkerManager mMarkerManager = new MarkerManager(mMap);
mMap.setInfoWindowAdapter(mMarkerManager);
mMarkerManagerCollection = mMarkerManager.newCollection("myMarker");
mMarkerManagerCollection.setOnInfoWindowAdapter(new GoogleMap.InfoWindowAdapter(){
#Override
public View getInfoWindow(Marker marker) {
return null;
}
#Override
public View getInfoContents(Marker marker) {
View v = getLayoutInflater().inflate(R.layout.info_window, null);
TextView tv = (TextView) v.findViewById(R.id.myTv);
tv.setText("hello world, this is my marker");
return v;
}
});
// Add a marker in Sydney and move the camera
LatLng sydney = new LatLng(-34, 151);
MarkerOptions options = new MarkerOptions()
.position(sydney)
.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_RED))
.flat(true)
.title("Marker in Sydney");
Marker marker = mMarkerManagerCollection.addMarker(options);
marker.setTag("test marker");
mMap.moveCamera(CameraUpdateFactory.newLatLng(sydney));
}
}
info_window.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:layout_width="93dp"
android:layout_height="96dp"
android:id="#+id/rect_load"
android:padding="2dp"
android:src="#mipmap/ic_launcher"
/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="#+id/myTv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="myText"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>
Gradle
compile 'com.android.support:appcompat-v7:25.0.0'
compile 'com.google.android.gms:play-services:9.8.0'
compile 'com.google.maps.android:android-maps-utils:0.4'
compile 'com.google.android.gms:play-services:9.8.0'
The problem is that you are creating and populating a new MarkerManager on each onMapReady call. As onMapReady gets called when the orientation changes but you are not removing old markers, the map has two markers on the same position.
When you click the marker after the orientation changes, the old marker is triggered and you see the default behaviour (show title). If you click the marker again you will see your info window.
One possible solution can be calling mMap.clear(); on the onMapReady before adding markers.
Related
I'm trying to enable a location in my mapbox map.
However, the line mapboxMap.setMyLocationEnabled(true); fail and Android Studio gives the 'Cannot resolve method setMyLocationEnabled(boolean).
I'm using MapBox version 6.0.1 (the latest) in my Gradle:
//
compile ('com.mapbox.mapboxsdk:mapbox-android-sdk:6.0.1#aar') { // Map BOX
transitive=true
}
When I switch to version 5.1.3 (which I had used in a previous app I made), Android studio detects the method, but on runtime the app throws the following exception:
android.view.InflateException: Binary XML file line #0: Error inflating class com.mapbox.mapboxsdk.maps.MapView
Would love some help on this one.
Relevant code is below, thanks!
This is the relevant code in the activity:
mapView = (MapView) findViewById(R.id.map);
mapView.onCreate(savedInstanceState);
mapView.getMapAsync(new OnMapReadyCallback() {
#Override
public void onMapReady(MapboxMap mapboxMap) {
// Set map style
mapboxMap.setStyleUrl(Style.MAPBOX_STREETS);
// Set the camera's starting position
CameraPosition cameraPosition = new CameraPosition.Builder()
.target(new LatLng(32.1622,34.8338)) // set the camera's center position on the first coordinate of the tour
.zoom(14) // set the camera's zoom level
.tilt(0) // set the camera's tilt
.build();
// Move the camera to that position
mapboxMap.moveCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));
// Set user location
mapboxMap.setMyLocationEnabled(true);
//Set MyLocation Button
//userLocationFAB(DrivingActivity.get, mapboxMap);
// Add route line
//mapboxMap.addPolyline(getRoute());
// // Add markers
// ArrayList<MarkerOptions> markers = getMarkers();
//
// for (int i=0 ; i<markers.size() ; i++) {
// mapboxMap.addMarker(markers.get(i));
// }
}
});
}
And this is the code from the XML file:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="36dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Score:"
android:layout_centerInParent="true"
android:id="#+id/driving_window_txt"
android:textStyle="bold"
android:layout_margin="10dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="0000000"
android:layout_below="#id/driving_window_txt"
android:layout_centerHorizontal="true"
android:layout_marginTop="20dp"
android:id="#+id/score_counter"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Distance: 0"
android:layout_below="#id/score_counter"
android:layout_centerHorizontal="true"
android:id="#+id/total_distance"/>
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.mapbox.mapboxsdk.maps.MapView
android:layout_height="match_parent"
android:layout_width="match_parent"
android:id="#+id/map"/>
<!-- Implementation of find my location button -->
<android.support.design.widget.FloatingActionButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_marginBottom="20dp"
android:layout_marginRight="20dp"
android:src="#android:drawable/ic_menu_mylocation"
app:borderWidth="0dp"
app:elevation="6dp"
app:pressedTranslationZ="12dp"
app:fabSize="normal"
android:id="#+id/fab_location"/>
</RelativeLayout>
</LinearLayout>
</android.support.constraint.ConstraintLayout>
#Hey mickey check this , how to get current location:
mapFragment.getMapAsync(new OnMapReadyCallback() {
#Override
public void onMapReady(MapboxMap mapboxMap) {
mapboxmap = mapboxMap;
mapboxMap.setOnMapClickListener(DetailDirectionFragment.this);
mapboxMap.getUiSettings().setZoomControlsEnabled(true);
initLocationEngine();
}
});
#SuppressWarnings( {"MissingPermission"})
private void initLocationEngine() {
locationEngine = new LocationEngineProvider(activity).obtainBestLocationEngineAvailable();
locationEngine.setPriority(HIGH_ACCURACY);
locationEngine.setInterval(0);
locationEngine.setFastestInterval(1000);
locationEngine.addLocationEngineListener(this);
locationEngine.activate();
if (locationEngine.getLastLocation() != null) {
Location lastLocation = locationEngine.getLastLocation();
onLocationChanged(lastLocation);
currentLocation = Point.fromLngLat(lastLocation.getLongitude(), lastLocation.getLatitude());
updateLocation(lastLocation.getLongitude(), lastLocation.getLatitude());
}
}
#Override
public void onLocationChanged(Location location) {
currentLocation = Point.fromLngLat(location.getLongitude(), location.getLatitude());
Log.e("currentlocation",location.getLatitude()+"lat"+location.getLongitude()+"lng");
onLocationFound(location);
}
#SuppressWarnings( {"MissingPermission"})
#Override
public void onConnected() {
locationEngine.requestLocationUpdates();
}
private void onLocationFound(Location location) {
if (!locationFound) {
animateCamera(new LatLng(location.getLatitude(), location.getLongitude()));
MarkerViewOptions markerViewOptions = new MarkerViewOptions()
.position(new LatLng(location.getLatitude(), location.getLongitude()));
currentMarker = mapboxmap.addMarker(markerViewOptions);
locationFound = true;
}
}
private void animateCamera(LatLng point) {
mapboxmap.animateCamera(CameraUpdateFactory.newLatLngZoom(point, DEFAULT_CAMERA_ZOOM), CAMERA_ANIMATION_DURATION);
}
check this link.
I'm working on a mapping app. And I want to show some views under the MapView.
I'm using the method:
mapView.setAlpha(0.5f);
Nothing happens to the mapView. When I try to apply this method to my buttons it works fine, even applying it to the parent view of the mapView makes every child view transparent except for the mapView.
How can we make the mapView transparent? I have looked into other questions here and I cant seem to find a solution.
Is there some special method in the mapView.getMap() that we can use to make it transparent?
You can set the transparence for the map using View.setAlpha() (documentation). Here is a working example:
activity_maps.xml:
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="THIS TEXT IS VISIBLE"
android:textSize="24sp"
android:gravity="center"/>
<fragment
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/map"
tools:context=".MapsActivity"
android:name="com.google.android.gms.maps.SupportMapFragment" />
</RelativeLayout>
MapsActivity.java
public class MapsActivity extends FragmentActivity implements OnMapReadyCallback{
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_maps);
((SupportMapFragment) getSupportFragmentManager()
.findFragmentById(R.id.map)).getMapAsync(this);
}
#Override
public void onMapReady(GoogleMap googleMap) {
Marker m = googleMap.addMarker(new MarkerOptions().position(new LatLng(0, 0)).title("Marker"));
m.showInfoWindow();
View v = getSupportFragmentManager().findFragmentById(R.id.map).getView();
v.setAlpha(0.5f); // Change this value to set the desired alpha
}
}
The result looks like this (as you see the text is visible because of the map's transparency):
You can do this through the setAlpha(float) method of your MapView reference, like this:
mMapView = (MapView) findViewById(R.id.map);
mMapView.onCreate(savedInstanceState);
mMapView.getMapAsync(this);
mMapView.setAlpha(0.5f);
If you are using MapFragment, then just use setAlpha(float) for your MapFragment.
Use a Framelayout where you put your 2 Views (CameraView and MapView) over themselfes.
And use the xml attribute alpha to make the MapView transparent.
I want to build a circle with a specified radius on a map just like
RadPad Apple application.
The idea is that the circle stays fixed but the user can move the map.
inside the circle I want to count all the markers that are located inside its radius.
This works from me.
In xml layout
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<fragment
android:id="#+id/map"
android:layout_width="match_parent"
android:layout_height="wrap_content"
class="com.google.android.gms.maps.SupportMapFragment" />
</LinearLayout>
In Main.java
public class Main extends ActionBarActivity{
private GoogleMap gmap;
private LatLng exact_location;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.locations_layout);
exact_location = new LatLng(LAT,LONG);
mapFragment = (SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map);
gmap = mapFragment.getMap();
gmap.moveCamera(CameraUpdateFactory.newLatLngZoom(exact_location,15));
gmap.animateCamera(CameraUpdateFactory.zoomIn());
// Zoom out to zoom level 10, animating with a duration of 2 seconds.
gmap.animateCamera(CameraUpdateFactory.zoomTo(10), null);
CameraPosition cameraPosition = new CameraPosition.Builder()
.target(exact_location) // Sets the center of the map to Mountain View
.zoom(17) // Sets the zoom
.bearing(90) // Sets the orientation of the camera to east
.tilt(85) // Sets the tilt of the camera to 30 degrees
.build(); // Creates a CameraPosition from the builder
gmap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));
CircleOptions circleOptions = new CircleOptions().center(exact_location)
.strokeColor(Color.RED)
.radius(1000); // In meters
gmap.addCircle(circleOptions);
}
How can I change google map my location default button?
I set my location enable and map draw standard image to find location, is it possible to change default image?
See below xml file to custom button:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<fragment
android:id="#+id/maps"
android:name="pl.mg6.android.maps.extensions.SupportMapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:layout_marginTop="5dp" >
<ImageView
android:id="#+id/imgMyLocation"
android:layout_width="40dp"
android:layout_height="40dp"
android:scaleType="fitXY"
android:src="#drawable/track_my_location" />
</LinearLayout>
</RelativeLayout>
Then in java class, declare your location button:
private ImageView imgMyLocation;
imgMyLocation = (ImageView) findViewById(R.id.imgMyLocation);
Click Event:
imgMyLocation.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
getMyLocation();
}
Get Location Method, in that just pass your current latitude and longitude.
private void getMyLocation() {
LatLng latLng = new LatLng(Double.parseDouble(getLatitude()), Double.parseDouble(getLongitude()));
CameraUpdate cameraUpdate = CameraUpdateFactory.newLatLngZoom(latLng, 18);
googleMap.animateCamera(cameraUpdate);
}
});
With a simple trick, you can replace the my location button with a custom one.
Customize the Layout of your new button.
Set my location enabled in maps api.
Hide default button of my location.
call click method of my location on your custom button click.
1. Customize the layout of your new button
add a new button to your desired location in layout file of map activity.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:map="http://schemas.android.com/apk/res-auto"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_height="match_parent"
android:layout_width="match_parent">
<fragment
android:id="#+id/map"
android:name="com.google.android.gms.maps.SupportMapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".activities.MapsActivity" />
<ImageView
android:id="#+id/ic_location"
android:layout_width="18dp"
android:layout_height="18dp"
android:src="#drawable/ic_location"
android:layout_marginRight="8dp"
android:layout_marginBottom="18dp"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true"/>
</RelativeLayout>
2. Set my location enabled in maps api
#Override
public void onMapReady(GoogleMap googleMap) {
mMap = googleMap;
// Enable My Location
mMap.setMyLocationEnabled(true);
/* and DON'T disable the default location button*/
}
3. Hide default button of my location.
//Create field for map button.
private View locationButton;
#Override
public void onMapReady(GoogleMap googleMap) {
mMap = googleMap;
mMap.setMyLocationEnabled(true);
// get your maps fragment
SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map);
// Extract My Location View from maps fragment
locationButton = mapFragment.getView().findViewById(0x2);
// Change the visibility of my location button
if(locationButton != null)
locationButton.setVisibility(View.GONE);
}
4. call click method of my location on your custom button click.
findViewById(R.id.ic_location).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(mMap != null)
{
if(locationButton != null)
locationButton.callOnClick();
}
}
});
If you want to retain the smoothness of the default My Location button but not the look, call the following lines:
//Make sure you have explicit permission/catch SecurityException
mMap.setMyLocationEnabled(true);
mMap.getUiSettings().setMyLocationButtonEnabled(false);
Now you can start & stop location updates while keeping the default behavior
This might be useful to someone who is looking for changing the location icon directly, instead of using any trick or workaround:
locationButton = (mapView.findViewById<View>(Integer.parseInt("1")).parent as View)
.findViewById<ImageView>(Integer.parseInt("2"))
locationButton.setImageResource(R.drawable.ic_location_current)`
Here findViewById<ImageView> is the right thing to be done.
I get the default ImageView of my location using tag instead of idfor preventingExpected resource of type id` lint warning -
ImageView imageView = ((ImageView)mapFragment.getView().findViewWithTag("GoogleMapMyLocationButton"));
Then you can manipulate the imageView then.
imageView.setImageResource(R.drawable.icon_custom_location);
I wish to add a marker to a googleMap with an editable title and snippet. The marker should appear when the user does a long click on the map and then the marker should appear showing a title of something like "click to edit".
The code is all up and running except that I can not figure out how to get the title to appear the moment the marker is created. I can only make it appear with a subsequent click on the marker. I can not see anything within MarkerOptions that allows me to do this. Did I miss something?
The option your looking for is not in MarkerOptions it is a function of the Marker itself. Here's a link to the related docs
marker.showInfoWindow();
To call this method you need to have the marker, or a reference to it. If you're doing this on creation it should be easy. Otherwise just store your makers in some collection - like a HashMap so they are easy to find - and you can display the info window whenever you want.
In case you want to display your marker's title and snippet in your own custom way - you may set it in the onMapReadyCallback.
#Override
public void onMapReady(GoogleMap googleMap) {
mGoogleMap = googleMap;
mGoogleMap.setInfoWindowAdapter(new GoogleMap.InfoWindowAdapter() {
#Override
public View getInfoWindow(Marker marker) {
return null;
}
#Override
public View getInfoContents(Marker marker) {
View view = getLayoutInflater().inflate(R.layout.marker_info, null);
TextView textViewTitle = (TextView) view.findViewById(R.id.marker_title);
textViewTitle.setText(marker.getTitle());
TextView textViewSnippet = (TextView) view.findViewById(R.id.marker_snippet);
textViewSnippet.setText(marker.getSnippet());
return view;
}
});
And here is my layout file for the same.
marker_info.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">
<TextView
android:id="#+id/marker_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textStyle="bold"
android:gravity="center"/>
<TextView
android:id="#+id/marker_snippet"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"/>
</LinearLayout>