简体   繁体   English

滚动时Android ListView生涩

[英]Android ListView jerky when scrolling

I'm using XMLParser to get a XML with Date for a Listview. 我正在使用XMLParser为Listview获取带有Date的XML。 The Parser send the Date to the LazyAdapter und the LazyAdapter build the List. 解析器将日期发送到LazyAdapter,并将LazyAdapter发送到列表。

When I scrolling in my App, the ListView ist jerky and slow. 当我在我的应用程序中滚动时,ListView是生涩而缓慢的。 I think the Problem will be the ImageLoader. 我认为问题将是ImageLoader。 When I disable the line with the ImageLoader, its work better and not jerky. 当我使用ImageLoader禁用该行时,它的工作更好而不生涩。

The LazyAdapter: LazyAdapter:

  public View getView(int position, View convertView, ViewGroup parent) {
        View vi=convertView;
        if(convertView==null)


        vi = inflater.inflate(R.layout.list_row, null);

        TextView id = (TextView)vi.findViewById(R.id.id); 
        TextView title = (TextView)vi.findViewById(R.id.title);
        TextView artist = (TextView)vi.findViewById(R.id.artist);
        ImageView thumb_image=(ImageView)vi.findViewById(R.id.list_image); 

        HashMap<String, String> coupon = new HashMap<String, String>();
        coupon = data.get(position);

        id.setText(coupon.get(NewCoupons.id));
        title.setText(title);
        artist.setText(coupon.get(NewCoupons.artist));
        imageLoader.DisplayImage(KEY_THUMN, thumb_image);
        return vi;
    }

The ImageLoader: ImageLoader:

public class ImageLoader {

    MemoryCache memoryCache=new MemoryCache();
    FileCache fileCache;
    private Map<ImageView, String> imageViews=Collections.synchronizedMap(new WeakHashMap<ImageView, String>());
    ExecutorService executorService; 

    public ImageLoader(Context context){
        fileCache=new FileCache(context);
        executorService=Executors.newFixedThreadPool(5);
    }

    final int stub_id = R.drawable.no_image;
    public void DisplayImage(String url, ImageView imageView)
    {
        imageViews.put(imageView, url);
        Bitmap bitmap=memoryCache.get(url);
        if(bitmap!=null)
            imageView.setImageBitmap(bitmap);
        else
        {
            queuePhoto(url, imageView);
            imageView.setImageResource(stub_id);
        }
    }

    private void queuePhoto(String url, ImageView imageView)
    {
        PhotoToLoad p=new PhotoToLoad(url, imageView);
        executorService.submit(new PhotosLoader(p));
    }

    private Bitmap getBitmap(String url) 
    {
        File f=fileCache.getFile(url);

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

        //from web
        try {
            Bitmap bitmap=null;
            URL imageUrl = new URL(url);
            HttpURLConnection conn = (HttpURLConnection)imageUrl.openConnection();
            conn.setConnectTimeout(30000);
            conn.setReadTimeout(30000);
            conn.setInstanceFollowRedirects(true);
            InputStream is=conn.getInputStream();
            OutputStream os = new FileOutputStream(f);
            Utils.CopyStream(is, os);
            os.close();
            bitmap = decodeFile(f);
            return bitmap;
        } catch (Exception ex){
           ex.printStackTrace();
           return null;
        }
    }

    //decodes image and scales it to reduce memory consumption
    private Bitmap decodeFile(File f){
        try {
            //decode image size
            BitmapFactory.Options o = new BitmapFactory.Options();
            o.inJustDecodeBounds = true;
            BitmapFactory.decodeStream(new FileInputStream(f),null,o);

            //Find the correct scale value. It should be the power of 2.
            final int REQUIRED_SIZE=70;
            int width_tmp=o.outWidth, height_tmp=o.outHeight;
            int scale=1;
            while(true){
                if(width_tmp/2<REQUIRED_SIZE || height_tmp/2<REQUIRED_SIZE)
                    break;
                width_tmp/=2;
                height_tmp/=2;
                scale*=2;
            }

            //decode with inSampleSize
            BitmapFactory.Options o2 = new BitmapFactory.Options();
            o2.inSampleSize=scale;
            return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
        } catch (FileNotFoundException e) {}
        return null;
    }

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

