开发者

which the smooth and fast way to load image from net in android list view!

i need to list out data from net in that i have 1 image and 2 text. i parse all data and display it but the image displaying very slow in list. so i m looking for best way to do this.

Plea开发者_高级运维se help me out.

Thanks In Advance


Please copy the below class . That class is download the images from the web and stores into the memory card or into internal memory of application .

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.util.HashMap;
import java.util.Stack;

import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.widget.ImageView;

public class Imageloader 
{
    // the simplest in-memory cache implementation. This should be replaced with
    // something like SoftReference or BitmapOptions.inPurgeable(since 1.6)

    private HashMap<String, Bitmap> cache = new HashMap<String, Bitmap>();

    private File cacheDir = null;
    private Bitmap  useThisBitmap = null;
    @SuppressWarnings("unused")
    private Context ctx = null;

    public Imageloader(Context context) 
    {
        // Make the background thead low priority. This way it will not affect
        // the UI performance

        ctx = context;
        photoLoaderThread.setPriority(Thread.NORM_PRIORITY - 1);

        // Find the dir to save cached images
        if (android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED))
            cacheDir = new File(android.os.Environment.getExternalStorageDirectory(),"DownloadImages/AlbumArt/");
        else
            cacheDir = context.getCacheDir();

        if (!cacheDir.exists())
            cacheDir.mkdirs();
    }

    public void DisplayImage(String url, Activity activity, ImageView imageView) 
    {
        if(!url.equals(""))
         {
             if (cache.containsKey(url))
             {
                 imageView.setImageBitmap(cache.get(url));
             }
             else
             {
                queuePhoto(url, activity, imageView);
            }
         }
    }

    private void queuePhoto(String url, Activity activity, ImageView imageView) 
    {
        // This ImageView may be used for other images before. So there may be
        // some old tasks in the queue. We need to discard them.

        photosQueue.Clean(imageView);
        PhotoToLoad p = new PhotoToLoad(url, imageView);

        synchronized (photosQueue.photosToLoad) 
        {
            photosQueue.photosToLoad.push(p);
            photosQueue.photosToLoad.notifyAll();
        }

        // start thread if it's not started yet
        if (photoLoaderThread.getState() == Thread.State.NEW)
            photoLoaderThread.start();
    }

    public Bitmap getBitmap(String url) 
    {
        try
        {
            // I identify images by hashcode. Not a perfect solution, good for the
            // demo.
            String filename = String.valueOf(url.hashCode());
            File f = new File(cacheDir, filename);

            // from SD cache
            Bitmap b = decodeFile(f);
            if (b != null)
                return b;

            // from web
            try {
                Bitmap bitmap = null;       
                if(!url.equals("")){
                InputStream is = new URL(url).openStream();
                OutputStream os = new FileOutputStream(f);
                Utils.CopyStream(is, os);
                os.close();
                bitmap = decodeFile(f);
                }
                return bitmap;
            } 
            catch (Exception ex) 
            {
                ex.printStackTrace();
            return null;
            }
        }
        catch(Exception e)
        {
            e.printStackTrace();

            return null;    
        }
    }

    /*decodes image and scales it to reduce memory consumption
     * @param file path
     * @throws FileNotFoundException
     * @return bitmap
     * */
    private Bitmap decodeFile(File f){
        Bitmap b = null;
        try {

            useThisBitmap = null;
            //Decode image size
            BitmapFactory.Options o = new BitmapFactory.Options();
            o.inJustDecodeBounds = true;
            final int IMAGE_MAX_SIZE = 70;
            BitmapFactory.decodeStream(new FileInputStream(f), null, o);
            int scale = 2;
            if (o.outHeight > IMAGE_MAX_SIZE || o.outWidth > IMAGE_MAX_SIZE) {
                scale = 2 ^ (int) Math.ceil(Math.log(IMAGE_MAX_SIZE / (double) Math.max(o.outHeight, o.outWidth)) / Math.log(0.5));
            }

            //Decode with inSampleSize
            BitmapFactory.Options o2 = new BitmapFactory.Options();

            o2.inSampleSize = scale;
            b = BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
            useThisBitmap = b;

        } 
        catch (FileNotFoundException e) {
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
        finally
        {
            System.gc();
        }
        return useThisBitmap;
    }


    // Task for the queue
    private class PhotoToLoad 
    {
        public String url;
        public ImageView imageView;

        public PhotoToLoad(String u, ImageView i) {
            url = u;
            imageView = i;
        }
    }

    private PhotosQueue photosQueue = new PhotosQueue();



    // stores list of photos to download
    private class PhotosQueue 
    {
        private Stack<PhotoToLoad> photosToLoad = new Stack<PhotoToLoad>();
        // removes all instances of this ImageView
        private void Clean(ImageView image) 
        {
            for (int j = 0; j < photosToLoad.size();) 
            {
                if (photosToLoad.get(j).imageView == image)
                    photosToLoad.remove(j);
                else
                    ++j;
            }
        }
    }

    private class PhotosLoader extends Thread 
    {
        public void run() 
        {
            try 
            {
                while (true) 
                {
                    // thread waits until there are any images to load in the
                    // queue
                    if (photosQueue.photosToLoad.size() == 0)
                        synchronized (photosQueue.photosToLoad) {
                            photosQueue.photosToLoad.wait();
                        }
                    if (photosQueue.photosToLoad.size() != 0) {
                        PhotoToLoad photoToLoad;
                        synchronized (photosQueue.photosToLoad) {
                            photoToLoad = photosQueue.photosToLoad.pop();
                        }
                        Bitmap bmp = getBitmap(photoToLoad.url);
                        cache.put(photoToLoad.url, bmp);
                        if (((String) photoToLoad.imageView.getTag())
                                .equals(photoToLoad.url)) {
                            BitmapDisplayer bd = new BitmapDisplayer(bmp,
                                    photoToLoad.imageView);
                            Activity a = (Activity) photoToLoad.imageView
                                    .getContext();
                            a.runOnUiThread(bd);
                        }
                    }
                    if (Thread.interrupted())
                        break;
                }
            } catch (InterruptedException e) {
                // allow thread to exit
            }
        }
    }

    private PhotosLoader photoLoaderThread = new PhotosLoader();

    // Used to display bitmap in the UI thread
    private class BitmapDisplayer implements Runnable 
    {
        private Bitmap bitmap;
        private ImageView imageView;

        private BitmapDisplayer(Bitmap b, ImageView i) 
        {
            bitmap = b;
            imageView = i;
        }

        public void run() 
        {
            if (bitmap != null)
                imageView.setImageBitmap(bitmap);
        }
    }

    public void stopThread() 
    {
        photoLoaderThread.interrupt();
    }

    public void clearCache() 
    {
        cache.clear();
        File[] files = cacheDir.listFiles();
        for (File f : files)
            f.delete();
    }
}

