I have a BackgroundImageActivity that load background image using Picasso. But when I go back to home (onStop() called in BackgroundImageActivity) and go into another instance of this activity, which is supposed to load another background image, but at first 2 seconds, it still shows the image from previous BackgroundImageActivity. Is this some kind of cache?
How I can clear this image so that whenever I go into a new instance of BackgroundImageActivity, i don't see the image from previous one?
public class BackgroundImageActivity extends Activity {
#Override
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initBackground();
}
#Override
protected void onStop() {
super.onStop();
Picasso.invalidate(imageUrl, getApplicationContext());
}
private void initBackground() {
...
}
private void setBg(final String imageUrl, final int bg) {
this.imageUrl = imageUrl;
final RequestCreator picassoRequest = Picasso.load(imageUrl, bg).memoryPolicy(MemoryPolicy.NO_CACHE);
targetReference = new Target() {
#Override
public void onBitmapLoaded(final Bitmap bitmap, final Picasso.LoadedFrom from) {
...
}
#Override
public void onBitmapFailed(final Drawable errorDrawable) {
...
}
#Override
public void onPrepareLoad(final Drawable placeHolderDrawable) {
...
}
};
picassoRequest.placeholder(bg).error(bg).into(targetReference);
}
}
Thanks!!!
Use this piece of code while loading the image.
Picasso.with(getContext())
.load(data.get(pos)
.getFeed_thumb_image())
.memoryPolicy(MemoryPolicy.NO_CACHE)
.into(image);
Picasso set the image to ImageView directly without caching in the background. This makes the app lightweight also.
Related
I'm trying to use an IntentService for processing and uploading images that's running in a different process to have more memory. I'm Using also Picasso to load the Image. When the Image is small the bitmap is loaded successfully and uploaded, however if the image is big the IntentService is terminated before Picasso is done loading It.
Picasso have to run on UIThread
Here is the code.
private void downloadImage(File file) {
final Uri uri = Uri.fromFile(file);
Handler uiHandler = new Handler(Looper.getMainLooper());
uiHandler.post(new Runnable() {
#Override
public void run() {
Picasso.with(NewImageProcessingService.this).load(uri).transform(new ImageLoadingUtil.DecreaseQualityTransformation(imageQuality)).into(NewImageProcessingService.this);
}
});
}
#Override
protected void onHandleIntent(Intent intent) {
File file = (File) intent.getSerializableExtra(KEY_IMAGE_FILE);
imageQuality = ImagesUtils.IMAGE_QUALITY
.values()[intent.getIntExtra(IMAGE_QUALITY, ImagesUtils.IMAGE_QUALITY.DEFAULT.ordinal())];
downloadImage(file);
}
This question is quite old, but if anyone steps by. The Target is getting garbage collected before it can show the bitmap.
Use it like this
public class BitmapLoader {
public static Target getViewTarget(final OnImageLoadingCompleted onCompleted) {
return new Target() {
#Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
onCompleted.imageLoadingCompleted(bitmap);
}
#Override
public void onBitmapFailed(Drawable errorDrawable) {
}
#Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
}
};
}
}
You need to have a strong reference to the Target so have a field in your IntentService holding it e.g.
private Target viewTarget;
viewTarget = BitmapLoader.getViewTarget(bitmap -> {
// do stuff with the bitmap
});
new Handler(Looper.getMainLooper()).post(() -> Picasso.with(getApplicationContext()).load(object.getImageUrl()).into(viewTarget));
I am trying to pass a URL to be converted to a bitmap to be used in the activity. I have a class of "Projects" and everything loads fine from the API, including the String Url for the image.
My code looks like this:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_discover);
ButterKnife.bind(this);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
target = new Target() {
#Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
adapter.add(new CardModel(mProject.mName, mProject.mTagline, bitmap));
}
#Override
public void onBitmapFailed(Drawable errorDrawable) {
}
#Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
Log.d("ISSUE:: ", "IMAGE NOT LOADED");
}
};
adapter = new SimpleCardStackAdapter(this);
mController = new Controller(this);
mController.startFetchingProjects();
}
#Override
public void onFetchProgress(Project project) {
mProjectList.add(project);
mImageUrls.add(project.mImageUrl);
}
#Override
public void onFetchComplete() {
for (int i = 0; i < mProjectList.size(); i++){
mProject = mProjectList.get(i);
mImage = mImageUrls.get(i);
Picasso.with(this).load(mImage).into(target);
}
}
}
For some reason even though there are 24 projects, when I try to load an image, it returns only about 8 random images (the rest go through onPrepareLoad for some reason). If I adapt my projects with no images, I'll have 24 projects, but when I include this picasso method, I only get about 8 images returned, and thus only 8 projects returned. Any idea how to fix this?
Thanks for all the effort #Budius has made.
Most part of image work of my app could be handled by Picasso/Glide, however, some images are displayed in a TextView by Html.fromHtml. And the images in TextView are also used frequently.
However, I don't know how to implement getDrawable() method with Picasso/Glide for the ImageGetter passed to Html.fromHtml. Is it possible to share the same cache of Picasso/Glide for these pictures in TextView and other bitmaps?
Or should I use an custom LruCache instead to cache these pictures form ImageGetter separately? Will this way increase the risk of an OOM error? And I think it creates unnecessary workload to use 2 different systems for processing images.
Update: I tried to use .get() of Picasso, but the doc says
/**
* The result of this operation is not cached in memory because the underlying
* {#link Cache} implementation is not guaranteed to be thread-safe.
*/
So the cache is not used in this case.
Update:
The answer of #Budius is right, but code of setting bounds for the Drawable is missing, which leaves the Drawable not displayed in the TextView. So I modified the code in the DrawableWrapper class into:
public void setWrappedDrawable(Drawable drawable) {
if (mDrawable != null) {
mDrawable.setCallback(null);
}
mDrawable = drawable;
if (drawable != null) {
mDrawable.setBounds(0,0,mDrawable.getIntrinsicWidth(),mDrawable.getIntrinsicHeight());
drawable.setCallback(this);
}
}
Update:, the problem is still unsolved. If you implement the solution forementioned, there are some strange behaviors for the image in TextView. Sometimes the image could not be refreshed unless you switch to another app and switch back, and the position of image is severely incorrect.
Update: I have post all the code for test below. There're still some bugs. Without a placeholder, it still throws an NPE. With a placeholder, the behavior is very strange. The first time I enter TestActivity, it shows the placeholder but it won't change into the downloaded pic. But after I switch to another app or press a back button and enter TestActivity again, the pic is displayed(maybe because it's in the cache?).
And also the size of pic is right but the place is still not left for the image. And if I call mDrawable.setBounds(getBounds()); instead of mDrawable.setBounds(0,0,getIntrinsicWidth(),getIntrinsicHeight());, it will not be displayed.
DrawableWrapper
public class DrawableWrapper extends Drawable implements Drawable.Callback {
private Drawable mDrawable;
public DrawableWrapper(Drawable drawable) {
setWrappedDrawable(drawable);
}
#Override
public void draw(Canvas canvas) {
mDrawable.draw(canvas);
}
#Override
public int getIntrinsicWidth() {
return 384;
}
#Override
public int getIntrinsicHeight() {
return 216;
}
//... other delegation methods are omitted
public void setWrappedDrawable(Drawable drawable) {
if (mDrawable != null) {
mDrawable.setCallback(null);
}
mDrawable = drawable;
if (drawable != null) {
mDrawable.setBounds(0,0,getIntrinsicWidth(),getIntrinsicHeight());
drawable.setCallback(this);
}
}
}
PicassoTargetDrawable
public class PicassoTargetDrawable extends DrawableWrapper
implements Target {
private Context context;
public PicassoTargetDrawable(Context context) {
super(new ColorDrawable(0));
// use application context to not leak activity
this.context = context.getApplicationContext();
}
public void onBitmapFailed(Drawable errorDrawable) {
setWrappedDrawable(errorDrawable);
invalidateSelf();
}
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
setWrappedDrawable(new BitmapDrawable(context.getResources(), bitmap));
context = null;
invalidateSelf();
}
public void onPrepareLoad(Drawable placeHolderDrawable) {
setWrappedDrawable(placeHolderDrawable);
invalidateSelf();
}
}
TestActivity
public class TestActivity extends FragmentActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
TextView textView = new TextView(this);
textView.setLayoutParams(new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT));
setContentView(textView);
String html = "<div>test<br/>" +
"<img src=\"http://i2.cdn.turner.com/money/dam/assets/150910165544-elon-evo-open-still-384x216.png\"></img>" +
"<br/>/test</div>";
textView.setText(Html.fromHtml(html, new Html.ImageGetter() {
#Override
public Drawable getDrawable(String source) {
PicassoTargetDrawable d = new PicassoTargetDrawable(TestActivity.this);
Picasso.with(TestActivity.this)
.load(source)
//add placeholder here
.into(d);
return d;
}
}, null));
}
}
My Suggestion is to return a wrap drawable. And keep using Picasso to download the image.
On the following link you can find an DrawableWrapper, it's from Googles support library, but it's not part of the public docs, so I would just copy the whole code into your project https://android.googlesource.com/platform/frameworks/support/+/master/v7/appcompat/src/android/support/v7/graphics/drawable/DrawableWrapper.java
And then you create a PicassoTargetDrawable from it.
public class PicassoTargetDrawable extends DrawableWrapper
implements Picasso.Target {
private Context context;
public PicassoTargetDrawable(Context context) {
super(new ColorDrawable(0));
// use application context to not leak activity
this.context = context.getApplicationContext();
}
public void onBitmapFailed(Drawable errorDrawable) {
setWrappedDrawable(errorDrawable);
invalidateSelf();
}
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
setWrappedDrawable(new BitmapDrawable(context.getResources(), bitmap));
context = null;
invalidateSelf();
}
public void onPrepareLoad(Drawable placeHolderDrawable) {
setWrappedDrawable(placeHolderDrawable);
invalidateSelf();
}
}
Then it's just a matter of loading it up
public void Drawable getDrawable(String source) {
PicassoTargetDrawable d = new PicassoTargetDrawable(context);
Picasso.with(context)
.load(source)
..... add here onError and placeholder drawables
.into(d);
return d;
}
PS.:
I wrote all this without looking up too much, there will probably be a few typos and a few issues to sort it out, but it's certainly enough for you to understand the concept.
update:
Just correcting your code.
The TextView already told the WrapDrawable the Bounds it should use. If you're telling the new mDrawable that it can use whatever size it wants, it will use whatever size it wants. So instead of passing its own intrinsic width/height, you should pass the size that was give to the WrapDrawable
public void setWrappedDrawable(Drawable drawable) {
if (mDrawable != null) {
mDrawable.setCallback(null);
}
mDrawable = drawable;
if (drawable != null) {
mDrawable.setBounds(getBounds());
drawable.setCallback(this);
}
}
I'm trying to load an url into an ImageView but Target doesn't seem to work like what I found told me. Below is my code:
ImageView methodButton= (ImageView) View.inflate(context, R.layout.view_home_scroll_view_image, null);
setMethodPicture(sectionModel.getContent().get(0).getThumbnail(), methodButton);
methodsContainer.addView(methodButton);
and
private void setMethodPicture(final String methodPicture, final ImageView methodButton){
methodButton.setBackgroundColor(0x000000);
if (!StringUtils.isEmpty(methodPicture)) {
Target target = new Target() {
#Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
methodButton.setImageBitmap(bitmap);
}
#Override
public void onBitmapFailed(Drawable errorDrawable) {
}
#Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
}
#Override
public boolean equals(Object o) {
return methodPicture.equals(o);
}
#Override
public int hashCode() {
return methodPicture.hashCode();
}
};
Picasso.with(context).load(methodPicture).into(target);
}
}
This doesn't load the picture to the imageView, but when i do this,
ImageView methodButton= (ImageView) View.inflate(context, R.layout.view_home_scroll_view_image, null);
methodButton.setBackgroundColor(0x000000);
Picasso.with(context).load(sectionModel.getContent().get(0).getThumbnail()).into(methodButton);
methodsContainer.addView(methodButton);
it loads the picture.
I want to do the first one so I can change the Bitmap I get before I put it in the ImageView, like changing the width and height, but basing on the original dimensions.
I found the issue, Picasso holds a Weak Reference to the Target. The correct answer can be found here: onBitmapLoaded of Target object not called on first load
Use Glide instead, it provide predefine constrain options which you can add with it
https://github.com/bumptech/glide
I want to load large bitmaps efficiently in my Image Switcher and i have been using Picasso for this purpose but now i am stuck at this point. Without Picasso a lot of OOMs and other nasty exceptions please tell me if I can use this library with Image Switcher too.
If yes then provide sample code.
Thanks!
imswitch.setFactory(new ViewFactory() {
#Override
public View makeView() {
ImageView imageView = new ImageView(getApplicationContext());
imageView.setScaleType(ImageView.ScaleType.FIT_CENTER);
imageView.setLayoutParams(new ImageSwitcher.LayoutParams(LayoutParams.MATCH_PARENT,LayoutParams.MATCH_PARENT));
return imageView;
}
} );
And onClick:
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
currentIndex++;
// If index reaches maximum reset it
if(currentIndex==messageCount)
currentIndex=0;
ImageView imageView = (ImageView)findViewById(R.id.imswitch);
Picasso.with(getApplicationContext()).load(imageIds[currentIndex]).into(imageView);
Toast.makeText(getApplicationContext(), "Pressed "+currentIndex,Toast.LENGTH_LONG).show();
}
One of the possible way is to create own implementation using Target interface.
https://square.github.io/picasso/javadoc/com/squareup/picasso/Target.html
public class ImageSwitcherPicasso implements Target {
private ImageSwitcher mImageSwitcher;
private Context mContext;
public ImageSwitcherPicasso(Context context, ImageSwitcher imageSwitcher){
mImageSwitcher = imageSwitcher;
mContext = context;
}
#Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom loadedFrom) {
mImageSwitcher.setImageDrawable(new BitmapDrawable(mContext.getResources(), bitmap));
}
#Override
public void onBitmapFailed(Drawable drawable) {
}
#Override
public void onPrepareLoad(Drawable drawable) {
}
}
Than just use as follow
ImageSwitcherPicasso mImageSwitcherPicasso = new ImageSwitcherPicasso(getActivity(), playerBackground);
Picasso.with(getActivity()).load(new File(path)).into(mImageSwitcherPicasso);
where playerBackground is reference to ImageSwitcher