Android仅加载ListView的可见元素

发布于 2024-11-29 18:38:18 字数 7532 浏览 1 评论 0原文

我在我的应用程序中使用延迟加载列表,我只需要加载 ListView 的可见元素。我在互联网上搜索,尝试了很多东西,但仍然无法让这些东西工作。你能给我一个建议吗怎么办呢?

这是 ImageLoader 类:

package com.stampii.stampii.tableview;

import java.io.File;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Stack;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import com.stampii.stampii.R;
import android.app.Activity;
import android.content.Context;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.Log;
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;
    private AssetManager mAssetManager;


    public ImageLoader(Context context){
        //Make the background thead low priority. This way it will not affect the UI performance
        photoLoaderThread.setPriority(Thread.NORM_PRIORITY-1);
        mAssetManager = context.getAssets();

        //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(),"Stampii");
        else
            cacheDir=context.getCacheDir();
        if(!cacheDir.exists())
            cacheDir.mkdirs();
    }


    final int stub_id=R.drawable.default_img;
    public void DisplayImage(String url, Activity activity, ImageView imageView)
    {
        if(cache.containsKey(url))
            imageView.setImageBitmap(cache.get(url));
        else
        {
            queuePhoto(url, activity, imageView);
            imageView.setImageResource(stub_id);
        }    
    }

    private void queuePhoto(String url, Activity activity, ImageView imageView)  //opashka
    {
        //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();
    }

    private Bitmap getBitmap(String src) {
        Bitmap myBitmap = null;


                //Decryption
                try {
                Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
                SecretKeySpec keySpec = new SecretKeySpec("01234567890abcde".getBytes(), "AES");
                IvParameterSpec ivSpec = new IvParameterSpec("fedcba9876543210".getBytes());
                cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);

                InputStream input = mAssetManager.open(src);
                CipherInputStream cis = new CipherInputStream(input, cipher);

                myBitmap = BitmapFactory.decodeStream(cis);

                }
                catch(Exception e){
                    e.printStackTrace();
                    Log.v("ERROR","Error : "+e);
                }


                return myBitmap;


        }

    //Task for the queue
    private class PhotoToLoad
    {
        public String url;
        public ImageView imageView;
        public PhotoToLoad(String u, ImageView i){
            url=u; 
            imageView=i;
        }
    }

    PhotosQueue photosQueue=new PhotosQueue();

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

    //stores list of photos to download
    class PhotosQueue
    {
        private Stack<PhotoToLoad> photosToLoad=new Stack<PhotoToLoad>();

        //removes all instances of this ImageView
        public void Clean(ImageView image)
        {
            for(int j=0 ;j<photosToLoad.size();){
                if(photosToLoad.get(j).imageView==image)
                    photosToLoad.remove(j);
                else
                    ++j;
            }
        }
    }

    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);
                        Object tag=photoToLoad.imageView.getTag();
                        if(tag!=null && ((String)tag).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
            }
        }
    }

    PhotosLoader photoLoaderThread=new PhotosLoader();

    //Used to display bitmap in the UI thread
    class BitmapDisplayer implements Runnable
    {
        Bitmap bitmap;
        ImageView imageView;
        public BitmapDisplayer(Bitmap b, ImageView i){bitmap=b;imageView=i;}
        public void run()
        {
            if(bitmap!=null)
                imageView.setImageBitmap(bitmap);
            else
                imageView.setImageResource(stub_id);
        }
    }

    public void clearCache() {
        //clear memory cache

        long size=0;
            cache.clear();

            //clear SD cache
            File[] files=cacheDir.listFiles();
            for(File f:files){
                size=size+f.length();
                    f.delete();
            }
    }




}

EDIT1:

