在android列表视图中从网络加载图像的流畅和快速的方式!

时间:2011-07-27 06:43:29

标签: android listview

我需要从网上列出数据,因为我有1张图片和2张文字。我解析所有数据并显示它,但图像显示在列表中非常慢。所以我正在寻找最佳方法。

请帮帮我。

提前致谢

6 个答案:

答案 0 :(得分:5)

请复制以下课程。该类是从网上下载图像并存储到 存储卡或应用程序的内部存储器。

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();
    }
}

实施例

首先创建你的图像Loader类对象。

Imageloader imageLoader = new Imageloader(getApplicationContext());

然后将图片网址设置为imageview的setTag属性。

imgImageView.setTag(Your Image Url);

然后调用ImageLoader类显示图像功能。需要3个参数。

1)图片网址

2)您当前的班级名称

3)ImageView

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

这个功能从网上下载图像并存储到内存中并从内存中显示。

答案 1 :(得分:4)

您可以先下载整个文本,然后只显示图像。不要下载整个图像列表,因为可能大部分图像永远不会被显示。

尝试向用户迈进一步。例如,如果列表在您第一次输入活动时可以显示6个图像,请在切换到此活动之前预取这6个图像,然后通过Intent传递它们。您还可以使用一个线程下载如果用户向下滚动时将显示的以下(3或4)图像。

为了加快这一过程,请考虑预先缩放图像,以便缩小尺寸,下载速度更快。

答案 2 :(得分:2)

使用此类而不是普通的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");
    }

}

答案 3 :(得分:1)

在List中下载和显示图像有点复杂。您需要考虑的一些要点是:

  1. 使用不同的线程进行图像下载,你可以使用android asynctask类,你读了它here
  2. 缓存图像,有几种方法可以实现此目的,缓存内存,内部缓存存储或外部缓存存储(SD卡),请阅读here
  3. 您可以在显示图像时使用延迟加载,这意味着应用程序不会同时将所有图像一起下载,而是使用队列逐个下载它们,因为用户可能不希望看到所有结果所有这些,你将浪费网络和电池资源,对于这个主题,请阅读here
  4. 我认为你了解这些更好,因为它们非常重要。希望它有所帮助。

答案 4 :(得分:0)

我认为你应该试试Fedor的Android ListView LazyLoading example,它的效果很好。

Fedor在答案中提到了源代码,你可以从以下源代码获得源代码:

可在此处获取资源http://open-pim.com/tmp/LazyList.zip

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

答案 5 :(得分:0)

FYI

Chirag Raval回答有效,但你也需要Utils课程。

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){}
    }
}

您可以阅读有关此here

的更多信息