Android ImageView - Load Image from URL [closed] - android

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
I have a number of "contact" objects each with an imageURL String associated with them. All the ways I've seen of putting images into a ListView involve manually putting images into a "drawable" folder and calling resources. Manually entering the images in would defeat the purpose of this. I've provided my getView method and the commented out line is the one I'm confused about.
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View row = inflater.inflate(R.layout.single_row, parent, false);
TextView name = (TextView) row.findViewById(R.id.topLine);
TextView phone = (TextView) row.findViewById(R.id.secondLine);
ImageView icon = (ImageView) row.findViewById(R.id.icon);
name.setText(contactArray.get(position).getName());
phone.setText((CharSequence) contactArray.get(position).getPhone().getWorkPhone());
//icon.setImage from contactArray.get(position).getImageURL(); ????
return row;
}

While using listView you should load image asynchronously, otherwise your view will be freezes and case an ANR. Following is a complete code example which would load image asynchronously.
Create this class inside your custom adapter.
class ImageDownloader extends AsyncTask<String, Void, Bitmap> {
ImageView bmImage;
public ImageDownloader(ImageView bmImage) {
this.bmImage = bmImage;
}
protected Bitmap doInBackground(String... urls) {
String url = urls[0];
Bitmap mIcon = null;
try {
InputStream in = new java.net.URL(url).openStream();
mIcon = BitmapFactory.decodeStream(in);
} catch (Exception e) {
Log.e("Error", e.getMessage());
}
return mIcon;
}
protected void onPostExecute(Bitmap result) {
bmImage.setImageBitmap(result);
}
}
Now you can load the image very easily like following.
new ImageDownloader(imageView).execute("Image URL will go here");
Don't forget to add following permission into your project's Manifest.xml file
<uses-permission android:name="android.permission.INTERNET" />

Load Image from URL like this.
URL url = new URL(contactArray.get(position).getImageURL());
Bitmap bmp = BitmapFactory.decodeStream(url.openConnection().getInputStream());
icon.setImageBitmap(bmp);
Perhaps if you are looking for more comprehensive way and you have very large data set. I would recomend you to use Android-Universal-Image-Loader library.

Related

ListView Lagging when Scrolling Up with Images

