I'm trying to create a image Gallery using ViewPager. I've found several examples and I've got them all to work. The one thing the examples don't show, is how to populate the ViewPager using downloaded images from an URL. All the tutorials either have the images in the Drawables folder or on the SD card. Does anyone know of an example where a ViewPager gets the images from a URL. I've found examples of Lazy Loading a GridView and a ListView, but I'm not smart enough to be able to convert those into a working ViewPager. I mainly run into issues with the Adapter classes. The Lazy Loading examples seem to use BaseAdapter and I think a ViewPager needs to use PagerAdapter right? So, are there any examples of a ViewPager that gets the images from a URL? What I need the ViewPager to do is to download the images from a String Array something like this:
String[] imageGalleryStr = {
"http://www.website.com/images/image01.png",
"http://www.website.com/images/image02.png",
"http://www.website.com/images/image03.png",
"http://www.website.com/images/image04.png",
"http://www.website.com/images/image25.png",
"http://www.website.com/images/image26.png",
"http://www.website.com/images/image27.png",
"http://www.website.com/images/image28.png" };
Any ideas?
use this library for image galary https://github.com/nostra13/Android-Universal-Image-Loader
I haven't tried this code but hopefully something like this should work for you. :)
#Override
public Object instantiateItem(final ViewGroup container, final int position) {
tagShown = false;
View view = ((LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate(R.layout.image_view_item, container, false);
imageView = (ImageView) view.findViewById(R.id.imageView);
new DownloadImageInBackground(imageView).execute(imageGalleryStr[position]);
((ViewPager) container).addView(view);
return view;
}
...
private class DownloadImageInBackground extends AsyncTask<String, Void, Bitmap>{
ImageView iv;
public DownloadImageInBackground(ImageView iv) {
this.iv = iv;
}
#Override
protected Bitmap doInBackground(String... params) {
// download bitmap from string url
return bitmap;
}
#Override
protected void onPostExecute(Bitmap result) {
super.onPostExecute(result);
iv.setImageBitmap(result);
}
}
I would recommend you use the Volley library, the same library used by Google in its application Google Play. Please check this answer: Getting Bitmap images from server to set on ImageView
If you already have a working template (ViewPager) maybe you could choose one of the libs for getting images/bitmaps from across the net and then wrap a loader for images inside the Fragment type being operated on by the ViewPager...
When the pager calls 'onCreateView' on the type being paged, you have something like:
imgLoadr.DisplayImage(mMap.get("pic"), imgView);
Your implementation of 'DisplayImage' is built on one of the many libs for networked image management and includes the following details:
DisplayImage implementation looks first for a properly scaled bitmap in memory...
then it looks for a bitmap in local file system cache...
then it fetches the file from across the network and feeds the network stream to get instances of local bitmap/caches.
Your post only concerned the last item above, but IMO you may have to become interest in all of the list because that is just part of working with an image-centric app..
If you are going to be dealing with large album of photos, you probably will have to look at efficiently managing each bitmap and scaling the bitmaps in some way to cut your requirements for memory.
Related
I am doing an API request to Imgur using Retrofit in order to retrieve about 40 URLs and then display them in a RecyclerView using Glide like such:
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
Glide
.with(context)
.load(list.get(position).getLink()) // Gets the URL in my list
.centerCrop()
.placeholder(R.mipmap.placeholder)
.crossFade()
.into(holder.imageView);
}
The request gets answered quickly, but most of my images stay as placeholders as some of them appear one by one later on.
What is causing this delay in the display of my images? Is it perhaps linked to the speed of my Internet connection?
Additionally is my approach a correct one when it comes to "large" amounts of pictures?
Please note that most ImageViews do not load, even the ones that are visible to my user.
This might be depending on your internet connection. If so, you can use imgur smaller sizes, you can add one of those letter at the end of your filename:
s = Small Square (90×90)
b = Big Square (160×160)
t = Small Thumbnail (160×160)
m = Medium Thumbnail (320×320)
l = Large Thumbnail (640×640)
h = Huge Thumbnail (1024×1024)
i.e. this:
http://i.imgur.com/bF8zPs4.jpg
becomes this (for the small square):
http://i.imgur.com/bF8zPs4s.jpg
Once you've set the url for the image in Glide, the image itself still needs to be downloaded and shown in the ImageView, which is causing the delay. If the images are not loading, can you check if the image url loads the image in your browser for example?
A better approach to load images in an adapter with Glide is to use a RequestManager which you pass in the constructor of your adapter. Glide will then subscribe to the lifecycle of your activity of fragment. This will prevent images from being downloaded when your activity of fragment has been destroyed, which is especially useful when Glide has to download large images.
public MyAdapter(List<Object> items, RequestManager requestManager) {
this.items = items;
this.requestManager = requestManager;
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
requestManager
.load(list.get(position).getLink()) // Gets the URL in my list
.centerCrop()
.placeholder(R.mipmap.placeholder)
.crossFade()
.into(holder.imageView);
}
And then you can call the constructor of your adapter in an acitivty or a fragment as such:
MyAdapter myAdapter = new MyAdapter(items, Glide.with(this));
I've been using this approach after I've found this answer from TWiStErRob.
it is depend on your internet connection speed and the size of images.
if the size of images it to large,it may cause of of Memory exception.
In Recyclerview, Glide only loads images in visible imageViews. ImageViews that are not visible will be loaded once you scrolldown. This is due to recycler property of RecyclerView.
Add following line in you onBindViewHolder and see the logs, you will understand it:
Log.w("Link", list.get(position).getLink());
I'm trying to display an animated GIF picture in Widget, and I have URL for the picture. I know I can use WebView in activity with webView.loadUrl("http://my_url_here.gif"), but WebView is not supported in Android Widget.
I've already had URL for the picture, and I'm finding some ways not using third party libraries.
Does anyone know how to display it in Widget?
Where is no way to show GIF animation on RemoteViews. You can use only widgets that describes in official documentation
A bit late to the party, but I will post this for future references who want to get a widget animated.
I think this is probably what you want to achieve:
https://media.giphy.com/media/Rb7FAFdrgHr51V6zjy/giphy.gif
Fundamentally, GIF files "allow images or frames to be combined, creating basic animations", any GIF is a collection of images with continuous updates/refreshes to achieve the animated effect.
Hence, even though widgets on Android are RemoteViews that only support basic Text and Image, but if you can downsample a GIF into a series of PNGs or Bitmaps, you can achieve the animation effect by creating an async background task that updates the image bitmap or src of the Widget's RemoteView.
There are great answers on SO for converting Gifs into a collection of PNGs:
Using Glide, how can I go over each frame of GifDrawable, as Bitmap?
public class GifPlayer extends AsyncTask<String, Void, List<Bitmap>> {
#Override
protected List<Bitmap> doInBackground(String... params) {
try {
// load Gif images
InputStream in = new java.net.URL(url).openStream();
//... use Glide to convert Gifs into PNGs and save it in your local file system.
List<Bitmap> gifs = ...
} catch (Exception e) {
}
return null;
}
#Override
protected void onPostExecute(List<Bitmap> pngArray) {
while(true) {
for(Bitmap png : pngArray){
views.setImageViewBitmap(R.id.new_widget_image, png);
WidgetManager.updateAppWidget(WidgetID, views);
Thread.sleep(500); // higher lower this number depends on how fast you want to animate
}
}
Not to mention, Android Widgets have a small memory limit, so the above code works, but it will run out of memory for larger GIFs.
In general, RemoteViews.setImageViewBitmap(viewId, bitmap) should only be used for Widgets that don't update frequently.
For frequent updates of Widget such as animating a GIF, always use setImageViewResource(viewId, resId) instead (save frames of a GIF as PNG files identified by consecutive IDs such as gif_1.png, gif_2.png, gif_3.png...)
and replace the above code by:
//...
#Override
protected void onPostExecute(List<Integer> pngArray) {
while(true) {
for(int id : pngArray){
views.setImageViewResource(R.id.new_widget_image, id);
WidgetManager.updateAppWidget(WidgetID, views);
Thread.sleep(500); // higher lower this number depends on how fast you want to animate
}
}
You can use the gifView library :https://github.com/koral--/android-gif-drawable
use this is your .xml:-
<pl.droidsonroids.gif.GifImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="#drawable/src_anim"
/>
where src_anim is your loader gif file
You can use a load of different image loading libraries for this, but I recommend Fresco. They have great built-in GIF support detailed on this page which looks like the following in code:
Uri uri = Uri.parse("http://my_url_here.gif");
DraweeController controller = Fresco.newDraweeControllerBuilder()
.setUri(uri)
.setAutoPlayAnimations(true)
. // other setters
.build();
mSimpleDraweeView.setController(controller);
update
I never really understand where this need to not use libraries stems from, but you could just manually copy across all the code classes to your app then! The code in these libraries is battle tested and will work much more reliably than most code you decide to write alone.
That being said, there's a useful gist I've used in the past that may be what you're looking for.
I have a List View of images.
If no image exists in the local cache, the placeholder image will remain in place of the image.
Then an asynchronous task is run to download the image from a server.
Once the async task finishes I set the imageView bitmap to the downloaded image.
Then I call notifyDataSetChanged()
The problem is that I need to scroll down and scroll back up for each cell to show the new image that has been downloaded. It doesn't update the cells automatically.
So I have:
public class ImageLoadTask extends AsyncTask<Void, Void, Bitmap> {
private ImageView imageView;
public ImageLoadTask(ImageView imageView) {
this.imageView = imageView;
}
#Override
protected Bitmap doInBackground(Void... params) {
// download image from server and return
}
#Override
protected void onPostExecute(Bitmap result) {
super.onPostExecute(result);
imageView.setImageBitmap(result)
notifyDataSetChanged()
}
}
and in my getView() method I have:
if(image exists in cache){
myImageView.setBitmap(image in Cache);
}else{
new ImageLoadTask(myImageView).execute()
}
So any images that were stored in the Cache work fine.
Any images that have to be downloaded first will not update correctly, unless you scroll down and back up again.
What is the problem?
I believe you are doing thigs which have been already done and are working rly fine.
I advise you to use library for loading images with listview. One of these is picasso Picasso Android library. This library provides downloading and caching, and can easily (with one line) display them to imageview. Also it provides possibility for placeholder image.
Picasso.with(context)
.load(url)
.resize(50, 50)
.centerCrop()
.into(imageView)
Here is another question on stackoveflow:
Using Picasso library with ListView
EDIT:
If you use picasso you will ALSO solve problem with images wrong displaying while scrolling. This happens if you are loading/downloading them in AsyncTask and you are not doing it correctly.
I was answering question about this problem few daays ago: Why are wrong images getting loaded in ListView occasionally?
Firstly, if you are using the ImageView object directly, then you don't need to call notifyDataSetChanged.
Secondly, what you need to ensure is that the ImageView object you are passing still exists for the same URL requested, i.e., as you must know that Android will re-use views and might be if in between you had scrolled the ImageView object for which you had requested for an URL would have changed to some other URL.
I have worked on a very similar scenario, where I had a separate design like Adapter -> ImageViewRequest(This class will be used to post request, and this class maintains a map of ImageView and URL. So for every request entry is done to the map, and for every response we check that the response for the URL is the current requested URL for the entry of that ImageView and then set accordingly) -> AsyncTask
And also ensure that while setting the bitmap you do that inside a runOnUIThread task to ensure that the image is properly loaded, otherwise you might get a crash saying you are setting in a wrong thread.
Hope that helps :)
I want to download all images from server and store it in a cache.In most of cases, downloaded images are directly bound to the imageView(e.g. Picasso). I want to use same functionalities in android which is provided by SDWebImage("https://github.com/rs/SDWebImage").
Shutterbug is a good library for this purpose (https://github.com/applidium/Shutterbug). I've used this myself in a few apps and it works well. You can either use the Shutterbug FetchableImageView (which extends ImageView and you give it a URL rather than a bitmap or resource ID and it will display the image when download is complete) or you can use the ShutterbugManager which allows you to control what happens after downloading and caching. All the caching is done for you, it's pretty awesome :)
There are good instructions on the GitHub page's README on how to use both, and there is a demo project too.
EDIT: Just noticed your comment. If you don't want to bind to an ImageView, you can use the ShutterbugManager to download the image and then do whatever with it. Any subsequent calls to the download method will fetch the image from the cache rather than doing an HTTP call again.
public void downloadImage(Context context, String url) {
ShutterbugManager.getSharedImageManager(context).download(url, new ShutterbugManager.ShutterbugManagerListener() {
#Override
public void onImageSuccess(ShutterbugManager manager, Bitmap bitmap, String arg2) {
// Do what you like with the Bitmap here.
}
#Override
public void onImageFailure(ShutterbugManager arg0, String arg1) {
// Failure :(
}
});
}
In my Android Activity, I want to display about 10 images from web. Normally, I have to create 10 ImageView to display each image, but ImageView may cause Out of memory exception. So I decide to use 10 WebView to display image. It worked great.
But I don't know how performance of WebView, can anybody advise me how performance of 10 WebViews and Can I use this method to display images?
WebView occupies more space in memory then image view . you won't get proper scaling in webView just by simply displaying images. Webview can be faster in popping image to Ui but its not the efficient way .
You can try Volly which has in build tool for image downloading,caching and scaling and i can insure you its quite fast also.
All you need to do is add the Volley library to your project and replace ImageView in your XML layout with com.android.volley.toolbox.NetworkImageView.
Add the following variables to your Activity class:
private RequestQueue mRequestQueue;
private ImageLoader mImageLoader;
Create the objects in the onCreate() method of your Activity:
mRequestQueue = Volley.newRequestQueue(context);
mImageLoader = new ImageLoader(mRequestQueue, new ImageLoader.ImageCache() {
private final LruCache<String, Bitmap> mCache = new LruCache<String, Bitmap>(10);
public void putBitmap(String url, Bitmap bitmap) {
mCache.put(url, bitmap);
}
public Bitmap getBitmap(String url) {
return mCache.get(url);
}
});
Then download the image the getView() method of your Adapter class:
NetworkImageView image = (NetworkImageView)view.findViewById(R.id.image);
image.setImageUrl("http://someurl.com/someimage.png",mImageLoader);
In production code, you would use a global instance of both the RequestQueue and ImageLoader classes, and your onCreate() method wouldn't be cluttered as it is in this toy example.
I wouldn't use a webview to display images, though of course it can be done. If you really want to see which way is "faster", you can try out ImageView, NetworkImageView and WebView to load a large image and get a rough time estimate with the System.nanoTime() method.
Do you display 10 images at the same time ?
If yes, the size of the image view must be relatively small, so you can use small version of your images and avoid out of memory errors (or download the real size but create in memory a small version).
If not, you should use a ListView or GridView or ViewPager or something that recycle the views. You only get a couple of them in memory at the same time and avoid out of memory errors.
Furthermore, there are excellent libraries to display download and display images, for instance Universal Image Loader will do everything in the background, resize the image to the required size, you can activate the memory cache / disk cache, etc.