I have an ArrayList of type GeoPoint.
private List<GeoPoint> points = new ArrayList<GeoPoint>();
I want to pass points to another Activity and retrive the data in that activity. How do I do it? I know I have to use the parcelable but I searched, but could not find a way to pass ArrayLists.
This function will help you: http://developer.android.com/reference/android/content/Intent.html#putParcelableArrayListExtra(java.lang.String, java.util.ArrayList<? extends android.os.Parcelable>)
But the problem is, that GeoPoint is not Parcelable. Well, you can do a workaround here:
1) Create a class, that implements Parcelable:
public class ParcelableGeoPoint implements Parcelable {
private GeoPoint geoPoint;
public ParcelableGeoPoint(GeoPoint point) {
geoPoint = point;
}
public GeoPoint getGeoPoint() {
return geoPoint;
}
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel out, int flags) {
out.writeInt(geoPoint.getLatitudeE6());
out.writeInt(geoPoint.getLongitudeE6());
}
public static final Parcelable.Creator<ParcelableGeoPoint> CREATOR
= new Parcelable.Creator<ParcelableGeoPoint>() {
public ParcelableGeoPoint createFromParcel(Parcel in) {
return new ParcelableGeoPoint(in);
}
public ParcelableGeoPoint[] newArray(int size) {
return new ParcelableGeoPoint[size];
}
};
private ParcelableGeoPoint(Parcel in) {
int lat = in.readInt();
int lon = in.readInt();
geoPoint = new GeoPoint(lat, lon);
}
}
2) when sending to the other activity (points is your List<GeoPoint>:
ArrayList<ParcelableGeoPoint> pointsExtra = new ArrayList<ParcelableGeoPoint>();
foreach(GeoPoint point: points) {
pointsExtra.add(new ParcelableGeoPoint(point));
}
intent.putExtra("geopoints", pointsExtra);
3) in the called activity:
ArrayList<ParcelableGeoPoint> pointsExtra = getIntent().getParcelableArrayListExtra("geopoints");
ArrayList<GeoPoint> points = new ArrayList<GeoPoint>();
foreach(ParcelableGeoPoint point: pointsExtra) {
points.add(point.getGeoPoint());
}
code should work, but is untested.
Related
I tried to save this object using Realm But I got this error
Error:(24, 9) error: Type
java.util.ArrayList of field
savedPath is not supported
Here is my code :
public class TrackingInfo extends RealmObject {
private int order_id;
private double savedDistance;
private double savedDuration;
private ArrayList <LatLng>savedPath;
public int getOrder_id() {
return order_id;
}
public void setOrder_id(int order_id) {
this.order_id = order_id;
}
public double getSavedDistance() {
return savedDistance;
}
public void setSavedDistance(double savedDistance) {
this.savedDistance = savedDistance;
}
public double getSavedDuration() {
return savedDuration;
}
public void setSavedDuration(double savedDuration) {
this.savedDuration = savedDuration;
}
public ArrayList<LatLng> getSavedPath() {
return savedPath;
}
public void setSavedPath(ArrayList<LatLng> savedPath) {
this.savedPath = savedPath;
}
public TrackingInfo(){}}
Thanx in advance
Both List and LatLng cannot be stored in Realm directly. You will need to create a model object for LatLng and then use a RealmList object containing your model objects.
public class Location extends RealmObject {
public Location() { }
double latitude;
double longitude;
}
RealmList<Location> savedPath = new RealmList<Location>();
//Add location objects to savedPath and store it in your TrackingInfo object
You will need to manually convert objects of the LatLng class to the Location class when you are inserting/retrieving from the database.
I have a Linked List in one activity (A) that I want to share with another Activity (B).
The list contains a username of type string and contains coordinates of type LatLng. I am also using Intent and bundle to share data between activities. I tried using Parcelable but unable to figure out how to use it. Here is the code I have:
data.java
public class data implements Parcelable{
private LatLng coordinates;
private String name;
public data() {
name = null;
coordinates = null;
}
public data(String name, LatLng coordinates)
{
this.name = name;
this.coordinates = coordinates;
}
public data(Parcel in) {
coordinates = in.readParcelable(LatLng.class.getClassLoader());
name = in.readString();
}
public static final Creator<data> CREATOR = new Creator<data>() {
#Override
public data createFromParcel(Parcel in) {
return new data(in);
}
#Override
public data[] newArray(int size) {
return new data[size];
}
};
public LatLng getLatLng () {
return coordinates;
}
#Override
public int describeContents() {
return hashCode();
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeParcelable(coordinates, flags);
}
}
Activity A
public class A extends FragmentActivity implements
OnMapReadyCallback,
GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener,
GoogleMap.OnMyLocationButtonClickListener,
ActivityCompat.OnRequestPermissionsResultCallback {
Button switchToSeek;
double mLatitude;
double mLongitude;
LinkedList<data> storedData = new LinkedList<>();
protected void onCreate(Bundle savedInstanceState) {
...
switchToSeek.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
getCurrentLocation();
Intent intent = new Intent(A.this, B.class);
Bundle xy = new Bundle();
xy.putDouble("x", mLatitude);
xy.putDouble("y", mLongitude);
xy.putParcelable("list", storedData); <---------- error: wrong second arugment
intent.putExtra("xy", xy);
A.this.startActivity(intent);
}
});
Activity B
public class B extends FragmentActivity implements OnMapReadyCallback {
double mLatitude;
double mLongitude;
LatLng current;
GoogleMap gMap;
LinkedList <data> copyData = new LinkedList<>();
#Override
public void onMapReady(GoogleMap googleMap) {
gMap = googleMap;
...
Intent intent = getIntent();
Bundle xy = intent.getBundleExtra("xy");
if (xy != null) {
mLatitude = xy.getDouble("x");
mLongitude = xy.getDouble("y");
}
/***** Call linked list here and set equal to copyData *****/
current = new LatLng(mLatitude, mLongitude);
gMap.moveCamera(CameraUpdateFactory.newLatLngZoom(current, 18.0f));
}
There is no easy way to do that, since LinkedList does not implement serializable or parcelable.
You CAN implement your own linked list class and make it a serializable/parcelable object which can then be passed.
Or you can convert its content into another data type such as an array and then recreate the linkedlist.* THIS IS HIGHLY INEFFICIENT
I believe there are other ways but this is a standard problem in android dev. Maybe try using fragments if possible and passing the linkedlist through a setter()
If the list is not huge, you can do it using the following helper class:
public class ParcelableLinkedList<E extends Parcelable> implements Parcelable {
private final LinkedList<E> linkedList;
public final Creator<ParcelableLinkedList> CREATOR = new Creator<ParcelableLinkedList>() {
#Override
public ParcelableLinkedList createFromParcel(Parcel in) {
return new ParcelableLinkedList(in);
}
#Override
public ParcelableLinkedList[] newArray(int size) {
return new ParcelableLinkedList[size];
}
};
public ParcelableLinkedList(Parcel in) {
// Read size of list
int size = in.readInt();
// Read the list
linkedList = new LinkedList<E>();
for (int i = 0; i < size; i++) {
linkedList.add((E)in.readParcelable(ParcelableLinkedList.class.getClassLoader()));
}
}
public ParcelableLinkedList(LinkedList<E> linkedList) {
this.linkedList = linkedList;
}
LinkedList<E> getLinkedList() {
return linkedList;
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel parcel, int flags) {
// Write size of the list
parcel.writeInt(linkedList.size());
// Write the list
for (E entry : linkedList) {
parcel.writeParcelable(entry, flags);
}
}
}
In your onClick() method, add the data to the Bundle like this:
xy.putParcelable("list", new ParcelableLinkedList<data>(storedData));
To extract the data from the Bundle, do this:
copyData = ((ParcelableLinkedList<data>)xy.getParcelable("list")).getLinkedList();
I haven't actually compiled and tested this code, but it should work.
If the list is really huge, you are better off storing it in a static member variable in one class and then just referencing it from the other. This isn't normally the way you want to do things in Android, but it is sometimes more expedient to do this than to serialize and deserialize a huge amount of data just to pass it between 2 activities that have access to the same memory space.
I have been searching for a way to pass an object from one Activity to another.
Different tutorials stated that the best way to do it is to make the class Parcelable. I've managed to implement it, but I have one question left.
There is a reference to another parcelable object (location) inside the Office class. This tutorial tells to serialize it using dest.writeParcelable(location, flags); and in.readParcelable(LatLng.class.getClassLoader());, but the parcelabler created the code with dest.writeValue(location); and then (LatLng) in.readValue(LatLng.class.getClassLoader());.
I have checked and it worked both ways.
Could somebody please explain what is the difference between these two approaches? Is any of them better for some reasons? Thank you!
public class Office implements Parcelable {
#SuppressWarnings("unused")
public static final Parcelable.Creator<Office> CREATOR = new Parcelable.Creator<Office>() {
#Override
public Office createFromParcel(Parcel in) {
return new Office(in);
}
#Override
public Office[] newArray(int size) {
return new Office[size];
}
};
public final String name;
public final String address;
public final LatLng location;
public Office(String name, String address, LatLng location) {
this.name = name;
this.address = address;
this.location = location;
}
protected Office(Parcel in) {
name = in.readString();
address = in.readString();
// location = (LatLng) in.readValue(LatLng.class.getClassLoader());
location = in.readParcelable(LatLng.class.getClassLoader());
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeString(address);
// dest.writeValue(location);
dest.writeParcelable(location, flags);
}
}
writeValue is more generic, and since it takes an Object as parameter, internally they check the instanceOf the object to call the specific method. If you know the type, I would stick with using the specific one
I am using Google Maps Marker Clustering Utility to cluster the markers. It unclusters on double tapping. Is it possible to do it manually on single click.
Bit late to the party
In your onClusterClick function:
LatLngBounds.Builder builder = LatLngBounds.builder();
for (ClusterItem item : cluster.getItems()) {
builder.include(item.getPosition());
}
final LatLngBounds bounds = builder.build();
getMap().animateCamera(CameraUpdateFactory.newLatLngBounds(bounds, 100));
mClusterManager
.setOnClusterClickListener(new OnClusterClickListener<MyItem>() {
#Override
public boolean onClusterClick(final Cluster<MyItem> cluster) {
map.animateCamera(CameraUpdateFactory.newLatLngZoom(
cluster.getPosition(), (float) Math.floor(map
.getCameraPosition().zoom + 1)), 300,
null);
return true;
}
});
you can do that on the click of the marker too ,
but before that you need to do map.setOnMarkerClickListener(mClusterManager);
so that cluster manager gets the click events and
you can do
mClusterManagersetOnClusterItemClickListener(new OnClusterItemClickListener<MyItem>() {
}
For those who want to expand the cluster and KEEP the zoom level, try the following steps. It seems complicate, but really is not.
Steps
Suppose you have a model called Location that implements ClusterItem, add following code:
private boolean shouldCluster = true;
public boolean isShouldCluster() {
return shouldCluster;
}
public void setShouldCluster(boolean shouldCluster) {
this.shouldCluster = shouldCluster;
}
The whole model looks similar to this:
public class Location implements ClusterItem
{
private double latitude;
private double longitude;
private boolean shouldCluster = true;
#Override
public LatLng getPosition() {
return new LatLng(latitude, longitude);
}
#Override
public String getTitle() {
return null;
}
#Override
public String getSnippet() {
return null;
}
public boolean isShouldCluster() {
return shouldCluster;
}
public void setShouldCluster(boolean shouldCluster) {
this.shouldCluster = shouldCluster;
}
}
Add the algorithm below to your project, and replace Location with your own model name. Note: this algorithm is simply a copy of Google's NonHierarchicalDistanceBasedAlgorithm with some modifications.
public class DistanceBasedAlgorithm implements Algorithm<Location> {
public static final int MAX_DISTANCE_AT_ZOOM = 100; // essentially 100 dp.
/**
* Any modifications should be synchronized on mQuadTree.
*/
private final Collection<QuadItem> mItems = new ArrayList<QuadItem>();
/**
* Any modifications should be synchronized on mQuadTree.
*/
private final PointQuadTree<QuadItem> mQuadTree = new PointQuadTree<QuadItem>(0, 1, 0, 1);
private static final SphericalMercatorProjection PROJECTION = new SphericalMercatorProjection(1);
#Override
public void addItem(Location item) {
if (item == null) return;
final QuadItem quadItem = new QuadItem(item);
synchronized (mQuadTree) {
mItems.add(quadItem);
mQuadTree.add(quadItem);
}
}
#Override
public void addItems(Collection<Location> items) {
for (Location item : items) {
addItem(item);
}
}
#Override
public void clearItems() {
synchronized (mQuadTree) {
mItems.clear();
mQuadTree.clear();
}
}
#Override
public void removeItem(Location item) {
// QuadItem delegates hashcode() and equals() to its item so,
// removing any QuadItem to that item will remove the item
final QuadItem quadItem = new QuadItem(item);
synchronized (mQuadTree) {
mItems.remove(quadItem);
mQuadTree.remove(quadItem);
}
}
#Override
public Set<? extends Cluster<Location>> getClusters(double zoom) {
final int discreteZoom = (int) zoom;
final double zoomSpecificSpan = MAX_DISTANCE_AT_ZOOM / Math.pow(2, discreteZoom) / 256;
final Set<QuadItem> visitedCandidates = new HashSet<QuadItem>();
final Set<Cluster<Location>> results = new HashSet<Cluster<Location>>();
final Map<QuadItem, Double> distanceToCluster = new HashMap<QuadItem, Double>();
final Map<QuadItem, StaticCluster<Location>> itemToCluster = new HashMap<QuadItem, StaticCluster<Location>>();
synchronized (mQuadTree) {
for (QuadItem candidate : mItems) {
if (visitedCandidates.contains(candidate)) {
// Candidate is already part of another cluster.
continue;
}
Collection<QuadItem> clusterItems;
if (candidate.mClusterItem.isShouldCluster()) {
Bounds searchBounds = createBoundsFromSpan(candidate.getPoint(), zoomSpecificSpan);
clusterItems = mQuadTree.search(searchBounds);
}
else {
List<QuadItem> temp = new ArrayList<>();
temp.add(candidate);
clusterItems = temp;
}
if (clusterItems.size() == 1) {
// Only the current marker is in range. Just add the single item to the results.
results.add(candidate);
visitedCandidates.add(candidate);
distanceToCluster.put(candidate, 0d);
continue;
}
StaticCluster<Location> cluster = new StaticCluster<Location>(candidate.mClusterItem.getPosition());
results.add(cluster);
for (QuadItem clusterItem : clusterItems) {
Double existingDistance = distanceToCluster.get(clusterItem);
double distance = distanceSquared(clusterItem.getPoint(), candidate.getPoint());
if (existingDistance != null) {
// Item already belongs to another cluster. Check if it's closer to this cluster.
if (existingDistance < distance) {
continue;
}
// Move item to the closer cluster.
itemToCluster.get(clusterItem).remove(clusterItem.mClusterItem);
}
distanceToCluster.put(clusterItem, distance);
cluster.add(clusterItem.mClusterItem);
itemToCluster.put(clusterItem, cluster);
}
visitedCandidates.addAll(clusterItems);
}
}
return results;
}
#Override
public Collection<Location> getItems() {
final List<Location> items = new ArrayList<Location>();
synchronized (mQuadTree) {
for (QuadItem quadItem : mItems) {
items.add(quadItem.mClusterItem);
}
}
return items;
}
private double distanceSquared(Point a, Point b) {
return (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y);
}
private Bounds createBoundsFromSpan(Point p, double span) {
// TODO: Use a span that takes into account the visual size of the marker, not just its
// LatLng.
double halfSpan = span / 2;
return new Bounds(
p.x - halfSpan, p.x + halfSpan,
p.y - halfSpan, p.y + halfSpan);
}
private static class QuadItem implements PointQuadTree.Item, Cluster<Location> {
private final Location mClusterItem;
private final Point mPoint;
private final LatLng mPosition;
private Set<Location> singletonSet;
private QuadItem(Location item) {
mClusterItem = item;
mPosition = item.getPosition();
mPoint = PROJECTION.toPoint(mPosition);
singletonSet = Collections.singleton(mClusterItem);
}
#Override
public Point getPoint() {
return mPoint;
}
#Override
public LatLng getPosition() {
return mPosition;
}
#Override
public Set<Location> getItems() {
return singletonSet;
}
#Override
public int getSize() {
return 1;
}
#Override
public int hashCode() {
return mClusterItem.hashCode();
}
#Override
public boolean equals(Object other) {
if (!(other instanceof QuadItem)) {
return false;
}
return ((QuadItem) other).mClusterItem.equals(mClusterItem);
}
}
}
Setup algorithm and cluster click listener
map.setOnMarkerClickListener(mClusterManager);
mClusterManager.setAlgorithm(new DistanceBasedAlgorithm());
mClusterManager.setOnClusterClickListener(new ClusterManager.OnClusterClickListener<Location>() {
#Override
public boolean onClusterClick(Cluster<Location> cluster) {
for (Location location : cluster.getItems()) {
location.setShouldCluster(false);
}
mClusterManager.addItem(null); // this line resets the cache
mClusterManager.cluster(); // re-cluster
return false;
}
});
Done!
Explanation
A little explanation of how does the DistanceBasedAlgorithm enable uncluster
In getClusters function, the snippet below checks whether the item should be cluster
if (candidate.mClusterItem.isShouldCluster()) {
Bounds searchBounds = createBoundsFromSpan(candidate.getPoint(), zoomSpecificSpan);
clusterItems = mQuadTree.search(searchBounds);
}
else {
List<QuadItem> temp = new ArrayList<>();
temp.add(candidate);
clusterItems = temp;
}
The changes of addItem function, allows the algorithm to accept a null value, thereby allows PreCachingAlgorithmDecorator to clear cache.
#Override
public void addItem(Location item) {
if (item == null) return; // this line is the key to reset cache
final QuadItem quadItem = new QuadItem(item);
synchronized (mQuadTree) {
mItems.add(quadItem);
mQuadTree.add(quadItem);
}
}
It's been a while since I wrote the code. I might miss something. Leave me a comment if you have trouble to make this solution work. Thanks!
I wrote a code to draw a path from gpx file. If the user turns the smartphone, the path is cleared, then I created a ArrayList<MyLatLng> where every MyLatLng object is:
public class MyLatLng implements Parcelable {
private double latitude;
private double longitude;
public MyLatLng(double lat, double lon) {
latitude = lat;
longitude = lon;
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel parcel, int arg) {
parcel.writeDouble(latitude);
parcel.writeDouble(longitude);
}
public static final Parcelable.Creator<MyLatLng> CREATOR = new Creator<MyLatLng>() {
#Override
public MyLatLng createFromParcel(Parcel parcel) {
double latitude = parcel.readDouble();
double longitude = parcel.readInt();
return new MyLatLng(latitude, longitude);
}
#Override
public MyLatLng[] newArray(int size) {
return new MyLatLng[size];
}
};
//Metodi get/set
public double getLatitude() {
return latitude;
}
public double getLongitude() {
return longitude;
}
}
And using onSaveInstanceState I avoid the cancellation of the path. But this method introduces a non-elegance in my code because every time I need to create a LatLng object from MyLatLng object. Do you have any advice about this? Thanks a lot :)