I have Activity
with GoogleMap
and I send request to server, parse JSON response and add Markers
, using Picasso for loading icons.
PROBLEM
The problem is next: when I close app and open It again, data loads as well (urls also loading well), Markers
are added on GoogleMap
, but they have default icons, Picasso doesn't load new icons (onBitmapLoaded() not called). When I finish Activity
and start it again (without app closing), new markers' icons loads as well.
My implementation of Picasso Target:
public class PicassoMarker implements Target {
private static final String TAG = "PicassoMarker";
private Marker marker;
private LoadingCallBacks loadingCallBacks;
public PicassoMarker(Marker marker) {
this.marker = marker;
}
public PicassoMarker(Marker marker, LoadingCallBacks loadingCallBacks) {
this.marker = marker;
this.loadingCallBacks = loadingCallBacks;
}
@Override
public int hashCode() {
return marker.hashCode();
}
@Override
public boolean equals(Object o) {
if (o instanceof PicassoMarker) {
Marker marker = ((PicassoMarker) o).marker;
return this.marker.equals(marker);
} else {
return false;
}
}
@Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
marker.setIcon(BitmapDescriptorFactory.fromBitmap(bitmap));
Log.d(TAG, "onBitmapLoaded() -> " +
"\nBitmap:" +
"\n\twidth: " + bitmap.getWidth() +
"\n\theight: " + bitmap.getHeight() /*+
"\nCanvas:" +
"\n\twidth: " + resultBitmap.getWidth() +
"\n\theight: " + resultBitmap.getHeight()*/
);
if (loadingCallBacks != null) {
loadingCallBacks.onLoaded(bitmap);
}
}
@Override
public void onBitmapFailed(Drawable errorDrawable) {
Log.e(TAG, "onBitmapFailed()");
}
@Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
Log.d(TAG, "onPrepareLoad()");
}
public void setLoadingCallBacks(LoadingCallBacks loadingCallBacks) {
this.loadingCallBacks = loadingCallBacks;
}
public interface LoadingCallBacks {
void onLoaded(Bitmap bitmap);
}
}
And my AsyncTask
for sending request and adding Markers
:
new AsyncTask<String, Void, ArrayList<MarkerDataModel>>() {
private ArrayList<MarkerDataModel> markerDataModels = new ArrayList<>();
@Override
protected void onPreExecute() {
}
@Override
protected ArrayList<MarkerDataModel> doInBackground(String... params) {
// Parsing JSON ...
return markerDataModels;
}
@Override
protected void onPostExecute(ArrayList<MarkerDataModel> markerDataModels) {
if (markerDataModels != null && markerDataModels.size() != 0) {
MarkerDataModel markerDataModel;
for (int i = 0; i < markerDataModels.size(); i++) {
markerDataModel = markerDataModels.get(i);
Marker marker = googleMap.addMarker(new MarkerOptions()
.position(new LatLng(markerDataModel.getLat(), markerDataModel.getLng()))
.title(markerDataModel.getLabel()));
PicassoMarker picassoMarker picassoMarker = new PicassoMarker(marker);
Picasso.
with(activityContext)
.load(markerDataModel.getIcon())
//.memoryPolicy(MemoryPolicy.NO_CACHE, MemoryPolicy.NO_STORE)
//.networkPolicy(NetworkPolicy.NO_CACHE)
.into(picassoMarker);
}
}
}
}.execute("http://example.com");
SOLUTION
I've solved problem, using Glide.
Here is example code for loading Markers'
data from network and setting new icons using Glide:
new AsyncTask<String, Void, ArrayList<MarkerDataModel>>() {
@Override
protected void onPreExecute() {
...
}
@Override
protected ArrayList<MarkerDataModel> doInBackground(String... params) {
ArrayList<MarkerDataModel> markerDataModels = new ArrayList<>();
Send request and parse response...
return markerDataModels;
}
@Override
protected void onPostExecute(ArrayList<MarkerDataModel> markerDataModels) {
if (markerDataModels != null && markerDataModels.size() != 0) {
for (int i = 0; i < markerDataModels.size(); i++) {
MarkerDataModel markerDataModel = markerDataModels.get(i);
final Marker marker = googleMap.addMarker(new MarkerOptions()
.position(new LatLng(markerDataModel.getLat(), markerDataModel.getLng()))
.title(markerDataModel.getLabel()));
// Getting icon's URL from DataModel and loading icon
Glide
.with(activityContext)
.load(markerDataModel.getIcon())
.asBitmap()
.into(new SimpleTarget<Bitmap>() {
@Override
public void onResourceReady(Bitmap resource, GlideAnimation<? super Bitmap> glideAnimation) {
// Setting new icon for Marker
marker.setIcon(BitmapDescriptorFactory.fromBitmap(resource));
}
});
}
}
}
}.execute(...);
And MarkerDataModel
is simple class for storing Marker's
data:
public class MarkerDataModel {
public MarkerDataModel() {
}
private final String TAG = "MarkerDataModel";
public static final int TYPE_PRIMARY = 1;
public static final int TYPE_PEOPLE = 2;
private int type;
private int id;
private double lat;
private double lng;
private Marker marker;
/**
* TYPE_PRIMARY
**/
private String icon;
private String label;
private String nameShort;
private String nameFull;
private String phone;
private String information;
private String website;
private String sliderImages;
/**
* TYPE_PEOPLE
**/
private String photoUrl;
private String uniqueId;
private String registerTime;
public MarkerDataModel(int type,
int id,
double lat,
double lng,
String icon,
String label,
String nameShort,
String nameFull,
String phone,
String information,
String website,
String sliderImages) {
this.type = type;
this.id = id;
this.lat = lat;
this.lng = lng;
this.icon = icon;
this.label = label;
this.nameShort = nameShort;
this.nameFull = nameFull;
this.phone = phone;
this.information = information;
this.website = website;
this.sliderImages = sliderImages;
}
public MarkerDataModel(int type,
int id,
double lat,
double lng,
String photoUrl,
String uniqueId,
String registerTime) {
this.type = type;
this.lat = lat;
this.lng = lng;
this.photoUrl = photoUrl;
this.uniqueId = uniqueId;
this.registerTime = registerTime;
}
/**
* Getters
*/
public int getType() {
return type;
}
@Nullable
public Marker getMarker() {
return marker;
}
public int getId() {
return id;
}
public double getLat() {
return lat;
}
public double getLng() {
return lng;
}
/**
* TYPE_PRIMARY
**/
public String getIcon() {
return icon;
}
public String getInformation() {
return information;
}
public String getLabel() {
return label;
}
public String getNameFull() {
return nameFull;
}
public String getNameShort() {
return nameShort;
}
public String getPhone() {
return phone;
}
public String getSliderImages() {
return sliderImages;
}
public String getWebsite() {
return website;
}
/**
* TYPE_PEOPLE
**/
public String getPhotoUrl() {
return photoUrl;
}
public String getUniqueId() {
return uniqueId;
}
public String getRegisterTime() {
return registerTime;
}
@Nullable
public LatLng getLatLng() {
try {
return new LatLng(lat, lng);
} catch (Exception ex) {
Log.e(TAG, "getLatLng() -> ", ex);
return null;
}
}
/**
* Setters
*/
public void setMarker(Marker marker) {
this.marker = marker;
}
}
BENEFITS
Switching between TYPE_PRIMARY
and TYPE_PEOPLE
you can catch type of clicked Marker
like this:
googleMap.setOnMarkerClickListener(new GoogleMap.OnMarkerClickListener() {
@Override
public boolean onMarkerClick(Marker marker) {
MarkerDataModel markerDataModel = markerAllHashMap.get(marker);
int type;
try {
Log.d(TAG, "onMarkerClick() -> " + markerDataModel.getType());
} catch (Exception ex) {
Log.e(TAG, "onMarkerClick() -> ", ex);
}
try {
type = markerDataModel.getType();
} catch (Exception ex) {
type = -1;
}
switch (type) {
case MarkerDataModel.TYPE_PRIMARY: {
// Do something for TYPE_PRIMARY Marker
break;
}
case MarkerDataModel.TYPE_PEOPLE: {
// Do something for TYPE_PEOPLE Marker (e.g. show user's info)
break;
}
default: {
// Type == -1
marker.showInfoWindow();
break;
}
}
// Animate to center
googleMap.animateCamera(CameraUpdateFactory.newCameraPosition(new CameraPosition.Builder()
.target(marker.getPosition())
.zoom(18)
.build()));
return true;
}
});