我的 LazyAdapter 类中的 getView 方法:

   public View getView(int position, View convertView, ViewGroup parent) {
        View vi=convertView;
        ViewHolder holder;
        if(convertView==null){
            vi = inflater.inflate(R.layout.item, null);
            holder=new ViewHolder();
            holder.name=(TextView)vi.findViewById(R.id.name);
            holder.info=(TextView)vi.findViewById(R.id.info);
            holder.image=(ImageView)vi.findViewById(R.id.image);
            vi.setTag(holder);
        }
        else
            holder=(ViewHolder)vi.getTag();

        holder.name.setText("MotoGP Rider");
        holder.info.setText("Category");
        holder.image.setTag(data[position]);
        imageLoader.DisplayImage(data[position], activity, holder.image);
        return vi;
    }

有什么想法吗?

I'm using Lazy Loading List in my application and I need to load only the visible elements of ListView.I've search over the internet,tried a lot of things but still cannot get the things to work.Can you give me a suggestion how to do it?

Here is the ImageLoader class :

package com.stampii.stampii.tableview;

import java.io.File;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Stack;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import com.stampii.stampii.R;
import android.app.Activity;
import android.content.Context;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.Log;
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;
    private AssetManager mAssetManager;


    public ImageLoader(Context context){
        //Make the background thead low priority. This way it will not affect the UI performance
        photoLoaderThread.setPriority(Thread.NORM_PRIORITY-1);
        mAssetManager = context.getAssets();

        //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(),"Stampii");
        else
            cacheDir=context.getCacheDir();
        if(!cacheDir.exists())
            cacheDir.mkdirs();
    }


    final int stub_id=R.drawable.default_img;
    public void DisplayImage(String url, Activity activity, ImageView imageView)
    {
        if(cache.containsKey(url))
            imageView.setImageBitmap(cache.get(url));
        else
        {
            queuePhoto(url, activity, imageView);
            imageView.setImageResource(stub_id);
        }    
    }

    private void queuePhoto(String url, Activity activity, ImageView imageView)  //opashka
    {
        //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();
    }

    private Bitmap getBitmap(String src) {
        Bitmap myBitmap = null;


                //Decryption
                try {
                Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
                SecretKeySpec keySpec = new SecretKeySpec("01234567890abcde".getBytes(), "AES");
                IvParameterSpec ivSpec = new IvParameterSpec("fedcba9876543210".getBytes());
                cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);

                InputStream input = mAssetManager.open(src);
                CipherInputStream cis = new CipherInputStream(input, cipher);

                myBitmap = BitmapFactory.decodeStream(cis);

                }
                catch(Exception e){
                    e.printStackTrace();
                    Log.v("ERROR","Error : "+e);
                }


                return myBitmap;


        }

    //Task for the queue
    private class PhotoToLoad
    {
        public String url;
        public ImageView imageView;
        public PhotoToLoad(String u, ImageView i){
            url=u; 
            imageView=i;
        }
    }

    PhotosQueue photosQueue=new PhotosQueue();

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

    //stores list of photos to download
    class PhotosQueue
    {
        private Stack<PhotoToLoad> photosToLoad=new Stack<PhotoToLoad>();

        //removes all instances of this ImageView
        public void Clean(ImageView image)
        {
            for(int j=0 ;j<photosToLoad.size();){
                if(photosToLoad.get(j).imageView==image)
                    photosToLoad.remove(j);
                else
                    ++j;
            }
        }
    }

    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);
                        Object tag=photoToLoad.imageView.getTag();
                        if(tag!=null && ((String)tag).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
            }
        }
    }

    PhotosLoader photoLoaderThread=new PhotosLoader();

    //Used to display bitmap in the UI thread
    class BitmapDisplayer implements Runnable
    {
        Bitmap bitmap;
        ImageView imageView;
        public BitmapDisplayer(Bitmap b, ImageView i){bitmap=b;imageView=i;}
        public void run()
        {
            if(bitmap!=null)
                imageView.setImageBitmap(bitmap);
            else
                imageView.setImageResource(stub_id);
        }
    }

    public void clearCache() {
        //clear memory cache

        long size=0;
            cache.clear();

            //clear SD cache
            File[] files=cacheDir.listFiles();
            for(File f:files){
                size=size+f.length();
                    f.delete();
            }
    }




}

