Issue when fast scroll a listview

天大地大妈咪最大 提交于 2020-01-14 19:05:30

问题


I am working on a small project where I create a listview bound to an ArrayAdapter. In the getView() function of ArrayAdapter, I do a loading of images from web urls on thread, and set them to list items (based on position of course, so url[0] image is set to list_item[0] etc). It all seems to work well.

However when I was testing the app, I noticed that if I wait my listview to fully display, then perform a fast scroll back and forth, I see sometimes the image on one list item is misplaced on other (like being in an intermediate state). However it's not going away until I scroll the wrongly-displayed-item out of screen and then back.

I do not know if it relates to my loading web url using thread, or maybe loading image from local resource folder can have the same issue.

This actually leads to a question I have about getView() function. I think the logic is correct in my getView() because it's as simple as a binding of url to view based on position. And whenever getView() get a chance to be called, like when I scroll an item out of screen then back, it will make the list item display correctly.

The thing I do not understand is how to explain the issue that happened (like an intermediate state), and how to avoid it when writing code?

I paste my adapter code piece below, but I think the question maybe a general one:

    @Override
    public View getView(int position, View v, ViewGroup parent) {

        ViewHolder viewHolder = null;

        if (v == null) {
            LayoutInflater inflater = (LayoutInflater) getContext()
                    .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            v = inflater.inflate(layoutResourceId, parent, false);

            viewHolder = new ViewHolder();
            viewHolder.title = (TextView) v.findViewById(R.id.title);
            viewHolder.description = (TextView) v.findViewById(R.id.description);
            viewHolder.image = (ImageView) v.findViewById(R.id.image);

            v.setTag(viewHolder);
        } else {
            viewHolder = (ViewHolder) v.getTag();
        }

        listItem item = items[position];  //items is some global array
                                          //passed in to ArrayAdapter constructor
        if (item != null) {
            viewHolder.title.setText(item.title);   
            viewHolder.description.setText(item.description);

            if (!(item.imageHref).equalsIgnoreCase("null")) {
                mDrawableManager.fetchDrawableOnThread(item.imageHref, viewHolder.image);
            } else {
                viewHolder.image.setImageDrawable(null);
            }
        }
        return v;
    }
}

static class ViewHolder {
    TextView title;
    TextView description;
    ImageView image;
}

回答1:


I have same issue when scroll quickly it alternate the vales of some item to others, just like background color of some items if changes randomly. I solved this issue by searching a lot and find exact solution is just adding these two methods in your adapter if you are using ViewHolder in your adapter

@Override
    public int getViewTypeCount() {
        return getCount();
    }

    @Override
    public int getItemViewType(int position) {
        return position;
    }



回答2:


Assuming that you are not caching the downloaded image.. lets see the following code:

    if (!(item.imageHref).equalsIgnoreCase("null")) {
        mDrawableManager.fetchDrawableOnThread(item.imageHref, viewHolder.image);
    } else {
        viewHolder.image.setImageDrawable(null);
    }

Now if the image view is getting reused then it would already have the old image for the assigned list item. So until the thread download the image from the network it would display the old image and when the thread download the image for the current item it would be replaced with the new image. Try to change it to:

    if (!(item.imageHref).equalsIgnoreCase("null")) {
        viewHolder.image.setImageDrawable(SOME_DEFULAT_IMAGE);
        mDrawableManager.fetchDrawableOnThread(item.imageHref, viewHolder.image);
    } else {
        viewHolder.image.setImageDrawable(null);
    }

Or you can use something link smart image view that supports HTTP URI and also caches the images. Check out following link for smart image view:

https://github.com/loopj/android-smart-image-view http://loopj.com/android-smart-image-view/




回答3:


Add ImageLoader class from below link in your project. link

just call DisplayImage() methode of Image loader class as below in getView()

  ImageLoader imageLoader = new ImageLoader();


 yourImageView.setTag(URL_FOR_Your_Image);

 imageLoader.DisplayImage(URL_FOR_Your_Image,ACTIVITY.this, yourImageView);

Your images will load in background as you want without wait.




回答4:


I think you should declare your downloader method fetchDrawableOnThread() as "synchronized" . Because a lot of threads are working simultaneously and any thread which started later, can end earlier. So there are chances of images being misplaced.

It happened to me for a long time. Finally "synchronized" helped me do it cleanly. I hope it helps you too.




回答5:


I give it a try with synchronization again. Either synchronize the fetchDrawableOnThread(), or synchronize the global hashmap data within fetchDrawableOnThread(). First i thought the issue is gone, but when i tried more times later, i found the issue is still there.

Then i thought about the synchronization. fetchDrawableOnThread() is called from getView(), and getview() itself does not have a concurrency issue. Even if as Yogesh said, what happened INSIDE getView() is thread-based, and return early or late, it can not affect the correctness of getView(), i.e. the list item's display, only the sooner or later.

What i did(synchronization) inside fetchDrawableOnThread() i think it's still correct, 'cause i used a hashmap to cache images downloaded from remote url, and this hashmap is read/write upon in a multi-thread situation, so it should be locked. But i do not think it's the rootcause of the UI misplace, if hashmap is messed up, the image misplacement will be permanent.

Then i looked further on convertView reuse mechanism based on Praful's explanation. He explained clearly what happened when image always comes from remote and no cache locally, but my situation is i waited my list to display fully, i.e. all images download complete and cached complete, before i do the fast scroll. So in my experiment, the images are read from cache.

Then when inspecting my code, i saw one minor difference in the use of convertView as in getView() method, a lot of the example usages are like this:

public View getView(int position, View convertView, ViewGroup parent) {  // case 1
    View v = convertView;
    ....  // modify v
    return v;
}

However the example i happened to copy from use:

public View getView(int position, View convertView, ViewGroup parent) {  // case 2
    ....  // modify convertView
    return convertView;
}

I thought it makes no difference at first, 'cause according to what android says, 'ListView sends the Adapter an old view that it's not used any more in the convertView param.', so why not use 'convertView' para directly?

But i guess i was wrong. I changed my getView() code to case 1. Boom! everything works. No funny business ever no matter how fast i scroll the list.

Quite strange, is convertView only old, or is it old & in-use? If the later, we should only get a copy and then modify..... ??



来源:https://stackoverflow.com/questions/13083597/issue-when-fast-scroll-a-listview

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!