Example

First create your image Loader class object.

Imageloader imageLoader = new Imageloader(getApplicationContext());

then set the image url into the imageview's setTag Property.

imgImageView.setTag(Your Image Url);

then call your ImageLoader class Display image function . There are 3 parameters required.

1) Image Url

2) Your Current Class Name

3) ImageView

imageLoader.DisplayImage(Your Image Url,ClassName.this,imgImageView);

this functon dowload images from the web and stored into the memory and show from the memory.


You can download the whole text first, then the images only as they are displayed. Don't download the whole list of images, because maybe most of them are never going to be shown.

Try to go a step ahead from the user. For instance, if the list can display 6 images when you first enter the activity, have these 6 prefetched before you switch to this activity, and pass them via Intent. You can also have a thread that downloads the following (3 or 4) images that would be displayed if the user scrolls down.

To speed up the process, consider have the images pre-scaled, so their size is smaller and the download is faster.


Use this class instead of the normal imageview

package sherif.android.ui;

import java.io.IOException;
import java.net.MalformedURLException;

import sherif.caching.R;

import android.content.Context;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Message;
import android.os.Handler.Callback;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;

/**
 *
 * @author Sherif
 * thanks Blundell
 *
 */
public class ImageViewLoading extends LinearLayout{

    private static final int COMPLETE = 0;
    private static final int FAILED = 1;

    private Context mContext;
    private Drawable mDrawable;
    private ProgressBar mSpinner;
    private ImageView mImage;

    /**
     * This is used when creating the view in XML
     * To have an image load in XML use the tag 'image="http://developer.android.com/images/dialog_buttons.png"'
     * Replacing the url with your desired image
     * Once you have instantiated the XML view you can call
     * setImageDrawable(url) to change the image
     * @param context
     * @param attrSet
     */
    public ImageViewLoading(final Context context, final AttributeSet attrSet) {
            super(context, attrSet);
            final String url = attrSet.getAttributeValue(null, "image");
            if(url != null){
                    instantiate(context, url);
            } else {
                    instantiate(context, null);
            }
    }