My ListView jumps/freezes while scrolling up ( Scrolling down has no problem ) when I add images in, this doesn't happen when my keyboard is up.
I don't own the DownloadImageWithURLTask class, I cannot remember where I got it from.
public class chatAdapter extends ArrayAdapter<chatModel> {
private Context context;
public String userName = null;
public ImageView hold = null;
private static class ViewHolder{
TextView userName;
TextView userMessage;
ImageView userImage;
}
public chatAdapter(Context c, List<chatModel> items){
super(c, 0, items);
this.context = c;
}
class DownloadImageWithURLTask extends AsyncTask<String, Void, Bitmap> {
ImageView bmImage;
public DownloadImageWithURLTask(ImageView bmImage) {
this.bmImage = bmImage;
}
protected Bitmap doInBackground(String... urls) {
String pathToFile = urls[0];
Bitmap bitmap = null;
try {
InputStream in = new java.net.URL(pathToFile).openStream();
bitmap = BitmapFactory.decodeStream(in);
} catch (Exception e) {
Log.e("Error", e.getMessage());
e.printStackTrace();
}
return bitmap;
}
protected void onPostExecute(Bitmap result) {
bmImage.setImageBitmap(result);
}
}
#Override
public View getView(int position, View convertView, ViewGroup parent){
final chatModel chatModel = getItem(position);
final ViewHolder viewHolder;
if (convertView == null){
viewHolder = new ViewHolder();
LayoutInflater inflater = LayoutInflater.from(getContext());
convertView = inflater.inflate(R.layout.activity_chat_box, parent, false);
TextView userName = (TextView)convertView.findViewById(R.id.usern);
TextView userMessage = (TextView)convertView.findViewById(R.id.msg);
final ImageView userImage = (ImageView)convertView.findViewById(R.id.imageView8);
userName.setText(chatModel.userName);
userMessage.setText(chatModel.chatMessage);
final String userAvatarURL = "http://downtowndons.eu/Downtown/Avatar/" + chatModel.userName;
DownloadImageWithURLTask downloadTask = new DownloadImageWithURLTask(userImage);
downloadTask.execute(userAvatarURL);
} else {
viewHolder = (ViewHolder)convertView.getTag();
}
convertView.setAnimation(AnimationUtils.loadAnimation(context, R.anim.abc_slide_in_bottom));
return convertView;
}
}
If your using AsyncTask to load Image in imageView.While scrolling rapidly in listview you may had chance to get OutofMemory exception.In order to solve those type of exceptions you need to handle Memory cache mechanism for handling images.By default there are plenty of image loading libraries available in android.One of the easiest library is Universal Image Loader.
Use Universal image loader for downloading images asynchronously.
http://github.com/nostra13/Android-Universal-Image-Loader
The Library itself has a sample code to download image.you may refer it.. After downloading library add library with your project and insert the below code at necessary place
Write this code in adapter constructor
ImageLoader imageloader = ImageLoader.getInstance();
imageloader.init(ImageLoaderConfiguration.createDefault(context));
DisplayImageOptions options; = new DisplayImageOptions.Builder()
.showImageForEmptyUri(R.drawable.ic_empty)
.showImageOnFail(R.drawable.ic_error)
.resetViewBeforeLoading(true).cacheOnDisk(true)
.imageScaleType(ImageScaleType.EXACTLY)
.bitmapConfig(Bitmap.Config.RGB_565).considerExifParams(true)
.cacheInMemory(true)
.displayer(new FadeInBitmapDisplayer(300)).build();
Replace this code in getView instead of yours code.
if(convertView==null){
// ... other stuff
viewHolder.userName=(TextView)convertView.findViewById(R.id.usern);
viewHolder.userMessage=(TextView)convertView.findViewById(R.id.msg);
viewHolder.userImage= (ImageView)convertView.findViewById(R.id.imageView8);
}else{
viewHolder=(ViewHolder)convertView.getTag();
}
viewHolder.userName.setText(chatModel.userName);
viewHolder.userMessage.setText(chatModel.chatMessage);
final String userAvatarURL="http://downtowndons.eu/Downtown/Avatar/"+chatModel.userName;
imageloader.displayImage(userAvatarURL, viewHolder.userImage);
To fetch images from the net, try using one of these two libraries mentioned in this answer: https://stackoverflow.com/a/22862991/1018109
I'd strongly suggest following all answers and comments on this question: Lazy load of images in ListView
The ListView Freezes because it's trying to Re-Download the images again.
One on the best practices in this case is to cache image into memory using Android LruCache or Disk using DiskLruCache for efficiency..
Check this link out, it will help you a lot..
http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html#disk-cache
Initially, your problem is fetching and loading images on scroll. Also you don't store earlier downloaded images in cache.
To solve your downloading images problem use any image caching library
Universal Image Loader
Lazy List Loader
Volley Image Loader
Glide
Picasso
Go with any of them and your problem will be solved! I have mentioned example of each library with listview. Check it out!
I'm relatively new to this, but I'm lost with your code. It doesn't look like you're actually doing anything with your viewholder pattern. Your viewholder should be a quick way of getting your two textviews and one imageview every time they get updated, which is what speeds up scrolling for listview. However, you created it but didn't actually do anything with it. So the pattern should be something like:
if(convertView==null){
// ... other stuff
viewHolder.userName=(TextView)convertView.findViewById(R.id.usern);
viewHolder.userMessage=(TextView)convertView.findViewById(R.id.msg);
viewHolder.userImage=(ImageView)convertView.findViewById(R.id.imageView8);
}else{
viewHolder=(ViewHolder)convertView.getTag();
}
viewHolder.userName.setText(chatModel.userName);
viewHolder.userMessage.setText(chatModel.chatMessage);
final String userAvatarURL="http://downtowndons.eu/Downtown/Avatar/"+chatModel.userName;
DownloadImageWithURLTask downloadTask=new DownloadImageWithURLTask(viewHolder.userImage);
downloadTask.execute(userAvatarURL);

