Referring to the image below. I want to update the map when the user clicks the View Map button. However, since this is part of an expandable list view, it is in it's own class, while the map itself is in another fragment activity.
Is there any way to listen to the button click and update the map accordingly from another activity?
Thanks!
Here's what i have so far. I tried getting the parameter via
ExpandableListElement.polyLinePass
but it does not give me anything back so i can't update the map. Also another question, does the map update itself when values are changed like the polyLine, or do I need to setup the map again?
Map Display Activity:
public class OfflineViewer extends FragmentActivity {
private void setUpMap() {
List<LatLng> listTemp = util.decodePoly(ExpandableListElement.polyLinePass);
for(int l=0;l<listTemp.size() - 1;l++){
list.add(listTemp.get(l));
if(l==0) {
}
}
int listSize = list.size();
//get polyline from expandable list elements
for (int i = 0; i < list.size() - 1; i++) {
LatLng src = list.get(i);
LatLng dest = list.get(i + 1);
Polyline line = mMap.addPolyline(new PolylineOptions() //mMap is the Map Object
.add(new LatLng(src.latitude, src.longitude),
new LatLng(dest.latitude,dest.longitude))
.width(5).color(Color.BLUE).geodesic(true));
}
...
}
ExpandableListElement Activity:
public class ExpandableListElement extends RelativeLayout {
public ExpandableListElement(Context context, String routeName, String mode, String dist, String routeId, String start, String end, final String polyLine) {
super(context);
mContext = context;
setTextViewElements(routeName, mode, dist, routeId, start, end, polyLine);
Button randButton = new Button(mContext);
randButton.setText("View Map");
randButton.setId(mIdPool);
randButton.setOnClickListener(new Button.OnClickListener() {
public void onClick(View v) {
polyLinePass = polyLine;
}
});
...
}
You can handle that from your activity class. Lets say the map is in one fragment and the button in another on. They are both "hosted" by the same activity.
let your activity have a method like this and be sure it is public:
class MyActivity extends Activity {
private Fragment fragment1; //the frag with the button
private Fragment fragment2; //the frag with the map
public void updateMap(String argument) {
if(fragment2 != null)
fragment2.update(argument);
}
}
then from the fragment with the button (fragment1), call ((MyActivity) getActivity()).updateMap(argument) to update your Map:
class Fragment1 extends Fragment implements OnClickListener{
// this is called on button click
void onClick(...) {
((MyActivity) getActivity()).updateMap(argument)
}
}
Your second fragment needs a public method for upting the map:
class Fragment1 extends Fragment implements OnClickListener{
public void updateMap(String arguments) {
// update map...
}
}
This strategy can also be used in lists
Related
I have an android application where a MapsActivity is included to display a number of markers using GoogleMaps.
The markers are created through Timestamp objects
Timestamp object attributes
(double lat,lon;
int stepSum;
long timeMilli;
String state=null;)
stored in Firebase Database.
So I retrieve each Timestamp from the database and try to create a marker with those attibutes above. My problem is that when I click a marker the custom info window is being displayed but its the same for all markers. It should show different attributes for different markers.
Why this is happening
In the drawMarkers() method I instantiate a separate infowindow when I am creating a new marker and set that info window to the
GoogleMap object with mMap.setInfoWindowAdapter(infoWindow);.
As I result mMap.setInfoWindowAdapter(infoWindow);is called as many times as markers are created and finally only the last infowindow survives. That is the problem but I can't figure out a solution.
How to implement an infowindow that when I click a marker it presents some kind of data and when I am clicking another marker it presents different kind of data (same layout, different attributes).
A valid example also would do.
CustomInfoWindow class
private class CustomInfoWindow implements GoogleMap.InfoWindowAdapter{
private String title=null,hour=null,state=null;
private int steps=0;
public CustomInfoWindow(){}
public CustomInfoWindow(String title, String hour, String state, int steps) {
this.title = title;
this.hour = hour;
this.state = state;
this.steps = steps;
}
#Override
public View getInfoWindow(Marker marker) {
return null;
}
public void setState(String state) {
this.state = state;
}
#Override
public View getInfoContents(Marker marker) {
LayoutInflater inflater = LayoutInflater.from(getApplicationContext());
View root = inflater.inflate(R.layout.marker_infowindow,null);
TextView titleTextView = (TextView) root.findViewById(R.id.infowindow_title);
TextView hourTextView = (TextView) root.findViewById(R.id.infowindow_hour);
TextView stepsTextView = (TextView) root.findViewById(R.id.infowindow_steps);
TextView stateTextView = (TextView) root.findViewById(R.id.infowindow_state);
titleTextView.setText(title);
if (state!=null){
stateTextView.setText(getApplicationContext().getString(R.string.state)+" "+state);
stateTextView.setVisibility(View.VISIBLE);
}
hourTextView.setText(getApplicationContext().getString(R.string.time)+" "+hour);
stepsTextView.setText(getApplicationContext().getString(R.string.steps_so__far)+" "+steps);
return root;
}
public void setTitle(String title) {
this.title = title;
}
public void setHour(String hour) {
this.hour = hour;
}
public void setSteps(int steps) {
this.steps = steps;
}
}
How markers are drawn
private void drawMarkers(Calendar c){
String userId = this.user.getAccount().getId();
int year = c.get(Calendar.YEAR);
int month = c.get(Calendar.MONTH);
int dayOfMonth = c.get(Calendar.DAY_OF_MONTH);
final DatabaseReference timestampRef = FirebaseDatabase.getInstance().getReference()
.child(FirebaseConstant.TIMESTAMPS.toString());
timestampRef.child(userId).child(""+year).child(""+month).child(""+dayOfMonth)
.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
long childCounter=0;
for (DataSnapshot timestampSnapshot:dataSnapshot.getChildren()){
CustomInfoWindow infoWindow=new CustomInfoWindow();
Timestamp temp = timestampSnapshot.getValue(Timestamp.class);
if (temp!=null){
infoWindow.setTitle(getString(R.string.todays_timestamp));
infoWindow.setHour(getFormatedHourFromTimeMilli(temp.getTimeMilli()));
infoWindow.setSteps(temp.getStepSum());
if (temp.getState()!=null){
infoWindow.setState(temp.getState());
}
mMap.setInfoWindowAdapter(infoWindow);
drawTimestamp(temp);
infoWindow=null;
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {}
});
}
I have read google's tutorial about info windows but couldn't solve my problem.
Hope this example will help you just use it as simple xml layout.
Remember button will not work just textview will complete your requirement on the spot if you want get buttons in workable condition follow chose007 example.
My solution
Create a class that extends GoogleMap.InfoWindowAdapter. That will be your custom infowindow for the whole map.
Inside onMapReady() set an implementation of your customInfoWindow object to the GoogleMap object. That will be the one and only infowindow that it will be displayed when a user is clicking a marker from the map.
Finally, inside onMapReady() method set an OnMarkerClickListener to the GoogleMap object as well. Your implementation of the GoogleMap.OnMarkerClickListener.onMarkerClick(Marker marker) will only change the content (change the attributes of the infowindow object, call it however you want) of the infowindow, depending on which marker is clicked.
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).
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.
Currently doing a simple app that contain google map. and i would like to link to next activity when the user click on a designed coordinate but it is not working and there is no error, please help me
double latitude = 1.34503109;
double longitude = 103.94008398;
LatLng latLng = new LatLng(latitude, longitude);
gMap.setOnMapClickListener(new GoogleMap.OnMapClickListener() {
#Override
public void onMapClick(LatLng latLng) {
Intent i = new Intent(Activity_Selecting_Bus.this,
Activity_Bus_Selected.class);
startActivity(i);
}
});
If I understand correctly you have markers set up in your map and when user clicks on a marker you start another activity. The following code should work (in SupportMapFragment):
getMap().setOnMarkerClickListener(new GoogleMap.OnMarkerClickListener() {
#Override
public boolean onMarkerClick(Marker marker) {
// do the thing!
return true;
}
});
If you don't have markers and want to listen for a certain location click use this instead:
getMap().setOnMapClickListener(new GoogleMap.OnMapClickListener() {
#Override
public void onMapClick(LatLng latLng) {
// do the thing!
}
});
In this case you probably want to start the activity when the user clicks "close enough" to a certain location. I use this library http://googlemaps.github.io/android-maps-utils/ which contains method
SphericalUtil.computeDistanceBetween(LatLng from, LatLng to)
which returns distance between two LatLngs in meters.
EDIT Example:
First you define where the user has to click and which activity does that particular click launch:
private static final HashMap<LatLng, Class<? extends Activity>> sTargets = new HashMap();
static {
sTargets.put(new LatLng(34.0204989,-118.4117325), LosAngelesActivity.class);
sTargets.put(new LatLng(42.755942,-75.8092041), NewYorkActivity.class);
sTargets.put(new LatLng(42.352711,-83.099205), DetroitActivity.class);
}
private static final int RANGE_METERS = 200 * 1000; // 200 km range
Then when the user clicks on the map, you compute distance to each point. If it fits, you launch the activity.
getMap().setOnMapClickListener(new GoogleMap.OnMapClickListener() {
#Override
public void onMapClick(LatLng input) {
for(Map.Entry<LatLng,<? extends Activity>> entry : sTargets.entrySet()) {
LatLng ll = entry.getKey();
boolean inRange = SphericalUtil.computeDistanceBetween(input, ll) < RANGE_METERS;
if (inRange) {
Class<? extends Activity> cls = entry.getValue();
Intent i = new Intent(getActivity(), cls);
getActivity().startActivity(i);
break; // stop calculating after first result
}
}
}
});
I have created a class of type BaseAdapter that is populated with buttons - when you click on a button I want to load a new intent. This has proven difficult on two levels:
You cannot associate the event with the button (one that creates a new intent) inside the Adapter. This is why I send the Buttons as an array to my Adapter (this solution works, but it is messy)
Even though my buttons are created inside the same Activity - they cannot create a new intent from that Activity. The exeption is so great that I have not even gotten a try...catch statement to work.
I have tried reflection, creating the buttons inside the activity and passing them through, passing the context (to call context.startIntent(...))
My question: can someone show me how to create a ButtonAdapter where each button creates a new Intent - even of the same type as the original Activity?
UPDATE: Here is the code because I am getting answers from people who think I am struggling with onClickListeners:
public class ButtonAdapter extends BaseAdapter
{
private Context _context;
private Button[] _button;
public ButtonAdapter(Context c, Button[] buttons)
{
_context = c;
_button = buttons;
}
// Total number of things contained within the adapter
public int getCount()
{
return _button.length;
}
// Require for structure, not really used in my code.
public Object getItem(int position)
{
return _button[position];
}
// Require for structure, not really used in my code. Can
// be used to get the id of an item in the adapter for
// manual control.
public long getItemId(int position)
{
return position;
}
public View getView(int position, View convertView, ViewGroup parent)
{
_button[position].setId(position);
return _button[position];
}
}
---------------
The Activity:
public class MainActivity extends Activity
{
private GridView _gv;
private TextView _heading;
private ButtonAdapter _adapter;
public void LoadActivity(String heading)
{
try
{
Itent intent = new Intent(getApplicationContext(), MainActivity.class);
intent.putExtra("Level", "NextPage");
intent.putExtra("Heading", heading);
startActivity(intent);
}
catch (Exception ex)
{
Toast.makeText(getApplicationContext(), String.format("Error LoadActivity: %s", ex.getMessage()), Toast.LENGTH_SHORT).show();
}
}
private void createButtonsAdapter(Button _button[])
{
_buttonadapter = new ButtonAdapter(getApplicationContext(), _button);
_gv.setAdapter(_adapter);
}
private void setupButtons()
{
Button[] _buttons = new Button[2];
String names[] = new String[]{"Button1","Button2"};
for (int i = 0; i < 2; i++)
{
_buttons[i] = new Button(this);
_buttons[i].setText(names[i]);
_buttons[i].setTag(names[i]);
_buttons[i].setOnClickListener(new View.OnClickListener()
{
public void onClick(View arg0)
{
try
{
LoadActivity(((Button)arg0).getTag().toString());
}
catch(Exception ex)
{
Toast.makeText(getApplicationContext(), String.format("Error button.onClick: %s", ex.getMessage()), Toast.LENGTH_SHORT).show();
}
}
});
}
createButtonsAdapter(_buttons);
}
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
_gv = (GridView)findViewById(R.id.gridview);
_heading = (TextView)findViewById(R.id.tv_heading);
Bundle params = getIntent().getExtras();
if (params == null)
{
setupButtons();
}
else if (params.containsKey("Level"))
{
_heading.setText(params.getString("Heading"));
if (params.getString("Level").equals("NextPage"))
{
//code not here yet
}
else if (params.getString("Level").equals("Chapters"))
{
//future code
}
}
}
}
Excuse the bold and caps but I have had enough silly answers to warrent this:
I HAVE TRIED PUTTING THE ONCLICKLISTENER INSIDE THE GRIDVIEW AND IT DOES NOT WORK EITHER
You cannout load an activity from a class outside that activity, even if you pass the context as a parameter. That is why I have resorted to this method, which completely bombs android, even though I have try catch statements.
Please try give me a solution in the form of a correction to my code, other code, or a tutorial that achieves what I want here. I know how to do a button adapter properly, it is the act of loading an Intent that has forced me to implement it this way.
I suggest the following,
Using a common onClick listner for all the buttons in your grid view
Set tag for all the butons in the getView func. of adapter.
Use the tag Object to decide on the intent to fire from the onClick listener.
I hope it helps..
I guess you can easily manipulate your buttons created in your class extending Base adapter. In the getView method .... if you have button b.. then do it as follows
b.setOnClickListener(new OnClickListener()
{
public void onClick()
{
// Do your stuff here ...
}
});
and if you want to start another activity on Click of this button then you need to pass the calling context to this adapter.
Once again I am answering my own question:
http://bottlecapnet.blogspot.com/2012/01/android-buttons-have-no-place-inside.html
I will just have to style TextViews as I want to see them.