    class PhotosLoader implements Runnable {
        PhotoToLoad photoToLoad;
        PhotosLoader(PhotoToLoad photoToLoad){
            this.photoToLoad=photoToLoad;
        }

        @Override
        public void run() {
            if(imageViewReused(photoToLoad))
                return;
            Bitmap bmp=getBitmap(photoToLoad.url);
            memoryCache.put(photoToLoad.url, bmp);
            if(imageViewReused(photoToLoad))
                return;
            BitmapDisplayer bd=new BitmapDisplayer(bmp, photoToLoad);
            Activity a=(Activity)photoToLoad.imageView.getContext();
            a.runOnUiThread(bd);
        }
    }

    boolean imageViewReused(PhotoToLoad photoToLoad){
        String tag=imageViews.get(photoToLoad.imageView);
        if(tag==null || !tag.equals(photoToLoad.url))
            return true;
        return false;
    }

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

    public void clearCache() {
        memoryCache.clear();
        fileCache.clear();
    }

}

You should pause your image loader during scroll. 您应该在滚动期间暂停图像加载器。 This is the best way to keep the scrolling very smooth. 这是保持滚动非常流畅的最佳方法。

To do that you need to add a setPaused(boolean pause) method in your image loader. 为此,您需要在图像加载器中添加setPaused(boolean pause)方法。 When paused you image loader should stop processing the queue and wait until the pause is cancelled. 暂停时,图像加载程序应停止处理队列并等待暂停取消。 This method will be triggered by an implementation of OnScrollListener inside the onScrollStateChanged() , by checking the scrollState parameter. 通过检查scrollState参数,此方法将由onScrollStateChanged()内的OnScrollListener实现触发。

imageLoader.setPaused(scrollState == OnScrollListener.SCROLL_STATE_FLING);

For more information, my source is a presentation given by Cyril Mottier : 有关更多信息,我的来源是Cyril Mottier给出的演示:

https://speakerdeck.com/cyrilmottier/optimizing-android-ui-pro-tips-for-creating-smooth-and-responsive-apps https://speakerdeck.com/cyrilmottier/optimizing-android-ui-pro-tips-for-creating-smooth-and-responsive-apps

See from slide 98. 见幻灯片98。


There is also an easier way but less efficient: just set the image loader thread priority to a lower value. 还有一种更简单的方法,但效率较低:只需将图像加载器线程优先级设置为较低的值即可。 Add the following line at the begginning of run() method inside your Runnable implementation : Runnable实现中的run()方法的前面添加以下行:

Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);

There is a useful little open source library on GitHub that can handle this. GitHub上有一个有用的小开源库,可以处理这个问题。

https://github.com/nostra13/Android-Universal-Image-Loader https://github.com/nostra13/Android-Universal-Image-Loader

It allows for pausing on scrolling as well as others niceties such as allowing the images to fade in and image caching. 它允许暂停滚动以及其他细节,例如允许图像淡入和图像缓存。

  1. first, you should use convertview and ViewHolder 首先,您应该使用convertview和ViewHolder

     static class ViewHolder { TextView idTestView; TextView titleTestView; TextView artistTestView; } public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder; if(convertView == null) { convertView = inflater.inflate(R.layout.list_row, null); holder = new ViewHolder(); holder.idTestView = (TextView)convertView.findViewById(R.id.id); holder.titleTestView = (TextView)convertView.findViewById(R.id.title); holder.artistTestView = (TextView)convertView.findViewById(R.id.artist); holder.image = (ImageView)convertView.findViewById(R.id.list_image); convertView.setTag(holder); } else { holder = convertView.getTag(); } // initial viewholder attributes 

    } }

  2. you should use imageloader to show picture, and cache in memory also cache in dist. 你应该使用imageloader来显示图片,并在内存中缓存也在dist中缓存。 you can get it from there: https://github.com/nostra13/Android-Universal-Image-Loader 你可以从那里得到它: https//github.com/nostra13/Android-Universal-Image-Loader

  3. your listview should add setOnScrollListener, when scroll flying, you should not load picture 你的listview应该添加setOnScrollListener,当滚动飞行时,你不应该加载图片

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM