#Override
public void onBindViewHolder(final ViewHolder holder ,int position) {
Glide.with(c)
.load(images.get(position))
.placeholder(R.mipmap.ic_launcher)
.into(holder.img);
holder.img.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
try{
String fileName = "bitmap.png";
FileOutputStream stream = c.openFileOutput(fileName,Context.MODE_PRIVATE);
Intent showBigPicture = new Intent(c,showBigPicture.class);
Bitmap bitmapImage = BitmapFactory.decodeFile(images.get(position));
bitmapImage.compress(Bitmap.CompressFormat.PNG,100,stream);
stream.close();
bitmapImage.recycle();
showBigPicture.putExtra("image",fileName);
c.startActivity(showBigPicture);
}catch (Exception e){
e.printStackTrace();
}
}
});
}
this is showing in logCat " Unable to decode stream: java.io.FileNotFoundException: android.support.v7.widget.AppCompatImageView{e22d977 V.ED..C. ...P.... 0,0-540,890 #7f0b0061 app:id/img}: open failed: ENOENT (No such file or directory)"
I believe you want to follow this answer on saving Bitmap images. I believe the reason you're getting a FileNotFoundException is because you're providing the URI to a file that doesn't exist yet to the decodeFile function that's quite possibly a URL from what I can tell. In short, to save a bitmap:
Create a new File(filename)
Decode file using getName on the File from step 1
Create FileOutputStream from File
Compress the bitmap image into the FileOutputStream
From what I can surmise from your question, it looks as though you're showing a images in a RecyclerView and when an image is clicked, you want to open another activity which shows a version of the full image. If that's close to your use-case, and you're using Glide, I would recommend taking advantage of its built-in automatic caching feature to reduce network calls instead of manually saving the file.
By default, disk and memory-based caching is enabled in Glide as long as the same filename, path, or URL are used to obtain the image on each Glide.load(...). If you'd like to manipulate how the caching occurs, use the DiskCacheStrategy enum to control that every time you load the image:
Glide.with(c)
.load(images.get(position))
.diskCacheStrategy(DiskCacheStrategy.SOURCE) # Will cache the source downloaded image before any transformations are applied
.placeholder(R.mipmap.ic_launcher)
.into(holder.img);
If you still want to save the file for other reasons, use a SimpleTarget instead of loading directly into your ImageView like so:
Glide.with(c)
.load(images.get(position))
.diskCacheStrategy(DiskCacheStrategy.SOURCE) # Will cache the source downloaded image before any transformations are applied
.placeholder(R.mipmap.ic_launcher)
.asBitmap()
.into(new SimpleTarget<GlideDrawable>() {
#Override
public void onResourceReady(Bitmap bitmap, GlideAnimation anim) {
holder.img.setImageDrawable(new BitmapDrawable(bitmap));
saveImage(bitmap); # This being an encapsulation of the steps outlined earlier
}
});
Related
I have recyclerview and in every row I have an image that I load with firebase storage. It seems like the image loading is affecting the scrolling performance of the recyclerView.
I am using glide to load the images that I got from firebase inside onBindViewHolder by calling imageLoadGlide method like this:
//Download image from firebase Storage and set gameImage("ImageView") image.
private void imageLoadGlide(DocumentSnapshot documentSnapshot, final QuestionsHolder questionsHolder) {
//for firebase storage
FirebaseStorage storage = FirebaseStorage.getInstance();
// Create a storage reference from our app
StorageReference storageRef = storage.getReference();
storageRef.child(documentSnapshot
.get("image").toString())
.getDownloadUrl()
.addOnSuccessListener(new OnSuccessListener<Uri>() {
#Override
public void onSuccess(Uri uri) {
//this part is loading image from url library
Glide
.with(context.getApplicationContext()) // pass Context
.load(uri)// pass the image url
.centerCrop() // optional scaletype
.crossFade() //optional - to enable image crossfading
.transform(new CircleTransform(context))//transfoms the imageView onto circle with the custon class CircleTransform
.into(questionsHolder.gameImage); // the ImageView to which the image is to be loaded
//stop the loading bar from spinning
questionsHolder.loadProgress.setVisibility(View.GONE);
}
});
}
The download works fine but it makes the scrolling super slow.
I have no idea why this is happening because I compress the images before uploading them so I don't think that this is a problem of image weight.
The image compress is made like this :
Bitmap bitmap = ((BitmapDrawable) questionImage.getDrawable()).getBitmap();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 10, baos);
byte[] data = baos.toByteArray();
UploadTask uploadTask = mountainImagesRef.putBytes(data);
Any ideas on why this is happening and how can I avoid this?
Here is onBindViewHolder for #Vadim Eksler request:
#Override
public void onBindViewHolder(#NonNull final QuestionsHolder holder, final int position) {
holder.gameImage.setImageBitmap(null);
setInfoForViews(holder, result.get(position));
imageLoadGlide(result.get(position), holder);
setOnClicksListners(holder, position);
setRankTextView(position, holder);
if (position == result.size() - 1 && !endOfDocs && next != null) {
loadMoreDocs(position);
}
}
This answer is based on all the comments together.
So as mentioned in the comments what I did wrong is that every time that onBindViewHolder was called I have pulled the image from firebase again and again - this causes poorly recyclerView performance.
What I did to fix it:
I have used flyweight design pattern to load the image only for the first time from firebase and after that simply recycle this image for the next time onBindViewHolder will get called.
First I created a map as a global variable:
private Map<String, Bitmap> flyweight = new HashMap<>();
after that when the image is loaded from the first time I will save it for later when onBindViewHolder will get called again:
Inside onBindViewHolder :
if (flyweight.get(result.get(position).get("image").toString()) == null) {
imageLoadGlide(result.get(position), holder); // load the image
} else {
//recycle the image that i already have
holder.gameImage.setImageBitmap(flyweight.get(result.get(position).get("image").toString()));
}
And the last thing is to add my image to the map that I created after the image is successfully pulled :
flyweight.put(documentSnapshot.get("image").toString(), resource);
I'm workin with Volley API in my WebServices, and then I'll write the data in with SQLITE.
Webservices that comes with a JSON with many itens, each of then has data and a image, and I need to keep this image in my cache to display offline in a ListView and later on a detail screen. In the future the user will clean these itens and clear the imagens of them too.
Well, how can I save these images in my local DB and link with each Item that I have from the JSON?
I will be, in 90% of the time, offline. I will only stay online for synchronizing and download the updated server items.
For dealing with images in Android the benchmark is to use Picasso library. It takes care of:
Handling ImageView recycling and download cancelation in an adapter;
Complex image transformations with minimal memory use;
Automatic memory and disk caching.
Besides that, if you're going to display images in lists and need animation to enhance your UI, I strongly recommend changing from ListView to RecyclerView. It is not recommended to store the images in the DB, you would lose time converting this from/to blobs (check here and here). Said that, what I suggest is:
Use Picasso to load the images from the URL provided in the JSON;
Implement a Custom target to handle the image file downloaded by Picasso;
Save the image in a folder inside your app directory;
Use RecyclerView to display the images; (optional)
If you need a project example where those things are done, you can check this. In this project I follow the approache I've described above. You can download the app from the store and see how it will download the images.
Quicky-guide:
To use Picasso add this to you module's gradle file: compile 'com.squareup.picasso:picasso:2.5.2'
Create a class that implements import com.squareup.picasso.Callback;:
public class ImageWarehouse implements Callback {
private static final String TAG = "ImageWarehouse";
private String mDirectory;
private String mFileName;
private ImageView mContainer;
#Inject
App mApplication;
public ImageWarehouse(String fileName, ImageView container, String directory) {
this.mFileName = fileName;
this.mContainer = container;
this.mDirectory = directory;
this.getStorageDir();
}
#Override
public void onSuccess() {
if (this.isExternalStorageWritable()) {
final Bitmap bitmap = ((BitmapDrawable) this.mContainer.getDrawable()).getBitmap();
new AsyncTask<Void, Void, File>() {
#Override
protected File doInBackground(Void... params) {
File file = null;
try {
file = new File(ImageWarehouse.this.getStorageDir().getPath().concat("/").concat(ImageWarehouse.this.mFileName.concat(Constants.MEDIA_EXTENSION)));
file.createNewFile();
FileOutputStream ostream = new FileOutputStream(file);
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, ostream);
ostream.close();
} catch (Exception e) {
Log.e(TAG, "External Storage is not available");
}
return file;
}
}.execute();
} else {
Log.e(TAG, "External Storage is not available");
}
}
#Override
public void onError() {
}
public boolean isExternalStorageWritable() {
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
return true;
}
return false;
}
public File getStorageDir() {
File file = new File(Environment.getExternalStorageDirectory(), Constants.MEDIA_DIRECTORY.concat(this.mDirectory));
if (!file.mkdirs()) {
}
return file;
}
}
Call Picasso in order to display the image in the layout and save it to the specified path:
Picasso
.load(URL)
.fit()
.centerCrop()
.into(viewHolder.cover,
new ImageWarehouse(
name,
viewHolder.cover,
Constants.MEDIA_CHARACTER
)
);
You can user Android-Universal-Image-Loader it is also cache image in memory. it is show from cache when next time same url used in imageloader for get image
see below link for complete source code of Universal Image Loader Example.
Android - Universal Image Loader
You can save your images in Sqlite as blob and can retrieve it when required. Create table as
create table sometable(id integer primary key autoincrement,photo BLOB);
And save the image as
//convert image into byte[]
ByteArrayOutputStream baos = new ByteArrayOutputStream();
Bitmap bitmap = ((BitmapDrawable)getResources().getDrawable(R.drawable.common)).getBitmap();
bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos);
byte[] photo = baos.toByteArray();
//And now store this image
ContentValues initialValues = new ContentValues();
initialValues.put("photo",photo);
return db.insert("sometable", null, initialValues);
And retrieve image as
Cursor cur=your query;
while(cur.moveToNext())
{
byte[] photo=cur.getBlob(index of blob cloumn);
}
I have many images that I want to preload and use later on. I want to make it so that I can load an image, hide it, and then load it again whenever I want.
https://github.com/bumptech/glide/wiki/Loading-and-Caching-on-Background-Threads#into
I have tried following this, but it doesn't work.
Here is my code:
private void loadImage() throws ExecutionException, InterruptedException {
FutureTarget<File> future = Glide.with(this)
.load(R.drawable.image1)
.downloadOnly(500, 500);
File cacheFile = future.get();
Bitmap myBitmap = Glide.with(this)
.load(cacheFile)
.asBitmap()
.centerCrop()
.into(500, 500)
.get();
Glide.with(this).load(myBitmap).into(image_name);
}
I tried using Picasso because I read that this can easily be done in it, but the images take really long to load in Picasso.
I have spent a lot of time trolling through multiple different threads concerning this topic, but I have yet to find an answer that works well with my code (Android SDK 23, in 2016). A lot of the answers are deprecated, and others just flat-out don't work like they're supposed to, and I was wondering if I could get a solid answer on this:
I am trying to include a Pokemon sprite (static image) in my program from Serebii. nums is a variable indicating the Pokemon's dex number (this one functions correctly, I promise). And this code is running in the main UI thread, which I know is frowned upon, but right now I'm trying to get the image loading, and then the smoothness of the app down. I don't really need a Bitmap, per se, but I need my ImageView to update and display the image given by the URL. How do I do it?
URL url = null;
try {
url = new URL("http://www.serebii.net/xy/pokemon/" + nums + ".png");
} catch (MalformedURLException e) {
e.printStackTrace();
}
Bitmap bmp = BitmapFactory.decodeStream(url.openConnection().getInputStream());
mImageView.setImageBitmap(bmp);
Just use Picasso library, it will do all your image loading. You only need to provide the url of the image correctly.
String url = "http://www.serebii.net/xy/pokemon/" + nums + ".png";
Picasso.with(yourContext)
.load(url)
.into(mImageView);
You can use Picasso Library to load images.
a) Add Gradle into your project.
compile 'com.squareup.picasso:picasso:2.5.2'
b) Usage
Picasso.with(context).load("http://www.serebii.net/xy/pokemon/" + nums + ".png").into(imageView);
Also there are another libraries, you can use like
Fresco by Facebook, Universal Image loader
You can try using Picasso as:
Picasso.with(context)
.load(url)
.into(imageview);
Or use Universal Image loader as:
ImageLoader imageLoader = new ImageLoader(context);
imageLoader.displayImage(imageUri, imageView);
Use the Piassco library for it..Library link
For setting the Bitmap on the ImageView
Picasso.with(getContext()).load("your url").into(new Target() {
#Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
//do what ever you want with your bitmap
imgView.setImageBitmap(loadedImage);///imgView is use to set the image in it
}
#Override
public void onBitmapFailed(Drawable errorDrawable) {
}
#Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
}
});
And another method of Picasso :-
Picasso.with(this)
.load("your_image_url")
.placeholder(R.drawable.no_image)
.error(android.R.drawable.stat_notify_error)
.networkPolicy(NetworkPolicy.OFFLINE)//user this for offline support
.into(YOUR_IMAEGVIEW);
OR
You can also use the Universal Image Loader..Here is the link Universal image loader
ImageLoader imageLoader = ImageLoader.getInstance(); // Get singleton instance
ImageLoader.getInstance().init(ImageLoaderConfiguration.createDefault(getActivity()));
imageLoader = com.nostra13.universalimageloader.core.ImageLoader.getInstance();
DisplayImageOptions options = new DisplayImageOptions.Builder()
.showImageOnLoading(R.drawable.YOUR_DRAWABLE)
.showImageForEmptyUri(R.drawable.YOUR_DRAWABLE)
.showImageOnFail(R.drawable.YOUR_DRAWABLE)
.cacheInMemory(true)
.cacheOnDisk(true)
.considerExifParams(true)
.build();
imageLoader.displayImage("your_image_url", YOUR_IMAGEVIEW, null);
I'm working on a school android project.
I need to have a download button which downloads a picture(when we have class)
And after display it in another activity(even in offline mode, and after quiting)
I've tried picasso, but I can't get it to save and use it in offline mode.
For you to support offline mode, You need to Save the image on your disk because when your cache is cleared, The image is cleared as well.
You can easily use Glide to Solve this, also storing on device and retrieving
You can Learn more about Glide here http://inthecheesefactory.com/blog/get-to-know-glide-recommended-by-google/en
/** Download the image using Glide **/
Bitmap theBitmap = null;
theBitmap = Glide.
with(YourActivity.this).
load("Url of your image").
asBitmap().
into(-1, -1).
get();
saveToInternalStorage(theBitmap, getApplicationContext(), "your preferred image name");
/** Save it on your device **/
public String saveToInternalStorage(Bitmap bitmapImage, Context context, String name){
ContextWrapper cw = new ContextWrapper(context);
// path to /data/data/yourapp/app_data/imageDir
String name_="foldername"; //Folder name in device android/data/
File directory = cw.getDir(name_, Context.MODE_PRIVATE);
// Create imageDir
File mypath=new File(directory,name);
FileOutputStream fos = null;
try {
fos = new FileOutputStream(mypath);
// Use the compress method on the BitMap object to write image to the OutputStream
bitmapImage.compress(Bitmap.CompressFormat.PNG, 100, fos);
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
Log.e("absolutepath ", directory.getAbsolutePath());
return directory.getAbsolutePath();
}
/** Method to retrieve image from your device **/
public Bitmap loadImageFromStorage(String path, String name)
{
Bitmap b;
String name_="foldername";
try {
File f=new File(path, name_);
b = BitmapFactory.decodeStream(new FileInputStream(f));
return b;
}
catch (FileNotFoundException e)
{
e.printStackTrace();
}
return null;
}
/** Retrieve your image from device and set to imageview **/
//Provide your image path and name of the image your previously used.
Bitmap b= loadImageFromStorage(String path, String name)
ImageView img=(ImageView)findViewById(R.id.your_image_id);
img.setImageBitmap(b);
Thanks to #Droidman :
How to download and save an image in Android
Of course you can perform downloading and managing images by yourself,
but if your project is quite complex already, there are a lot of
libraries around and you do not need to reinvent the wheel. I won't
post code this time since there are a lot of examples, but I'm going
to tell you about 2 most useful libraries (IMO) related to image
downloading.
1) Android Volley. A powerful networking library created by Google and
covered by official documentation. POST'ing or GET'ing data, images,
JSON - volley will manage it for you. Using volley just for image
downloading is a bit of an overkill in my opinion.
2) Picasso
Image downloading and caching, perfect for
ListView/GridView/RecyclerView. Apache 2.0 license.
3) Fresco
Quite a new image loading library created by Facebook. Progressive
JPEG streaming, gifs and more. Apache 2.0
You could use Android Library called Universal Image Loader:
https://github.com/nostra13/Android-Universal-Image-Loader