Set source of ImageView in custom ListView after list bind

I have a ListView binded with custom adapter and its working fine :
private class Placeslist extends ArrayAdapter<ArrayList<String>> {
private ArrayList<ArrayList<String>> items;
public Placeslist(Context context, int textViewResourceId,
ArrayList<ArrayList<String>> items) {
super(context, textViewResourceId, items);
this.items = items;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
if (v == null) {
LayoutInflater vi = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(R.layout.locationgpsrow, null);
}
ArrayList<String> o = items.get(position);
if (o != null) {
// Binding here
TextView tv1 = (TextView) v.findViewById(R.id.txt1);
tv1.setText(o.get(0));
ImageView imView = (ImageView) v.findViewById(R.id.imageView1);
//o.get(1) // Image url e.g. http://www.some-website/image.jpg
}
return v;
}
}
I have an image source url in one of my array elements and i need to download the image and set it to ImageView in the listview item custom layout. And, I have code for that too !!
ImageView imView = (ImageView) findViewById(R.id.imageView1);
HttpURLConnection conn = (HttpURLConnection) myFileUrl.openConnection();
conn.setDoInput(true);
conn.connect();
is = conn.getInputStream();
bmImg = BitmapFactory.decodeStream(is);
is.close();
imView.setImageBitmap(Bitmap.createScaledBitmap(bmImg, width,
height, true));
I can use it in my above getView() to set imageView source. But what i want is to let the list view bind to adapter without loading images(because it'll be heavy process). And then after listView is loaded then to initiate binding all the ImageViews in rows respectively. Is their a way to achieve this or any thing that can separate the image downloading process from listView binding ?
You have to use below lib to load image on run time :-
Universal loader lib
Picasso
Urlhelper
You can go for 3rd party libraries like universalimageloader and others for it.
Otherwise, if you want to handle it on your own, just create an asyncTask to download an image from url and set it to the imageView onPostExecute callBack. You can pass the parameters(ImageView img, String Url) to this AsyncTask's Constructor, download the image in DoinBackground() and finally set it to the img onPostExecute();
You can trigger this asyncTask from the getView() method from the Adapter, may be doing this on UI thread may be required.
I have used Picasso Image library which takes care of caching and downloading easily. Have a look here:
http://square.github.io/picasso/
Universal Image Loader also does this well, with a bit more options for configuration:
https://github.com/nostra13/Android-Universal-Image-Loader
You might want to look at using a library like this as it will save data, and not do as much network operations.
Use SmartImageView to load the url to images. http://loopj.com/android-smart-image-view/
Your need to read about Lazy Loading of listview where the images are loaded asynchronously. You can use any of the libraries available out there.
A good complete tutorial about listview and lazy loading :
http://www.androidhive.info/2012/02/android-custom-listview-with-image-and-text/

Load Bitmaps/images in ListView Adapter

I'm trying to add images in a ListView which has an ArrayAdapter. Fyi, the toList() is a conversion from iterator to a list of the given DBObject.
I override the View getView() and set a textview and an image.
private static class EventAdapter extends ArrayAdapter<DBObject> {
public EventAdapter(Context context, int resource, Iterable<DBObject> events) {
super(context, resource, toList(events));
}
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
LayoutInflater vi = LayoutInflater.from(getContext());
v = vi.inflate(R.layout.adapter_event_list, null);
DBObject event = getItem(position);
if (event != null) {
//Get the logo if any
if( ((DBObject)event.get("events")).containsField("logo") ){
String logoURL = ((DBObject)((DBObject)event.get("events")).get("logo")).get("0").toString();
ImageView eventLogo = (ImageView) v.findViewById(R.id.eventLogoList);
new setLogo().execute(logoURL, eventLogo);
}
TextView title= (TextView) v.findViewById(R.id.eventTitleList);
title.setText( ((DBObject)event.get("events")).get("title").toString() );
}
return v;
}
protected static <T> List<T> toList( Iterable<T> objects ) {
final ArrayList<T> list = new ArrayList<T>();
for( T t : objects ) list.add(t);
return list;
}
//setLogo() method here. See below
}
The text in the textview is fine. However the images are getting messed up. They seem to load in wrong places in the list. The route of the code is: 1)Get from the DB (async) 2)populate the ListView 3) while populating load each image(second async).
Here is the setLogo() AsyncTask which is inside the EventAdapter above:
private class setLogo extends AsyncTask<Object,Void,Bitmap>{
ImageView eventLogo = null;
#Override
protected Bitmap doInBackground(Object...params) {
try{
Bitmap eventImage = downloadBitmap((String) params[0]);
eventLogo = (ImageView) params[1];
return eventImage;
}
catch(Exception e){
e.printStackTrace();
return null;
}
}
#Override
protected void onPostExecute(Bitmap eventImage) {
if(eventImage!=null && eventLogo!=null){
eventLogo.setImageBitmap(eventImage);
}
}
}
I did so (using an Async) which I believe is the correct way to load images from urls. I saw this post on multithreading and from which I borrowed the downloadBitmap() method.
As explained above the images are loaded in wrong places of the ListView. What can be a robust way to load them?
Also the idea to pass the v.findViewById(R.id.eventLogoList) inside the AsyncTask is that the program will distinguish each adapter's ImageView but it seems it doesn't.
Update
After following the problem that is causing this mix I found this SO question.
I altered my code in order to check if the if is causing the problem.
//Get the logo if any
if( ((DBObject)event.get("events")).containsField("logo") ){
String logoURL = ((DBObject)((DBObject)event.get("events")).get("logo")).get("0").toString();
ImageView eventLogo = (ImageView) row.findViewById(R.id.eventLogoList);
//new setLogo().execute(logoURL, eventLogo);
TextView title= (TextView) row.findViewById(R.id.eventTitleList);
title.setText( "Shit happens" );
}
Let's say I have 40 items. The Shit happens is set on the fields that a logo field exists. If I scroll down/up the order changes and the text gets messed up. It is because the stack created inside the loop is small than the maximum of the list..I guess... I am still struggling.
PS: I found this easy library to load images asynchronously instead of DYI stuff.
Update 2
I added an else with a static url. Because of the time it take to the image to load they are still misplaced.
I would really go for a good library like Picasso.
It will handle all the hard part for you and it's very well written.
http://square.github.io/picasso/

