Related
I am creating custom marker image from layout file and using an image in that file. I am setting image on image view using Picasso which does not work for the first time, but works for the second time and image is loaded successfully. I have read about the weak reference that Piccaso holds and also tried solution given in various stack overflow posts, but none seems to work. Sharing my code, any help would be appreciated.
//adding marker method
protected Marker createMarker(double latitude, double longitude,String markerText,String imageUrl) {
Log.e("Adding","Marker");
LatLng marker = new LatLng(latitude, longitude);
CameraPosition cameraPosition = new CameraPosition.Builder().target(marker).zoom(16).build();
googleMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));
return googleMap.addMarker(new MarkerOptions()
.position(new LatLng(latitude, longitude))
.title(markerText)
.snippet("Salesman Info").icon(BitmapDescriptorFactory.fromBitmap(getMarkerBitmapFromView(markerText,imageUrl))));
}
// making drawable from layout file
private Bitmap getMarkerBitmapFromView(String markerText,String imageUrl) {
View customMarkerView = ((LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate(R.layout.custom_marker, null);
final ImageView markerImageView = (ImageView) customMarkerView.findViewById(R.id.profile_image);
TextView markerTextView = (TextView)customMarkerView.findViewById(R.id.marker_text);
markerTextView.setText(markerText);
final Target target = new Target() {
#Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
markerImageView.setImageBitmap(bitmap);
Log.e("Loading marker","Image loaded");
}
#Override
public void onBitmapFailed(Drawable errorDrawable) {
Log.e("Loading marker","Image failed");
}
#Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
Log.e("Loading marker","Image prepared");
}
};
markerImageView.setTag(target);
Picasso.with(getContext())
.load(imageUrl)
.into(target);
customMarkerView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
customMarkerView.layout(0, 0, customMarkerView.getMeasuredWidth(), customMarkerView.getMeasuredHeight());
customMarkerView.buildDrawingCache();
Bitmap returnedBitmap = Bitmap.createBitmap(customMarkerView.getMeasuredWidth(), customMarkerView.getMeasuredHeight(),
Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(returnedBitmap);
canvas.drawColor(Color.WHITE, PorterDuff.Mode.SRC_IN);
Drawable drawable = customMarkerView.getBackground();
if (drawable != null)
drawable.draw(canvas);
customMarkerView.draw(canvas);
return returnedBitmap;
}
I solved this issue by using Glide library with Simple target option.
Glide.with(getContext())
.asBitmap()
.load("imageurl")
.into(new SimpleTarget<Bitmap>() {
#Override
public void onLoadFailed(#Nullable Drawable errorDrawable) {
Marker m =googleMap.addMarker(new MarkerOptions()
.position(new LatLng(latitude, longitude))
.title(markerText)
.snippet("").icon(BitmapDescriptorFactory.fromBitmap(getMarkerBitmapFromView(markerText,null,mCustomMarkerView))));
m.setTag(info);
super.onLoadFailed(errorDrawable);
}
#Override
public void onResourceReady(Bitmap resource, Transition<? super Bitmap> transition) {
Marker m =googleMap.addMarker(new MarkerOptions()
.position(new LatLng(latitude, longitude))
.title(markerText)
.snippet("").icon(BitmapDescriptorFactory.fromBitmap(getMarkerBitmapFromView(markerText,resource,mCustomMarkerView))));
m.setTag(info);
}
}
);
I am using MapCustomClustering to show images on the map. The Cluster Icon also shows the number of items in the cluster but it doesn't show in mine. I am loading data from Parse cloud. The example one renders cluster.getSize() but in mine it doesn't work.
#Override
protected void onBeforeClusterRendered(final Cluster<MapPosts> cluster, final 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).
final List<Drawable> profilePhotos = new ArrayList<>(Math.min(4, cluster.getSize()));
final int width = mDimension;
final int height = mDimension;
int i = 0;
for (MapPosts p : cluster.getItems()) {
// Draw 4 at most.
i++;
Picasso.with(getApplicationContext())
.load(String.valueOf(p.profilePhoto))
.into(new Target() {
#Override
public void onBitmapLoaded(final Bitmap bitmap, Picasso.LoadedFrom from) {
Drawable drawable = new BitmapDrawable(getResources(), bitmap);
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()));
markerOptions.icon(BitmapDescriptorFactory.fromBitmap(icon));
}
#Override
public void onBitmapFailed(Drawable errorDrawable) {
}
#Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
}
});
if (profilePhotos.size() == 4) break;
}
}
I found the answear :-) -> Make sure you have some TextView like this in your icon style! The IMPORTANT POINT is you name it "text" !!!
<TextView
**android:id="#id/text"**
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#android:color/holo_green_dark"
android:paddingLeft="#dimen/custom_profile_padding"
android:paddingRight="#dimen/custom_profile_padding"
android:layout_gravity="center"
android:alpha=".8"/>
I work with Google Map cluster using Android-map-utils library from google. In my case, I use Glide to load image from URL:
#Override
protected void onBeforeClusterItemRendered(final FeedsModel feedsModel, final MarkerOptions markerOptions) {
// Draw a single person.
// Set the info window to show their name.
Glide
.with(mActivity.getApplicationContext())
.load(feedsModel.getImages().getThumbnail().getUrl())
.diskCacheStrategy(DiskCacheStrategy.ALL)
.into(new SimpleTarget<GlideDrawable>() {
#Override
public void onResourceReady(GlideDrawable resource, GlideAnimation<? super GlideDrawable> glideAnimation) {
mImageView.setImageDrawable(resource);
Bitmap icon = mIconGenerator.makeIcon();
Marker markerToChange = null;
for (Marker marker : mClusterManager.getMarkerCollection().getMarkers()) {
if (marker.getPosition().equals(feedsModel.getPosition())) {
markerToChange = marker;
}
}
// if found - change icon
if (markerToChange != null) {
markerToChange.setIcon(BitmapDescriptorFactory.fromBitmap(icon));
}
}
});
Bitmap icon = mIconGenerator.makeIcon();
markerOptions.icon(BitmapDescriptorFactory.fromBitmap(icon));
}
#Override
protected void onBeforeClusterRendered(final Cluster<FeedsModel> cluster, final 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).
final List<Drawable> profilePhotos = new ArrayList<Drawable>(Math.min(4, cluster.getSize()));
final int width = mDimension;
final int height = mDimension;
int i = 0;
for (final FeedsModel p : cluster.getItems()) {
// Draw 4 at most.
i++;
Glide
.with(mActivity.getApplicationContext())
.load(p.getImages().getThumbnail().getUrl())
.diskCacheStrategy(DiskCacheStrategy.ALL)
.into(new SimpleTarget<GlideDrawable>() {
#Override
public void onResourceReady(GlideDrawable resource, GlideAnimation<? super GlideDrawable> glideAnimation) {
resource.setBounds(0, 0, width, height);
profilePhotos.add(resource);
MultiDrawable multiDrawable = new MultiDrawable(profilePhotos);
multiDrawable.setBounds(0, 0, width, height);
mClusterImageView.setImageDrawable(multiDrawable);
Bitmap icon = mClusterIconGenerator.makeIcon(String.valueOf(cluster.getSize()));
markerOptions.icon(BitmapDescriptorFactory.fromBitmap(icon));
}
});
if (i == 4) break;
}
Bitmap icon = mClusterIconGenerator.makeIcon(String.valueOf(cluster.getSize()));
markerOptions.icon(BitmapDescriptorFactory.fromBitmap(icon));
}
With above code, I see that: image load in Item and Cluster wrong when zoom in or zoom out. What is my wrong here ?
Finally, I found solution, I combine with :
protected void onClusterItemRendered(FeedsModel clusterItem, Marker marker)
protected void onClusterRendered(Cluster<FeedsModel> cluster, Marker marker)
these function to load image from URL
#Override
protected void onBeforeClusterItemRendered(Trace trace, MarkerOptions markerOptions) {
// Draw a single person.
// Set the info window to show their name.
mImageView.setImageResource(R.drawable.aa001); //temp image.
icon = mIconGenerator.makeIcon();
markerOptions.icon(BitmapDescriptorFactory.fromBitmap(icon));
}
#Override
protected void onClusterItemRendered(final Trace trace, final Marker marker){
Glide.with(mContext.getApplicationContext())
.load(ApplicationClass.photoServer + trace.image)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.thumbnail(0.1f)
.into(new SimpleTarget<GlideDrawable>() {
#Override
public void onResourceReady(GlideDrawable resource, GlideAnimation<? super GlideDrawable> glideAnimation) {
mImageView.setImageDrawable(resource);
icon = mIconGenerator.makeIcon();
marker.setIcon(BitmapDescriptorFactory.fromBitmap(icon));
}
});
}
And for the clusters:
#Override
protected void onBeforeClusterRendered(Cluster<Trace> cluster, MarkerOptions markerOptions) {
super.onBeforeClusterRendered(cluster, markerOptions);
//pick the first media
Bitmap icon = mClusterIconGenerator.makeIcon(String.valueOf(cluster.getSize()));
markerOptions.icon(BitmapDescriptorFactory.fromBitmap(icon));
}
#Override
protected void onClusterRendered(Cluster<Trace> cluster, Marker marker) {
super.onClusterRendered(cluster, marker);
//pick the first trace
final Trace trace = cluster.getItems().iterator().next();
Glide.with(mContext.getApplicationContext())
.load(ApplicationClass.photoServer + trace.image)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.centerCrop()
.into(new SimpleTarget<GlideDrawable>() {
#Override
public void onResourceReady(GlideDrawable resource, GlideAnimation<? super GlideDrawable> glideAnimation) {
mClusterImageView. setImageDrawable(resource);
Bitmap icon = mClusterIconGenerator.makeIcon(String.valueOf(cluster.getSize()));
marker.setIcon(BitmapDescriptorFactory.fromBitmap(icon));
}
});
}
This is the solution i found with help of above mentions solution
private class PersonRenderer extends DefaultClusterRenderer<Person> {
private final IconGenerator mIconGenerator = new IconGenerator(getActivity().getApplicationContext());
private final IconGenerator mClusterIconGenerator = new IconGenerator(getActivity().getApplicationContext());
private final ImageView mImageView;
private final ImageView mClusterImageView;
private final int mDimension;
Bitmap icon;
public PersonRenderer() {
super(getActivity().getApplicationContext(), mMap, mClusterManager);
View multiProfile = getLayoutInflater().inflate(R.layout.multi_profile, null);
mClusterIconGenerator.setContentView(multiProfile);
mClusterImageView = (ImageView) multiProfile.findViewById(R.id.image);
mImageView = new ImageView(getActivity().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);
}
#Override
protected void onBeforeClusterItemRendered(Person person, MarkerOptions markerOptions) {
icon = mIconGenerator.makeIcon();
markerOptions.icon(BitmapDescriptorFactory.fromBitmap(icon)).title(person.name);
}
#Override
protected void onClusterItemRendered(Person clusterItem, final Marker marker) {
Glide.with(getActivity())
.load(clusterItem.profilePhoto)
.apply(new RequestOptions().diskCacheStrategy(DiskCacheStrategy.ALL))
.thumbnail(0.1f)
.into(new SimpleTarget<Drawable>() {
#Override
public void onResourceReady(Drawable drawable, Transition<? super Drawable> transition) {
mImageView.setImageDrawable(drawable);
icon = mIconGenerator.makeIcon();
marker.setIcon(BitmapDescriptorFactory.fromBitmap(icon));
}
});
}
#Override
protected void onClusterRendered(final Cluster<Person> cluster, final Marker marker) {
final List<Drawable> profilePhotos = new ArrayList<>(Math.min(4, cluster.getSize()));
final int width = mDimension;
final int height = mDimension;
Bitmap dummyBitmap = null;
Drawable drawable;
final int clusterSize = cluster.getSize();
final int[] count = {0};
for (Person p : cluster.getItems()) {
// Draw 4 at most.
if (profilePhotos.size() == 4) break;
try {
Glide.with(getActivity().getApplicationContext())
.load(p.profilePhoto)
.apply(new RequestOptions().diskCacheStrategy(DiskCacheStrategy.ALL))
.into(new SimpleTarget<Drawable>(){
#Override
public void onResourceReady(#NonNull Drawable resource, #Nullable Transition<? super Drawable> transition) {
resource.setBounds(0, 0, width, height);
profilePhotos.add(resource);
MultiDrawable multiDrawable = new MultiDrawable(profilePhotos);
multiDrawable.setBounds(0, 0, width, height);
mClusterImageView.setImageDrawable(multiDrawable);
Bitmap icon = mClusterIconGenerator.makeIcon(String.valueOf(cluster.getSize()));
marker.setIcon(BitmapDescriptorFactory.fromBitmap(icon));
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
Bitmap icon = mClusterIconGenerator.makeIcon(String.valueOf(cluster.getSize()));
marker.setIcon(BitmapDescriptorFactory.fromBitmap(icon));
}
#Override
protected void onBeforeClusterRendered(final Cluster<Person> cluster, final MarkerOptions markerOptions) {
}
#Override
protected boolean shouldRenderAsCluster(Cluster cluster) {
// Always render clusters.
return cluster.getSize() > 1;
}
}
Hide the marker in onBeforeClusterRendered. You do this so that the marker icon doesn't flicker between the default icon and your custom icon.
In onClusterItemRendered fetch the icon bitmap, once it is loaded set it as the marker icon and make the marker visible. If bitmap request fails just show the default icon.
Use Picasso for caching.
final String url = "...";
clusterManager.setRenderer(new DefaultClusterRenderer<MapClusterItem>(activity, googleMap, clusterManager) {
#Override
protected void onBeforeClusterItemRendered(MapClusterItem item, MarkerOptions markerOptions) {
markerOptions.visible(false);
}
#Override
protected void onClusterItemRendered(final MapClusterItem clusterItem, final Marker marker) {
Picasso.
with(context).
load(url)
.into(new Target() {
#Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
marker.setIcon(BitmapDescriptorFactory.fromBitmap(bitmap));
marker.setVisible(true);
}
#Override
public void onBitmapFailed(Drawable errorDrawable) {
marker.setVisible(true);
}
#Override
public void onPrepareLoad(Drawable placeHolderDrawable) {}
});
}
});
I would like to show the custom marker to google map and cluster them. The marker contains a ImageView that will shows the avatar that is downloaded from network. Here is my target:
Everythings are OK, however, when I implemented the Google Maps Android Marker Clustering Utility, the ImageView shows the same avatar (sometime two wrong avatars).
Here is my custom MarkerRender:
public class MarkerRender extends DefaultClusterRenderer<Image> {
private static final String TAG = MarkerRender.class.getSimpleName();
private IconGenerator clusterGenerator;
private IconGenerator markerGenerator;
private ImageView mImgMarkerThumbnail;
private ImageView mImgMarkerClusterThumbnail;
private TextView txtSizeCluster;
private Activity activity;
private Bitmap mask, background;
private AtomicInteger imageDownloadCounter;
private int totalItem;
private ImageSize imageSize;
public MarkerRender(FragmentActivity activity, GoogleMap mMap, ClusterManager<Image> mClusterManager) {
super(activity, mMap, mClusterManager);
this.activity = activity;
imageDownloadCounter = new AtomicInteger(0);
mask = BitmapFactory.decodeResource(activity.getResources(),
R.drawable.annotation_behind);
background = BitmapFactory.decodeResource(activity.getResources(),
R.drawable.annotation_behind);
setUpClusterIcon();
setUpMarker();
}
private void setUpClusterIcon() {
clusterGenerator = new IconGenerator(activity);
View clusterView = activity.getLayoutInflater().inflate(R.layout.custom_marker_cluster, null);
txtSizeCluster = (TextView) clusterView.findViewById(R.id.tv_number_marker);
mImgMarkerClusterThumbnail = (ImageView) clusterView.findViewById(R.id.img_load);
clusterGenerator.setContentView(clusterView);
clusterGenerator.setBackground(null);
}
private void setUpMarker() {
markerGenerator = new IconGenerator(activity);
View markerView = activity.getLayoutInflater().inflate(R.layout.custom_marker, null);
mImgMarkerThumbnail = (ImageView) markerView.findViewById(R.id.img_load);
markerGenerator.setContentView(markerView);
markerGenerator.setBackground(null);
}
#Override
protected void onBeforeClusterItemRendered(final Image image, final MarkerOptions markerOptions) {
initImageSizeIfNeed();
Bitmap icon = markerGenerator.makeIcon();
PFLogManager.INSTANCE.logE(TAG, "maken icon: " + icon.hashCode());
markerOptions.icon(BitmapDescriptorFactory.fromBitmap(icon));
icon.recycle();
}
#Override
protected void onClusterItemRendered(final Image image, Marker marker) {
super.onClusterItemRendered(image, marker);
ImageLoader.getInstance().loadImage(image.getMapImageLink(), imageSize,
new SimpleImageLoadingListener() {
#Override
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
Bitmap croppedBitmap = Helpers.makeCroppedBitmap(loadedImage, background, mask);
mImgMarkerThumbnail.setImageBitmap(croppedBitmap);
}
});
}
#Override
protected void onBeforeClusterRendered(Cluster<Image> cluster, MarkerOptions markerOptions) {
initImageSizeIfNeed();
Bitmap icon = clusterGenerator.makeIcon();
markerOptions.icon(BitmapDescriptorFactory.fromBitmap(icon));
icon.recycle();
}
#Override
protected void onClusterRendered(Cluster<Image> cluster, Marker marker) {
super.onClusterRendered(cluster, marker);
ArrayList<Image> list = new ArrayList<>(cluster.getItems());
setTextNumberMarker(cluster);
String urlFirstImage = list.get(0).getMapImageLink();
ImageLoader.getInstance().loadImage(urlFirstImage, imageSize,
new SimpleImageLoadingListener() {
#Override
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
final Bitmap croppedBitmap = Helpers.makeCroppedBitmap(loadedImage, background, mask);
mImgMarkerClusterThumbnail.setImageBitmap(croppedBitmap);
}
});
}
private void loadClusterThumbnail(String url) {
}
private void setTextNumberMarker(Cluster<Image> cluster) {
int size = cluster.getSize();
if (size > 99) {
txtSizeCluster.setText("99+");
} else {
txtSizeCluster.setText(String.valueOf(cluster.getSize()));
}
}
#Override
protected boolean shouldRenderAsCluster(Cluster cluster) {
return cluster.getSize() > 1;
}
}
I've guess that the issue is I use only one ImageView to show those avatar, so I try to use unique ImageView for each marker (by inflat new one from xml every time needed), but the result is they are all show the blank marker (just the background and there is no avatar).
I've resolved it myself. My solution is use the Marker.setIcon() method after the image is downloaded from netword or got from cache. I dont use the ImageView anymore.
So, i modified the above MarkerRender class:
The setUpClusterIcon() method:
private void setUpClusterIcon() {
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(markerWidth, markerHeight);
ImageView marker = new ImageView(activity);
marker.setLayoutParams(params);
clusterGenerator = new IconGenerator(activity);
clusterGenerator.setContentView(marker);
clusterGenerator.setBackground(null);
}
And the onClusterItemRendered() method:
protected void onClusterItemRendered(final Image image, final Marker marker) {
super.onClusterItemRendered(image, marker);
ImageLoader.getInstance().loadImage(image.getMapImageLink(), imageSize,
new SimpleImageLoadingListener() {
#Override
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
Bitmap croppedBitmap = Helpers.makeClusterItemBitmap(background, loadedImage, mask);
try {
marker.setIcon(BitmapDescriptorFactory.fromBitmap(croppedBitmap));
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
Also the makeClusterItemBitmap helper method:
public static Bitmap makeClusterItemBitmap(Bitmap background, Bitmap original, Bitmap mask) {
Bitmap croppedOriginal = makeCroppedBitmap(original, mask);
Bitmap result = Bitmap.createBitmap(background.getWidth(), background.getHeight(),
Bitmap.Config.ARGB_8888);
Canvas mCanvas = new Canvas(result);
croppedOriginal = Bitmap.createScaledBitmap(croppedOriginal, croppedOriginal.getWidth() - 20, croppedOriginal.getHeight() - 20, true);
mCanvas.drawBitmap(background, 0, 0, null);
mCanvas.drawBitmap(croppedOriginal, 10, 10, null);
return result;
}
public static Bitmap makeCroppedBitmap(Bitmap original, Bitmap mask) {
original = Bitmap.createScaledBitmap(original, mask.getWidth(),
mask.getHeight(), true);
Bitmap result = Bitmap.createBitmap(original.getWidth(), original.getHeight(),
Bitmap.Config.ARGB_8888);
Canvas mCanvas = new Canvas(result);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
mCanvas.drawBitmap(original, 0, 0, null);
mCanvas.drawBitmap(mask, 0, 0, paint);
paint.setXfermode(null);
return result;
}
Done, finish three nightmare researchingdays days :P
However, this approach leads new issue: the performance. Cause by drawing new bitmap with many layers, the map is laggy a bit. I'm thinking in improving this :)
Any sugguestion are appriciate :D
I ma building a chat application and I am attempting to append an image to an EditText, through use of Picasso to get the image from a URL and the append and ImageGetter to attach the image to the EditText. However, what I have implemented below does not work, as appending messages when using the app displays nothing (but the message does show up in the database).
I have tested without using Picasso, as simply just using the ImageGetter with an image resource within the app works just fine, only it's not from a URL as is required.
What is the proper way to configure the ImageGetter and/or the append method so that this functionality will work with Picasso? Or is there a simpler way?
Append method:
public void appendToMessageHistory(final String username,
final String message) {
if (username != null && message != null) {
Picasso.with(getBaseContext())
.load("http://localhost:3000/uploads/campaign/image/2/2.jpg")
.into(new Target() {
#Override
public void onPrepareLoad(Drawable arg0) {
}
#Override
public void onBitmapLoaded(Bitmap bitmap,
LoadedFrom arg1) {
Drawable drawImage = new BitmapDrawable(
getBaseContext().getResources(), bitmap);
drawImage.setBounds(0, 0,
drawImage.getIntrinsicHeight(),
drawImage.getIntrinsicWidth());
messageHistoryText.append(Html.fromHtml("<b>"
+ username + ":" + "</b>" + "<br>"));
messageHistoryText.append(Html.fromHtml(message
+ "<hr>" + "<br>")
+ System.getProperty("line.separator") + "");
messageHistoryText.append(Html
.fromHtml("<img src = '" + drawImage
+ "'/>",
imageGetter,
null));
}
#Override
public void onBitmapFailed(Drawable arg0) {
}
});
}
}
ImageGetter:
ImageGetter imageGetter = new ImageGetter() {
Drawable imageUsed=null;
#Override
public Drawable getDrawable(String source) {
Picasso.with(getBaseContext())
.load("http://localhost:3000/uploads/campaign/image/2/2.jpg")
.into(new Target() {
#Override
public void onPrepareLoad(Drawable arg0) {
}
#Override
public void onBitmapLoaded(Bitmap bitmap,
LoadedFrom arg1) {
Drawable drawImage = new BitmapDrawable(
getBaseContext().getResources(), bitmap);
drawImage.setBounds(0, 0,
drawImage.getIntrinsicHeight(),
drawImage.getIntrinsicWidth());
imageUsed=drawImage;
}
#Override
public void onBitmapFailed(Drawable arg0) {
}
});
return imageUsed;
}
};
I couldn't get it to work with using Picasso's Target...
My workaround is:
use an AsyncTask for concurrency
use Picasso's get() method to load the image synchronously in the AsyncTask's background method
Like this:
public class PicassoImageGetter implements Html.ImageGetter {
final Resources resources;
final Picasso pablo;
final TextView textView;
public PicassoImageGetter(final TextView textView, final Resources resources, final Picasso pablo) {
this.textView = textView;
this.resources = resources;
this.pablo = pablo;
}
#Override public Drawable getDrawable(final String source) {
final BitmapDrawablePlaceHolder result = new BitmapDrawablePlaceHolder();
new AsyncTask<Void, Void, Bitmap>() {
#Override
protected Bitmap doInBackground(final Void... meh) {
try {
return pablo.load(source).get();
} catch (Exception e) {
return null;
}
}
#Override
protected void onPostExecute(final Bitmap bitmap) {
try {
final BitmapDrawable drawable = new BitmapDrawable(resources, bitmap);
drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
result.setDrawable(drawable);
result.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
textView.setText(textView.getText()); // invalidate() doesn't work correctly...
} catch (Exception e) {
/* nom nom nom*/
}
}
}.execute((Void) null);
return result;
}
static class BitmapDrawablePlaceHolder extends BitmapDrawable {
protected Drawable drawable;
#Override
public void draw(final Canvas canvas) {
if (drawable != null) {
drawable.draw(canvas);
}
}
public void setDrawable(Drawable drawable) {
this.drawable = drawable;
}
}
Hope this is useful.
I built upon Thomas' answer. I used the existing Picasso methods to download any images on a different thread and accounted for the placeholder Drawables as well.
public class PicassoImageGetter implements Html.ImageGetter {
private TextView textView = null;
public PicassoImageGetter() {}
public PicassoImageGetter(TextView target) {
textView = target;
}
#Override
public Drawable getDrawable(String source) {
BitmapDrawablePlaceHolder drawable = new BitmapDrawablePlaceHolder();
Context context = FeedSurferApp.getContext();
FeedSurferApp
.getPicasso()
.load(source)
.error(ResourcesCompat.getDrawable(context.getResources(), R.drawable.connection_error, context.getTheme()))
.into(drawable);
return drawable;
}
private class BitmapDrawablePlaceHolder extends BitmapDrawable implements Target {
protected Drawable drawable;
#Override
public void draw(final Canvas canvas) {
if (drawable != null) {
drawable.draw(canvas);
}
}
public void setDrawable(Drawable drawable) {
this.drawable = drawable;
drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
if (textView != null) {
textView.setText(textView.getText());
}
}
#Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
setDrawable(new BitmapDrawable(FeedSurferApp.getContext().getResources(), bitmap));
}
#Override
public void onBitmapFailed(Drawable errorDrawable) {
setDrawable(errorDrawable);
}
#Override
public void onPrepareLoad(Drawable placeHolderDrawable) {}
}
}
I found a way to use ImageGetter with Picasso Target:
public Drawable getDrawable(String source) {
final BitmapDrawablePlaceHolder result = new BitmapDrawablePlaceHolder();
Picasso.with(getContext()).load(source).into(new Target() {
#Override public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
final BitmapDrawable drawable = new BitmapDrawable(mContext.getResources(), bitmap);
drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
result.setDrawable(drawable);
result.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
content.setText(content.getText());
// cache is now warmed up
}
#Override public void onBitmapFailed(Drawable errorDrawable) { }
#Override public void onPrepareLoad(Drawable placeHolderDrawable) { }
});
return result;
}
This uses the cache and is no longer a synchronous call requiring the AsyncTask.
Credit to: Fetch images with Callback in Picasso?
Another simple way to use Picasso is to create a custom TextView and make it implement Target interface of Picasso.
Then you can do something as simple as this.
Picasso.get().load("Some_img_url").placeholder(R.drawable.ic_launcher_foreground).into(tv)
Below is the class
class TextViewWithImageLoader #JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : AppCompatTextView(context, attrs, defStyleAttr), Target {
enum class Direction { START, END, TOP, BOTTOM }
private var mWidthInPixels: Int = 0
private var mHeightInPixels: Int = 0
private var mDirection: Direction = Direction.START //default value
// This method initialize the required parameters for the TextView to load the image
fun setupImageLoader(widthInPixels: Int, heightInPixels: Int, direction: Direction) {
mWidthInPixels = widthInPixels
mHeightInPixels = heightInPixels
mDirection = direction
}
// sets the size of the drawable
private fun setDrawableBounds(drawable: Drawable) {
drawable.setBounds(0, 0, mWidthInPixels, mHeightInPixels)
}
// Sets the initial placeholder drawable
override fun onPrepareLoad(placeHolderDrawable: Drawable?) {
//checking if height and width are valid
if (placeHolderDrawable != null && mWidthInPixels > 0 && mHeightInPixels > 0) {
setDrawableBounds(placeHolderDrawable)
setDrawable(placeHolderDrawable)
}
}
// set the drawable based on the Direction enum
private fun setDrawable(placeHolderDrawable: Drawable?) {
when (mDirection) {
Direction.START -> setCompoundDrawables(placeHolderDrawable, null, null, null);
Direction.END -> setCompoundDrawables(null, null, placeHolderDrawable, null);
Direction.TOP -> setCompoundDrawables(null, placeHolderDrawable, null, null);
Direction.BOTTOM -> setCompoundDrawables(null, null, null, placeHolderDrawable);
}
}
//In this method we receive the image from the url
override fun onBitmapLoaded(bitmap: Bitmap?, from: Picasso.LoadedFrom?) {
//checking if height and width are valid
if (mWidthInPixels > 0 && mHeightInPixels > 0) {
val drawable = BitmapDrawable(resources, bitmap)
setDrawableBounds(drawable)
setDrawable(drawable)
}
}
override fun onBitmapFailed(e: Exception?, errorDrawable: Drawable?) {
//Do nothing as we are already setting a default value in onPrepareLoad method
// you can add your logic here if required
}
}
You can use this class directly or if you currently have your own custom view then just extend this class.
You can either go through this medium article for more details
OR
You can see the sample project on github