I have two fragments in my application. Ons is a map fragment and the other is a list fragment. Both of these are added to a single activity. The idea is that when a user is selected from the list, the app switches to the map fragment and displays the user. The problem is that I am getting a nullPointer exception on the GoogleMap object.
I know that the map works, because on a button press in the map fragment, I can see my current location, and I do not get 'n NullPointer.
This is the onClickListener in my ListFragment:
listEmployees.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
ChildUser childUser = children.get(position);
Tracker.showMapFragment(childUser);
}
});
This is the function that is called from my ListFragment in my parent Activity:
public static void showMapFragment(ChildUser childUser) {
mViewPager.setCurrentItem(0, true);
MapFragment.showSelectedUser(childUser);
trackingEmployee = true;
}
This is the applicable code in my MapFragment:
Initialisation:
mMapFragment = new SupportMapFragment() {
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
Log.d("MAP", "on activity created");
googleMap = mMapFragment.getMap();
googleMap.getUiSettings().setZoomControlsEnabled(false);
}
};
getChildFragmentManager().beginTransaction().add(R.id.map, mMapFragment).commit();
The showSelectedUser function:
public static void showSelectedUser(ChildUser childUser) {
try {
googleMap.clear();
LatLng point = new LatLng(childUser.getChildLatitude(), childUser.getChildLongetude());
CameraUpdate cameraUpdate = CameraUpdateFactory.newLatLngZoom(point, 15);
MarkerOptions marker = new MarkerOptions();
marker.position(point);
googleMap.addMarker(marker);
googleMap.animateCamera(cameraUpdate);
} catch (NullPointerException e) {
e.printStackTrace();
}
}
I get a NullPointerException at googleMap.clear();. Please advise me as to what I can possibly be doing wrong as it works fine when I show my current location from within the MapFragment. Please let me know if you need additional information. Thank you in advance!
UPDATE
When the MapFragment is visible and I press a button, I can view the users current location in the MapFragment with the following code:
public static void showUserLocation() {
googleMap.clear();
if (Tracker.clockedInShift) {
CacheUploads cacheUpload = new CacheUploads(context);
LatLng point = cacheUpload.getMostRecentLocation();
CameraUpdate cameraUpdate = CameraUpdateFactory.newLatLngZoom(point, 15);
MarkerOptions marker = new MarkerOptions();
marker.position(point);
googleMap.addMarker(marker);
googleMap.animateCamera(cameraUpdate);
}
}
The map is linked up correctly because this code gets executed successfully. Why is the googleMap object null when accessed from the ListFragment? Please let me know if you require more information! Thanks
Looks like the answer to this question was fairly obvious. Using an inner class solved the problem. The following code solved everything:
private static GoogleMap googleMap;
public static class MyMapFragment extends SupportMapFragment {
public MyMapFragment() {
super();
}
public static MyMapFragment newInstance() {
MyMapFragment frag = new MyMapFragment();
return frag;
}
#Override
public View onCreateView(LayoutInflater arg0, ViewGroup arg1, Bundle arg2) {
View v = super.onCreateView(arg0, arg1, arg2);
googleMap = getMap();
return v;
}
}
Then just use googleMap to do whatever you want. The above code should be in your Activity/Fragment.
Related
I am working on the google map project for navigation. and creating and displaying many polygons.
But when I try to click near the marker it always detects the marker point. So I would like to know that is there any property there where I can set the marker clickable radius?
I show that is available in the JavaScript but I could not find any lead regarding Android.
Any help or reference much appreciated.
Anyway you can use a workaround:
disable marker clicks response;
detect touch on map determine nearest marker by yourself.
Of course, you need to store all of the markers in that case.
While the first point is simple:
mGoogleMap.setOnMarkerClickListener(new GoogleMap.OnMarkerClickListener() {
#Override
public boolean onMarkerClick(Marker marker) {
return true;
}
});
the second is not: even if "empty" onMarkerClick() return true markers will always intercept this event.
To get rid of that you can use approach with "touchable wrapper" like in this answer within custom MapFragment that can intercept touch events before they goes to MapFragment. When you get touch event you can get screen coordinates of touch. Then you need to find marker with minimal distance from touch location (you also need to convert marke's 'LatLng' position into screen flat coordinates via Projection.toScreenLocation() method). If founded marker within clickable radius you can process custom onMarkerClick event, if not - process polygon click.
Something like that:
Class TouchableWrapper - the core of approach:
public class TouchableWrapper extends FrameLayout {
private static final int CLICK_RADIUS_IN_PIXELS = 25;
private GoogleMap mGoogleMap;
private List<Marker> mMarkers;
public TouchableWrapper(Context context) {
super(context);
}
public void setGoogleMapAndMarkers(GoogleMap googleMap, List<Marker> markers) {
mGoogleMap = googleMap;
mMarkers = markers;
}
#Override
public boolean dispatchTouchEvent(MotionEvent event) {
if (mGoogleMap == null) return super.dispatchTouchEvent(event);
int screenX = (int) event.getX();
int screenY = (int) event.getY();
if ((event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_DOWN) {
// find marker nearest to touch position
Projection projection = mGoogleMap.getProjection();
Marker nearestMarker = null;
int minDistanceInPixels = Integer.MAX_VALUE;
for (Marker marker : mMarkers) {
Point markerScreen = projection.toScreenLocation(marker.getPosition());
int distanceToMarker = (int) Math.sqrt((screenX - markerScreen.x) * (screenX - markerScreen.x)
+ (screenY - markerScreen.y) * (screenY - markerScreen.y));
if (distanceToMarker < minDistanceInPixels) {
minDistanceInPixels = distanceToMarker;
nearestMarker = marker;
}
}
// "drop" nearest marker if it is not within radius
if (minDistanceInPixels > CLICK_RADIUS_IN_PIXELS) {
nearestMarker = null;
}
if (nearestMarker != null) {
// decide what to process (marker click or polygon click) here
Toast.makeText(getContext(),
"Clicked on marker " + nearestMarker.getTitle(), Toast.LENGTH_LONG).show();
}
}
return super.dispatchTouchEvent(event);
}
}
You can adjust clickable radius via CLICK_RADIUS_IN_PIXELS constant value.
Customized MapFragmet that uses TouchableWrapper class:
public class TouchableMapFragment extends MapFragment {
public View originalContentView;
public TouchableWrapper touchView;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {
originalContentView = super.onCreateView(inflater, parent, savedInstanceState);
touchView = new TouchableWrapper(getActivity());
touchView.addView(originalContentView);
return touchView;
}
#Override
public View getView() {
return originalContentView;
}
}
'MainActivity' that uses TouchableMapFragment:
public class MainActivity extends AppCompatActivity implements OnMapReadyCallback {
private static final String TAG = MainActivity.class.getSimpleName();
static final LatLng MARKER_1 = new LatLng(50.450311, 30.523730);
static final LatLng MARKER_2 = new LatLng(50.4502, 30.52365);
private GoogleMap mGoogleMap;
private TouchableMapFragment mMapFragment;
private List<Marker> mMarkers = new ArrayList<>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mMapFragment = (TouchableMapFragment) getFragmentManager()
.findFragmentById(R.id.map_fragment);
mMapFragment.getMapAsync(this);
}
#Override
public void onMapReady(GoogleMap googleMap) {
mGoogleMap = googleMap;
// store markers
Marker marker = mGoogleMap.addMarker(new MarkerOptions()
.position(MARKER_1)
.title("Marker 1"));
mMarkers.add(marker);
marker = mGoogleMap.addMarker(new MarkerOptions()
.position(MARKER_2)
.title("Marker 2"));
mMarkers.add(marker);
// pass stored markers to "touchable wrapper"
mMapFragment.touchView.setGoogleMapAndMarkers(mGoogleMap, mMarkers);
// disable marker click processing
mGoogleMap.setOnMarkerClickListener(new GoogleMap.OnMarkerClickListener() {
#Override
public boolean onMarkerClick(Marker marker) {
return true;
}
});
mGoogleMap.animateCamera(CameraUpdateFactory.newLatLngZoom(MARKER_1, 14));
}
}
and 'MainActivity' layout
<?xml version="1.0" encoding="utf-8"?>
<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"
tools:context=".activities.MainActivity">
<fragment
android:id="#+id/map_fragment"
android:name="<your.package.name>.TouchableMapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
Also, in your case you need to pass List<Polygon> to TouchableWrapper like List<Marker> in example above and process polygon click in its dispatchTouchEvent(MotionEvent event) too.
i want to show the location indicator when the map is loaded and add a marker whenever the map is clicked but none of these seem to work !
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final MapFragment mapFragment = (MapFragment) getSupportFragmentManager().findFragmentById(R.id.mapFragment);
assert mapFragment != null;
mapFragment.getMapAsync(new OnMapInitListener() {
#Override
public void onMapReady(MapView mapView) {
OnlineManager.getInstance().enableOnlineMapStreaming(true);
PositionManager.getInstance().startPositionUpdating();
PositionManager.getInstance().enableRemotePositioningService();
mpView=mapView;
mpView.addMapGestureListener(new MapGestureAdapter() {
#Override
public boolean onMapClicked(final MotionEvent e, final boolean isTwoFingers) {
MapMarker marker = new MapMarker(new GeoCoordinates(PositionManager.getInstance().getLastKnownPosition().getLongitudeAccuracy(),PositionManager.getInstance().getLastKnownPosition().getLatitudeAccuracy()));
mpView.addMapObject(marker);
return true;
}
});
}
#Override
public void onMapError(int error, String info) {}
});
}
You’re trying to create new Marker with getLongitudeAccuracy() and getLatitudeAccuracy(). You need to use geo coordinates!
If you want to add the marker to the position of last known gps signal you can use this code:
MapMarker marker = new MapMarker(PositionManager.getInstance().getLastKnownPosition().getCoordinates())
But as there can be no known position at that time it can result in no marker added. So be sure you have location turned on and strong signal. Based on your example it would make more sense to add marker to the position you clicked on. For that purpose use this code:
mpView.addMapGestureListener(new MapGestureAdapter() {
#Override
public boolean onMapClicked(final MotionEvent e, final boolean isTwoFingers) {
MapMarker marker = new MapMarker(mpView.geoCoordinatesFromPoint(e.getX(), e.getY()));
mpView.addMapObject(marker);
return true;
}
});
What I am trying to accomplish here is be able to show a AlertDialog when I click on a marker that is dynamically added on load (call an API, get positions and show them on map). This is done in the fragments onCreateView.
The map loads here:
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
FragmentManager fm = getChildFragmentManager();
mapFragment = (SupportMapFragment) fm.findFragmentById(R.id.map_container);
if (mapFragment == null) {
mapFragment = SupportMapFragment.newInstance();
fm.beginTransaction().replace(R.id.map_container, mapFragment).commit();
}
}
Later on, I switch from a SupportMapFragment to a GoogleMap:
#Override
public void onResume() {
super.onResume();
if (mMap == null && mapFragment != null) {
mapFragment.getMapAsync(new OnMapReadyCallback() {
#Override
public void onMapReady(GoogleMap googleMap) {
mMap = Tools.configBasicGoogleMap(googleMap);
mMap.setMapType(sharedPref.getMapType());
}
});
}
}
So, logically, my Map should be ready as mMap. Now, I want to show a AlertDialog, so I do implements GoogleMap.OnMarkerClickListener in the class definition for the fragment and implement it here:
#Override
public boolean onMarkerClick(final Marker marker) {
Toast.makeText(getContext(), "Hello world", Toast.LENGTH_SHORT).show();
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
builder.setTitle("Confirmation");
builder.setMessage("Pour confirmer le rapport, appuyez sur + 1.\nSi le rapport est faux, cliquez sur - 1.");
builder.setPositiveButton("+ 1", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id){
URL link = null;
try {
link = new URL(S.base_url + "report/increment/" + marker.getTag().toString());
} catch (MalformedURLException e) {
e.printStackTrace();
}
try {
BufferedReader in = new BufferedReader(
new InputStreamReader(
link.openStream()));
} catch (IOException e) {
e.printStackTrace();
}
}
});
AlertDialog ab = builder.create();
ab.show();
return false;
}
But not even the Toast doesn't show up... When I click on a marker I get the Directions and Open in Google Maps buttons, and the map centers itself on the marker, but my method doesn't start.
To conclude, logically when I click one of these markers that I load, the method onMarkerClick should trigger itself, but this never happens! Any idea why? Thanks.
I don't know why but Google Maps API can't used as implement to fragments. Following code may work it for you. You just need to assign markerClickListener for local, not implementing MarkerClickListener.
//Detect location and set on map
mMapView.getMapAsync(new OnMapReadyCallback() {
#SuppressLint("MissingPermission")
#Override
public void onMapReady(GoogleMap mMap) {
googleMap = mMap;
// Bla
// Bla
// Bla your codes about map
googleMap.setOnMarkerClickListener(new GoogleMap.OnMarkerClickListener() {
#Override
public boolean onMarkerClick(Marker marker) {
// Triggered when user click any marker on the map
return false;
}
});
}
});
How to Set background image of a custom google map marker?
this question is not about the marker mentioned in the above question link
its about the background of a land
As we set background image to google map makers
is there anyway to set a background image to highlight a special Continent using android?
any help or a reference
Instantiate a new GroundOverlayOptions object.
Specify the image as a BitmapDescriptor.
Set the position of the image using one of the available methods:
position(LatLng location, float width, float height)
position(LatLng location, float width)
positionFromBounds(LatLngBounds bounds)
Set any optional properties, such as transparency, as desired.
Call GoogleMap.addGroundOverlay() to add the image to the map.
Refer this and this
You need to draw a polygon by selecting some points on map.
Example code :
public class MainActivity extends FragmentActivity implements
OnMapClickListener,
OnMapLongClickListener,
OnMarkerClickListener {
private GoogleMap myMap;
Location myLocation;
boolean markerClicked;
PolygonOptions polygonOptions;
Polygon polygon;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
FragmentManager manager = getSupportFragmentManager();
SupportMapFragment mapFragment = (SupportMapFragment) manager
.findFragmentById(R.id.map);
myMap = mapFragment.getMap();
myMap.setMyLocationEnabled(true);
myMap.setMapType(GoogleMap.MAP_TYPE_NORMAL);
myMap.setOnMapClickListener(this);
myMap.setOnMapLongClickListener(this);
myMap.setOnMarkerClickListener(this);
markerClicked = false;
}
#Override
public void onMapLongClick(LatLng point)
{
myMap.addMarker(new MarkerOptions()
.position(point).title(point.toString()));
markerClicked = false;
}
#Override
public boolean onMarkerClick(Marker marker)
{
if(markerClicked)
{
if(polygon != null)
{
polygon.remove();
polygon = null;
}
polygonOptions.add(marker.getPosition());
polygonOptions.strokeColor(Color.BLACK);
polygonOptions.strokeWidth(5);
polygonOptions.fillColor(0x884d4d4d);
polygon = myMap.addPolygon(polygonOptions);
marker.remove();
}
else
{
if(polygon != null)
{
polygon.remove();
polygon = null;
}
polygonOptions = new PolygonOptions().add(marker.getPosition());
markerClicked = true;
marker.remove();
}
return true;
}
#Override
public void onMapClick(LatLng point)
{
Toast.makeText(getApplicationContext(),
"Long Press to select locations", Toast.LENGTH_LONG).show();
}
}
using this fragment
fragment
android:id="#+id/map"
android:layout_width="match_parent"
android:layout_height="match_parent"
class="com.google.android.gms.maps.SupportMapFragment" />
Also read the official Documentation here.
Replace codes in getInfoContents with getInfoWindow. The difference between them is getInfoContents wraps your View in ViewGroup with default background.
try this one
I am trying to display a list of venues on Google Maps in Android, which can be clustered on zoom out and on zoom in unclustered.
WHEN UNCLUSTERED, an individual item info window can be opened to look at that venue details, and clicked to open a separate activity.
I am using this https://developers.google.com/maps/documentation/android-api/utility/marker-clustering?hl=en
I am doing this :
Getting Map Fragment in onResume()
#Override
public void onResume() {
super.onResume();
// Getting map for the map fragment
mapFragment = new SupportMapFragment();
mapFragment.getMapAsync(new VenuesInLocationOnMapReadyCallback(getContext()));
// Adding map fragment to the view using fragment transaction
FragmentManager fragmentManager = getChildFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.add(R.id.venues_in_location_support_map_fragment_container, mapFragment);
fragmentTransaction.commit();
}
MapReadyCallback :
private class VenuesInLocationOnMapReadyCallback implements OnMapReadyCallback {
private static final float ZOOM_LEVEL = 10;
private final Context context;
public VenuesInLocationOnMapReadyCallback(Context context) {
this.context = context;
}
#Override
public void onMapReady(final GoogleMap map) {
// Setting up marker clusters
setUpClusterManager(getContext(), map);
// Allowing user to select My Location
map.setMyLocationEnabled(true);
// My location button handler to check the location setting enable
map.setOnMyLocationButtonClickListener(new GoogleMap.OnMyLocationButtonClickListener() {
#Override
public boolean onMyLocationButtonClick() {
promptForLocationSetting(getContext(), map);
// Returning false ensures camera try to move to user location
return false;
}
});
map.getUiSettings().setMyLocationButtonEnabled(true);
// Disabling map toolbar
map.getUiSettings().setMapToolbarEnabled(false);
}
}
Setting up Cluster Manager
private void setUpClusterManager(final Context context, GoogleMap map) {
// Declare a variable for the cluster manager.
ClusterManager<LocationMarker> mClusterManager;
// Position the map.
LatLng wocLatLng = new LatLng(28.467948, 77.080685);
map.moveCamera(CameraUpdateFactory.newLatLngZoom(wocLatLng, VenuesInLocationOnMapReadyCallback.ZOOM_LEVEL));
// Initialize the manager with the context and the map.
mClusterManager = new ClusterManager<LocationMarker>(context, map);
// Point the map's listeners at the listeners implemented by the cluster
// manager.
map.setOnCameraChangeListener(mClusterManager);
map.setOnMarkerClickListener(mClusterManager);
// Add cluster items (markers) to the cluster manager.
addLocations(mClusterManager);
// Setting custom cluster marker manager for info window adapter
map.setInfoWindowAdapter(mClusterManager.getMarkerManager());
mClusterManager.getMarkerCollection().setOnInfoWindowAdapter(new MyLocationInfoWindowAdapter());
map.setOnInfoWindowClickListener(new MyMarkerInfoWindowClickListener());
}
Adding Cluster items (markers)
private void addLocations(ClusterManager<LocationMarker> mClusterManager) {
for (int i = 0; i < venuesDetailsJsonArray.length(); i++) {
try {
JSONObject thisVenueJson = (JSONObject) venuesDetailsJsonArray.get(i);
JSONObject thisVenueLocationJson = thisVenueJson.getJSONObject("location");
LocationMarker thisVenueMarker = new LocationMarker(thisVenueLocationJson.getDouble("latitude"),
thisVenueLocationJson.getDouble("longitude"), thisVenueJson.getInt("id"));
mClusterManager.addItem(thisVenueMarker);
} catch (JSONException e) {
e.printStackTrace();
}
}
}
MyLocationInfoWIndowAdapter
private class MyLocationInfoWindowAdapter implements GoogleMap.InfoWindowAdapter {
#Override
public View getInfoWindow(Marker marker) {
return null;
}
#Override
public View getInfoContents(Marker marker) {
Log.e("getInfoContent", marker.toString());
View venueInfoWindow = ((LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE))
.inflate(R.layout.venues_map_item, null);
return venueInfoWindow;
}
}
MarkerInfoWindowClickListener
private class MyMarkerInfoWindowClickListener implements GoogleMap.OnInfoWindowClickListener {
#Override
public void onInfoWindowClick(Marker marker) {
// TODO: This is the click listener, that means all the info must be added as Tag to Marker
Intent venueDetailsDisplayIntent = new Intent(getActivity(), VenueDetailsDisplayActivity.class);
startActivity(venueDetailsDisplayIntent);
}
}
Location Marker class
public class LocationMarker implements ClusterItem{
private final LatLng mPosition;
private final int id;
public LocationMarker(double lat, double lng, int id) {
mPosition = new LatLng(lat, lng);
this.id = id;
}
#Override
public LatLng getPosition() {
return mPosition;
}
public int getId() {
return this.id;
}
}
The way that I am understanding the flow is this :
onResume --> fragmentTransaction --> VenuesInLocationOnMapReadyCallback --> setUpClusterManager --> addLocations (This adds Custom markers)
Marker Click --> MyLocationInfoWindowAdapter --> getInfoContents(Marker marker)
Marker Info Window click --> MyMarkerInfoWindowClickListener
According to my Understanding of process (I could be wrong):
I am adding an id to my custom LocationMarker when Adding markers in addLocations function.
I need to display different info in infoWindow for different markers.
InfoWindow is displayed using MyLocationInfoWindowAdapter-->getInfoContents(Marker marker)
But here is the rub, I can't find a way to figure out which marker has been clicked upon so that I can set appropriate info in InfoWindow.
On Click on opened InfoWindow I need to open a separate Activity. A/C to me InfoWindow click is handled using MyMarkerInfoWindowClickListener-->onInfoWindowClick(Marker marker) Here too I am having the same problem (I can't figure out which marker's info window has been clicked).