Android: How to set the image in this ListView?

How do i set the images in this layout?
The ListView will contain 3 of the above entries, each image will be downloaded in an AsyncTask (See Below) and the text will be filled in by a String array of preset Strings eg
String[] values = {"string one", "string two", "String three"};
I want to be able to first set the String content values of all 3 entries using the adapter below, then have AsyncTasks running in the background downloading and setting the icons for each entry.
The Strings are more important than the icons so i dont want the user to have to wait for each icon to download before the string is set.
I have a ListView Layout:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:paddingBottom="#dimen/small_8dp"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/small_8dp" >
<ImageView
android:id="#+id/logo"
android:layout_width="50dp"
android:layout_height="50dp"
android:contentDescription="#string/loading"
android:scaleType="fitXY"
android:src="#drawable/image" >
</ImageView>
<TextView
android:id="#+id/label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="#dimen/small_8dp"
android:text="#string/loading"
android:textSize="#dimen/medium_15dp" >
</TextView>
</LinearLayout>
Which is in a Layout:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/color_white"
android:orientation="vertical" >
<ListView
android:id="#android:id/list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:entries="#array/list_headlines">
</ListView>
</LinearLayout>
Ive been working with this custom adapter:
private class ArticleAdapter extends ArrayAdapter<String>{
private final Context context;
private final String[] values;
public ArticleAdapter(Context context, String[] values) {
super(context, R.layout.list_entry, values);
this.context=context;
this.values=values;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View rowView = inflater.inflate(R.layout.list_entry,parent,false);
TextView tv = (TextView) rowView.findViewById(R.id.label);
tv.setText(values[position]);
return rowView;
}
}
LoadThumbnail AsyncTask:
protected class LoadThumbnail extends AsyncTask<String, Void, String> {
private String url;
private boolean loaded; //if loaded set the bitmap image to whats downloaded
private Bitmap icon;
private int iconIndex;
public LoadThumbnail(int iconIndex, String url){
loaded = false;
this.url = url; //url of the icon to download
this.iconIndex=iconIndex; //Which icon in the listview were downloading
}
#Override
protected String doInBackground(String... params) {
Download download = new Download(url); //My Download Class
try {
icon = download.downloadImage(); //Returns A Bitmap image
loaded=true; //If no errors caught
} catch (Exceptions e) {
//Various Exception Handling Here
}
return null;
}
#Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
}
}
Can you tell me which functions i have to adapt to achieve this?
Thanks!
The easiest way to do this is to download your images to some Collection and then for the list to update when the downloads are complete by calling notifyDatasetChanged on the Adapter.
Before we get started - I don't know where your AsyncTask is located. If its a separate class you need to use an Interface to set up a call back to the place you're starting it from. If its an inner class within your Adaper you can do this localy.
What you're going to do is set up your Adapter to (1) check if the image you want is available (2) if not download the image and add it to a collection (3) when the image is downloaded refresh the list (or when all images are downloaded depening how long this list is).
This all happens when you set the content for a specific list item
if(logo != null){
if(imageCollection.contains(key){
logo.setImageBitmap(imageCollection.get(key));
} else {
thumbnailDownloader.execute;
}
}
If you're AsyncTask is an inner class witin your adapater then within onPostExecute you will (1) add the image to this collection (2) update the ListView using notifyDatasetChanged. If your AsyncTask is it's own class you would add the image to the collction in your callback and update the list from there as well.
For the collection it's easier if you use the LruCache built into android but you could use a HashMap or something else. Just depending on your implementation.
Firstly I'd suggest to give a reference of the ArticleAdapter to the AsyncTask.
Then create a Bitmap array or map in ArticleAdapter, and create an add(Bitmap bmp) method for the ArticleAdapter, which puts a bitmap object into the array/map.
Because you have reference in the AsyncTask, you can call the add() method in the onPostExecute() method, with the downloaded icon Bitmap.
Then you can call the ArticleAdapter's notifyDataSetChanged(), so it can refresh its views.
Of course your Adapter's getView should check its bitmap array/map, if the bitmap for the given key is already downloaded. if it's reference is null, then put a placeholder, if it is downloaded already, place the bitmap which is put in the array/map by the asynctask.
In getView(), you should start a new task to download the image for that list item. Pass a reference of the ImageView you want populated to the AsyncTask, and then set the source for that ImageView to the downloaded bitmap (icon) in onPostExecute().
This does get a little complicated as you will have to deal with Views being recycled by the ListView (by cancelling the tasks to download the images). I like to use this library for all my downloading and caching of images: http://square.github.io/picasso/
This answer is late but may be helpful for others.
In your code, if your AsyncTask<> is located as separate class then you can simply instantiate it with giving it ImageView in you adapter class, like
new LoadThumbnail(imageView).execute(url);
then in your LoadThumbnail class simply set bitmap in imageView.
i am writing this roughly, please make it as you want;
#Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View rowView = inflater.inflate(R.layout.list_entry,parent,false);
TextView tv = (TextView) rowView.findViewById(R.id.label);
ImageView imageView = (ImageView)rowView.findViewById(R.id.logo);
tv.setText(values[position]);
//Instantiate LoadThumbnail giving it imageView to set bitmap in.
new LoadThumbnail(imageView).execute(url);
return rowView;
}
Now in LoadThumbnail simply set bitmap in onPostExecute() method.
protected class LoadThumbnail extends AsyncTask<String, Void, Bitmap> {
private ImageView imageView;
public LoadThumbnail(ImageView imageView){
this.imageView = imageView;
}
#Override
protected String doInBackground(String... url) {
Bitmap icon = null;
Download download = new Download(url[0]); //My Download Class
try {
icon = download.downloadImage(); //Returns A Bitmap image
loaded=true; //If no errors caught
} catch (Exceptions e) {
//Various Exception Handling Here
}
return icon;
}
#Override
protected void onPostExecute(Bitmap icon) {
super.onPostExecute(icon);
//here set bitmap in imageView;
if(icon != null){
imageView.setImageBitmap(icon);
}
}
}