EDIT1:

getView method in my LazyAdapter class :

   public View getView(int position, View convertView, ViewGroup parent) {
        View vi=convertView;
        ViewHolder holder;
        if(convertView==null){
            vi = inflater.inflate(R.layout.item, null);
            holder=new ViewHolder();
            holder.name=(TextView)vi.findViewById(R.id.name);
            holder.info=(TextView)vi.findViewById(R.id.info);
            holder.image=(ImageView)vi.findViewById(R.id.image);
            vi.setTag(holder);
        }
        else
            holder=(ViewHolder)vi.getTag();

        holder.name.setText("MotoGP Rider");
        holder.info.setText("Category");
        holder.image.setTag(data[position]);
        imageLoader.DisplayImage(data[position], activity, holder.image);
        return vi;
    }

Any ideas?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

层林尽染 2024-12-06 18:38:18

检查代码后:我确信只有可见的图像正在加载!

但是因为下面的一些图像具有相同的网址!您会认为它们是以不同的顺序加载的。但是,它们是根据可见图像加载的,但具有相同 url 的任何其他列表项都会显示图像。


我在应用程序中使用延迟加载列表,并且只需要加载 ListView 的可见元素。

ListView 上可见的每个位置都会调用 getView

需要加载的图片必须从 Adapter 的 getView 添加到队列中,

因此您应该重写

public View   getView  (int position, View convertView, ViewGroup parent){
    super(position,convertView,parent);
    //now add the picture at position to the queue
}

注意:小心

假设您将照片 1 添加到队列中:这并不意味着不会再次调用 getView位置 1。

换句话说,考虑以下事实:可以使用getView多次调用position。

After checking the code: I am sure that ONLY the images that are visible are being loaded!

BUT because some of the images down have the same url! You will think that they are loading in a different order. However they are loaded based on the images visible but any other listitem with the same url will show the image.


I'm using Lazy Loading List in my application and I need to load only the visible elements of ListView.

getView is called for each position that is visible on the ListView

The pictures you need to load must be added to the queue from the getView of the Adapter

So you should Override

public View   getView  (int position, View convertView, ViewGroup parent){
    super(position,convertView,parent);
    //now add the picture at position to the queue
}

Note: Be careful

Say you added photo 1 to the queue: This does not mean that getView will not be called again with position 1.

In other words, account for the fact that a position may be called many times using getView.

萝莉病 2024-12-06 18:38:18

要仅加载可见元素,您需要实现 AbsListView.OnScrollListener 接口。从这个接口中你可以从它的方法中找到“scroll event”和“scrolleventstate”的变化。

ListView list = (ListView) findViewById(R.id.listview);
MyAdapter adapter = new MyAdapter(context, items);
list.setAdapter(adapter);
list.setOnScrollListener(new OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, 
        int scrollState) {
     // Do nothing
}

@Override
public void onScroll(AbsListView view, int firstVisibleItem, 
        int visibleItemCount, int totalItemCount) {

    // threshold being indicator if bottom of list is hit
    if (firstVisibleItem = threshold) {
        pullMoreData();
    }
}
});

有关于此的更多解释。您可以按照本教程进行操作。 智能加载列表视图 , 动态数据listview检测结束滚动获取可见数据索引

To load only visible elements you need to implements a interface of AbsListView.OnScrollListener. From this interface you can find out the "scroll event" and "scrolleventstate" change from its methods.

ListView list = (ListView) findViewById(R.id.listview);
MyAdapter adapter = new MyAdapter(context, items);
list.setAdapter(adapter);
list.setOnScrollListener(new OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, 
        int scrollState) {
     // Do nothing
}

@Override
public void onScroll(AbsListView view, int firstVisibleItem, 
        int visibleItemCount, int totalItemCount) {

    // threshold being indicator if bottom of list is hit
    if (firstVisibleItem = threshold) {
        pullMoreData();
    }
}
});

For more explanation about this. you can follow this tutorials. smart loading listview, dynamic data listview, detect end scroll, get visible data index

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文