    /**
     * This is used when creating the view programatically
     * Once you have instantiated the view you can call
     * setImageDrawable(url) to change the image
     * @param context the Activity context
     * @param imageUrl the Image URL you wish to load
     */
    //USE THIS TO ADD IMAGEVIEWS
    public ImageViewLoading(final Context context, final String imageUrl) {
            super(context);
            instantiate(context, imageUrl);         
    }

    /**
     *  First time loading of the LoaderImageView
     *  Sets up the LayoutParams of the view, you can change these to
     *  get the required effects you want
     */
    private void instantiate(final Context context, final String imageUrl) {
            mContext = context;

            mImage = new ImageView(mContext);
            mImage.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));

            mSpinner = new ProgressBar(mContext);
            mSpinner.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));

            mSpinner.setIndeterminate(true);

            //addView(mSpinner);
            //addView(mImage);

            if(imageUrl != null){
                    setImageDrawable(imageUrl);
            }
    }

    /**
     * Set's the view's drawable, this uses the internet to retrieve the image
     * don't forget to add the correct permissions to your manifest
     * @param imageUrl the url of the image you wish to load
     */
    public void setImageDrawable(final String imageUrl) {
            mDrawable = null;
            mSpinner.setVisibility(View.VISIBLE);
            mImage.setVisibility(View.GONE);
            new Thread(){
                    public void run() {
                            try {
                                    mDrawable = getDrawableFromUrl(imageUrl);
                                    imageLoadedHandler.sendEmptyMessage(COMPLETE);
                            } catch (MalformedURLException e) {
                                    imageLoadedHandler.sendEmptyMessage(FAILED);
                            } catch (IOException e) {
                                    imageLoadedHandler.sendEmptyMessage(FAILED);
                            }
                    };
            }.start();
    }

    /**
     * Callback that is received once the image has been downloaded
     */
    private final Handler imageLoadedHandler = new Handler(new Callback() {
            public boolean handleMessage(Message msg) {
                    switch (msg.what) {
                    case COMPLETE:
                            mImage.setImageDrawable(mDrawable);
                            mImage.setVisibility(View.VISIBLE);
                            mSpinner.setVisibility(View.GONE);
                            break;
                    case FAILED:
                    default:
                            mImage.setImageResource(R.drawable.failed);
                            mImage.setVisibility(View.VISIBLE);
                            mSpinner.setVisibility(View.GONE);
                            // Could change image here to a 'failed' image
                            // otherwise will just keep on spinning
                            break;
                    }
                    return true;
            }               
    });

    /**
     * Pass in an image url to get a drawable object
     * @return a drawable object
     * @throws IOException
     * @throws MalformedURLException
     */
    private static Drawable getDrawableFromUrl(final String url) throws IOException, MalformedURLException {
            return Drawable.createFromStream(((java.io.InputStream)new java.net.URL(url).getContent()), "name");
    }

}


Download and displaying image in a List is a bit complicated matter. Some points that you need to consider are:

  1. Using different thread to doing the image download, you can use android asynctask class for this, you read about it here
  2. Caching your image, there are several way to achieve this, caching in memory, internal cache storage, or external cache storage(SD Card), and please read here
  3. You can use Lazy Loading on displaying the Images, it's mean the apps doesn't download all the images together in the same time, instead it downloads them one by one using queue, since user may not want to see all the result all together, you will waste network and battery resource, for this topic please read here

I think it's better you learn about these, since they are very important. Hope it helps.


I think you should try Fedor's Android ListView LazyLoading example, its working nice.

As Fedor has mentioned about source code in their answer, you can have source code from:

Source is available here http://open-pim.com/tmp/LazyList.zip

GitHub: https://github.com/thest1/LazyList


FYI

Chirag Raval answer works but you need the Utils class too.

import java.io.InputStream;
import java.io.OutputStream;

public class Utils {
    public static void CopyStream(InputStream is, OutputStream os)
    {
        final int buffer_size=1024;
        try
        {
            byte[] bytes=new byte[buffer_size];
            for(;;)
            {
              int count=is.read(bytes, 0, buffer_size);
              if(count==-1)
                  break;
              os.write(bytes, 0, count);
            }
        }
        catch(Exception ex){}
    }
}

You can read more about this here

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