Why thumbnail images are set automatically in the ListView

I have a ListView with thumbnail images. All visible rows in the ListView don't have problem.
But for those new rows below the visible ones, even though I tried not to assign any images to those thumbnail ImageViews, images started from the first row are copied exactly the same order as in visible rows. I set breakpoints at those lines of codes assigning the images at thumbnail ImageViews, no breakpoints are hit but still get the images. What is the theory behind? And how can I stop assigning images automatically at the rows below the visible ones.
Thanks
EDIT1:
public View getView(int position, View convertView, ViewGroup parent) {
View vi=convertView;
ViewHolder viewHolder=new ViewHolder();
LayoutInflater inflater = (LayoutInflater) this.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if(vi==null){
vi = inflater.inflate(R.layout.list_row, parent, false);
viewHolder.id=(TextView)vi.findViewById(R.id.title);
viewHolder.thumbnailImage=(ImageView)vi.findViewById(R.id.list_image);
viewHolder.activationStatus = (TextView)vi.findViewById(R.id.activated);
//lazy load image
BitmapWorkerTask task = new BitmapWorkerTask(viewHolder.thumbnailImage);
//if beyond visible rows, position
//becomes zero again, at that time cnt is not zero
//so task is not executed, to prevent image assignment
//for rows below the visible ones
if(position == cnt){
String id = listIDs.get(position);
task.execute(id);
cnt++;
}else{
cnt = 0;
}
//Lazy image update
class BitmapWorkerTask extends AsyncTask<String, Void, Bitmap> {
private final WeakReference<ImageView> imageViewReference;
public BitmapWorkerTask(ImageView imageView) {
// Use a WeakReference to ensure the ImageView can be garbage collected
imageViewReference = new WeakReference<ImageView>(imageView);
}
// Decode image in background.
#Override
protected Bitmap doInBackground(String... params) {
Bitmap bitmap = null;
dbHelper.open();
byte[] img_bytes = dbHelper.getImagebyIDnumber(params[0]);
bitmap = BitmapFactory.decodeByteArray(img_bytes, 0, img_bytes.length);
dbHelper.close();
return bitmap;
}
// Once complete, see if ImageView is still around and set bitmap.
#Override
protected void onPostExecute(Bitmap bitmap) {
if (imageViewReference != null && bitmap != null) {
final ImageView imageView = imageViewReference.get();
if (imageView != null) {
imageView.setImageBitmap(bitmap);
}
}
}
}
Check your Layout. Perhaps you settet android:src = "#drawable..." by default for your images.
Configure your listView data in your adaper.
EDITED 22.08.
try to use this:
if(vi==null){
...
//findView by ID here;
...
vi.setTag(viewHolder);
} else {
viewHolder = ( ViewHolder ) vi.getTag();
}
//to do what you want here;
//setting values etc.
You can find good explanation 'how to work with ViewHolder' here ViewHolder Pattern
This is because ListView reuses views getting invisible for visible views. If you want to hide images, you need to do this explicitly in getView() method of your adapter.

Categories

Resources