-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
1,529 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,3 +11,4 @@ project.properties | |
.DS_Store | ||
.java-version | ||
secrets.properties | ||
.kotlin |
287 changes: 287 additions & 0 deletions
287
demo/src/main/java/com/google/maps/android/utils/demo/ClusteringDiffDemoActivity.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,287 @@ | ||
/* | ||
* Copyright 2013 Google Inc. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package com.google.maps.android.utils.demo; | ||
|
||
import android.graphics.Bitmap; | ||
import android.graphics.drawable.Drawable; | ||
import android.os.Handler; | ||
import android.util.Log; | ||
import android.view.View; | ||
import android.view.ViewGroup; | ||
import android.widget.ImageView; | ||
import android.widget.Toast; | ||
|
||
import androidx.annotation.NonNull; | ||
|
||
import com.google.android.gms.maps.CameraUpdateFactory; | ||
import com.google.android.gms.maps.GoogleMap; | ||
import com.google.android.gms.maps.model.BitmapDescriptor; | ||
import com.google.android.gms.maps.model.BitmapDescriptorFactory; | ||
import com.google.android.gms.maps.model.LatLng; | ||
import com.google.android.gms.maps.model.LatLngBounds; | ||
import com.google.android.gms.maps.model.Marker; | ||
import com.google.android.gms.maps.model.MarkerOptions; | ||
import com.google.maps.android.clustering.Cluster; | ||
import com.google.maps.android.clustering.ClusterItem; | ||
import com.google.maps.android.clustering.ClusterManager; | ||
import com.google.maps.android.clustering.view.ClusterRendererMultipleItems; | ||
import com.google.maps.android.ui.IconGenerator; | ||
import com.google.maps.android.utils.demo.model.Person; | ||
|
||
import java.util.ArrayList; | ||
import java.util.Collections; | ||
import java.util.List; | ||
import java.util.Random; | ||
|
||
/** | ||
* Demonstrates how to apply a diff to the current Cluster | ||
*/ | ||
public class ClusteringDiffDemoActivity extends BaseDemoActivity implements ClusterManager.OnClusterClickListener<Person>, ClusterManager.OnClusterInfoWindowClickListener<Person>, ClusterManager.OnClusterItemClickListener<Person>, ClusterManager.OnClusterItemInfoWindowClickListener<Person> { | ||
private ClusterManager<Person> mClusterManager; | ||
private final Random mRandom = new Random(1984); | ||
private Person itemtoUpdate = new Person(position(), "Teach", R.drawable.teacher); | ||
|
||
private final Random random = new Random(); | ||
private final Handler handler = new Handler(); | ||
|
||
@Override | ||
|
||
public void onMapReady(@NonNull GoogleMap map) { | ||
super.onMapReady(map); | ||
startRandomCalls(); | ||
} | ||
|
||
|
||
/** | ||
* Draws profile photos inside markers (using IconGenerator). | ||
* When there are multiple people in the cluster, draw multiple photos (using MultiDrawable). | ||
*/ | ||
private class PersonRenderer extends ClusterRendererMultipleItems<Person> { | ||
private final IconGenerator mIconGenerator = new IconGenerator(getApplicationContext()); | ||
private final IconGenerator mClusterIconGenerator = new IconGenerator(getApplicationContext()); | ||
private final ImageView mImageView; | ||
private final ImageView mClusterImageView; | ||
private final int mDimension; | ||
|
||
public PersonRenderer() { | ||
super(getApplicationContext(), getMap(), mClusterManager); | ||
|
||
View multiProfile = getLayoutInflater().inflate(R.layout.multi_profile, null); | ||
mClusterIconGenerator.setContentView(multiProfile); | ||
mClusterImageView = multiProfile.findViewById(R.id.image); | ||
|
||
mImageView = new ImageView(getApplicationContext()); | ||
mDimension = (int) getResources().getDimension(R.dimen.custom_profile_image); | ||
mImageView.setLayoutParams(new ViewGroup.LayoutParams(mDimension, mDimension)); | ||
int padding = (int) getResources().getDimension(R.dimen.custom_profile_padding); | ||
mImageView.setPadding(padding, padding, padding, padding); | ||
mIconGenerator.setContentView(mImageView); | ||
} | ||
|
||
public void setUpdateMarker(Person person) { | ||
Marker marker = getMarker(person); | ||
if (marker != null) { | ||
marker.setIcon(getItemIcon(person)); | ||
} | ||
} | ||
|
||
@Override | ||
protected void onBeforeClusterItemRendered(@NonNull Person person, @NonNull MarkerOptions markerOptions) { | ||
// Draw a single person - show their profile photo and set the info window to show their name | ||
markerOptions | ||
.icon(getItemIcon(person)) | ||
.title(person.name); | ||
} | ||
|
||
@Override | ||
protected void onClusterItemUpdated(@NonNull Person person, @NonNull Marker marker) { | ||
// Same implementation as onBeforeClusterItemRendered() (to update cached markers) | ||
marker.setIcon(getItemIcon(person)); | ||
marker.setTitle(person.name); | ||
} | ||
|
||
/** | ||
* Get a descriptor for a single person (i.e., a marker outside a cluster) from their | ||
* profile photo to be used for a marker icon | ||
* | ||
* @param person person to return an BitmapDescriptor for | ||
* @return the person's profile photo as a BitmapDescriptor | ||
*/ | ||
private BitmapDescriptor getItemIcon(Person person) { | ||
mImageView.setImageResource(person.profilePhoto); | ||
Bitmap icon = mIconGenerator.makeIcon(); | ||
return BitmapDescriptorFactory.fromBitmap(icon); | ||
} | ||
|
||
@Override | ||
protected void onBeforeClusterRendered(@NonNull Cluster<Person> cluster, @NonNull MarkerOptions markerOptions) { | ||
// Draw multiple people. | ||
// Note: this method runs on the UI thread. Don't spend too much time in here (like in this example). | ||
markerOptions.icon(getClusterIcon(cluster)); | ||
} | ||
|
||
@Override | ||
protected void onClusterUpdated(@NonNull Cluster<Person> cluster, Marker marker) { | ||
// Same implementation as onBeforeClusterRendered() (to update cached markers) | ||
marker.setIcon(getClusterIcon(cluster)); | ||
} | ||
|
||
/** | ||
* Get a descriptor for multiple people (a cluster) to be used for a marker icon. Note: this | ||
* method runs on the UI thread. Don't spend too much time in here (like in this example). | ||
* | ||
* @param cluster cluster to draw a BitmapDescriptor for | ||
* @return a BitmapDescriptor representing a cluster | ||
*/ | ||
private BitmapDescriptor getClusterIcon(Cluster<Person> cluster) { | ||
List<Drawable> profilePhotos = new ArrayList<>(Math.min(4, cluster.getSize())); | ||
int width = mDimension; | ||
int height = mDimension; | ||
|
||
for (Person p : cluster.getItems()) { | ||
// Draw 4 at most. | ||
if (profilePhotos.size() == 4) break; | ||
Drawable drawable = getResources().getDrawable(p.profilePhoto); | ||
drawable.setBounds(0, 0, width, height); | ||
profilePhotos.add(drawable); | ||
} | ||
MultiDrawable multiDrawable = new MultiDrawable(profilePhotos); | ||
multiDrawable.setBounds(0, 0, width, height); | ||
|
||
mClusterImageView.setImageDrawable(multiDrawable); | ||
Bitmap icon = mClusterIconGenerator.makeIcon(String.valueOf(cluster.getSize())); | ||
return BitmapDescriptorFactory.fromBitmap(icon); | ||
} | ||
|
||
@Override | ||
protected boolean shouldRenderAsCluster(@NonNull Cluster<Person> cluster) { | ||
// Always render clusters. | ||
return cluster.getSize() >= 1; | ||
} | ||
} | ||
|
||
private void startRandomCalls() { | ||
// Initial call to the random update. | ||
callUpdateRandom(); | ||
} | ||
|
||
private void callUpdateRandom() { | ||
// Generate a random delay between 1 and 5 seconds | ||
int delay = random.nextInt(5000) + 1000; // Random delay in milliseconds (1000ms to 5000ms) | ||
|
||
// Post the next call with the random delay | ||
handler.postDelayed(this::callUpdateRandom, delay); | ||
updateRandom(); | ||
} | ||
|
||
@Override | ||
public boolean onClusterClick(Cluster<Person> cluster) { | ||
// Show a toast with some info when the cluster is clicked. | ||
String firstName = cluster.getItems().iterator().next().name; | ||
Toast.makeText(this, cluster.getSize() + " (including " + firstName + ")", Toast.LENGTH_SHORT).show(); | ||
|
||
// Zoom in the cluster. Need to create LatLngBounds and including all the cluster items | ||
// inside of bounds, then animate to center of the bounds. | ||
|
||
// Create the builder to collect all essential cluster items for the bounds. | ||
LatLngBounds.Builder builder = LatLngBounds.builder(); | ||
for (ClusterItem item : cluster.getItems()) { | ||
builder.include(item.getPosition()); | ||
} | ||
// Get the LatLngBounds | ||
final LatLngBounds bounds = builder.build(); | ||
|
||
// Animate camera to the bounds | ||
try { | ||
getMap().animateCamera(CameraUpdateFactory.newLatLngBounds(bounds, 100)); | ||
} catch (Exception e) { | ||
e.printStackTrace(); | ||
} | ||
|
||
return true; | ||
} | ||
|
||
@Override | ||
public void onClusterInfoWindowClick(Cluster<Person> cluster) { | ||
// Does nothing, but you could go to a list of the users. | ||
} | ||
|
||
@Override | ||
public boolean onClusterItemClick(Person item) { | ||
// Does nothing, but you could go into the user's profile page, for example. | ||
return false; | ||
} | ||
|
||
@Override | ||
public void onClusterItemInfoWindowClick(Person item) { | ||
// Does nothing, but you could go into the user's profile page, for example. | ||
} | ||
|
||
@Override | ||
protected void startDemo(boolean isRestore) { | ||
if (!isRestore) { | ||
getMap().moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(51.503186, -0.126446), 6)); | ||
} | ||
|
||
mClusterManager = new ClusterManager<>(this, getMap()); | ||
mClusterManager.setRenderer(new PersonRenderer()); | ||
getMap().setOnCameraIdleListener(mClusterManager); | ||
getMap().setOnMarkerClickListener(mClusterManager); | ||
getMap().setOnInfoWindowClickListener(mClusterManager); | ||
mClusterManager.setOnClusterClickListener(this); | ||
mClusterManager.setOnClusterInfoWindowClickListener(this); | ||
mClusterManager.setOnClusterItemClickListener(this); | ||
mClusterManager.setOnClusterItemInfoWindowClickListener(this); | ||
|
||
addItems(); | ||
mClusterManager.cluster(); | ||
} | ||
|
||
private void addItems() { | ||
|
||
// http://www.flickr.com/photos/sdasmarchives/5036231225/ | ||
mClusterManager.addItem(new Person(position(), "John", R.drawable.john)); | ||
|
||
|
||
// http://www.flickr.com/photos/usnationalarchives/4726892651/ | ||
itemtoUpdate = new Person(position(), "Teach", R.drawable.teacher); | ||
mClusterManager.addItem(itemtoUpdate); | ||
} | ||
|
||
|
||
private void updateRandom() { | ||
itemtoUpdate = new Person(position(), "Teach", R.drawable.teacher); | ||
Log.d("ClusterTest", "We start updating the item. New position: " + itemtoUpdate.getPosition().toString()); | ||
|
||
mClusterManager.updateItem(this.itemtoUpdate); | ||
|
||
//We could also call the diff() method to add, remove and update at once. | ||
mClusterManager.diff(null, null, new ArrayList<>(Collections.singleton(this.itemtoUpdate))); | ||
mClusterManager.setAnimation(true); | ||
|
||
// Cluster needs to be called, to force an update of the cluster. | ||
mClusterManager.cluster(); | ||
} | ||
|
||
private LatLng position() { | ||
return new LatLng(random(51.6723432, 51.38494009999999), random(0.148271, -0.3514683)); | ||
} | ||
|
||
private double random(double min, double max) { | ||
return mRandom.nextDouble() * (max - min) + min; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.