Search code examples
androidlistviewandroid-listviewlistviewitem

progressBar in ListView changes parent on scroll


I have a ListView with rows created with using specific layout. Each row have ProgressBar that is View.GONE by default. I need to make ProgressBar in ListView item visible when this element is clicked. The problem is that when i click the element ProgressBars became visible not only in the row i clicked but in other random rows too. Here is the code:

Creating ListView and setting OnItemClickListener:

private void buildTracklist(ArrayList<Audio> differences, ArrayList<Audio> vkAudioList) {
        ArrayList<Map<String, Object>> data = new ArrayList<Map<String, Object>>(vkAudioList.size());
        Map<String, Object> m;
        int i;
        for (i=0;i<vkAudioList.size();i++) {
            m = new HashMap<String, Object>();
            m.put(AUDIO_ARTIST, vkAudioList.get(i).artist);
            m.put(AUDIO_TITLE, vkAudioList.get(i).title);
            if (arrayListContainsAudio(differences, vkAudioList.get(i))) {
                m.put(AUDIO_DOWNLOADED, R.drawable.image_blank);
            } else {
                m.put(AUDIO_DOWNLOADED, R.drawable.image_downloaded);
            }
            data.add(m);
        }
        String[] from = {AUDIO_TITLE,AUDIO_ARTIST,AUDIO_DOWNLOADED};
        int[] to = {R.id.audioTitleTextView,R.id.audioArtistTextView,R.id.isDownloadedImageView};
        sAdapter = new SimpleAdapter(this, data, R.layout.tracklist_item, from, to);
        audioListView = (ListView) findViewById(R.id.audioListView);
        audioListView.setAdapter(sAdapter);

        audioListView.setOnItemClickListener(new OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                Log.i(TAG,"position="+position+" id="+id);

                view.findViewById(R.id.audioItemProgressBar).setVisibility(View.VISIBLE);
                //Here I'm passing progressBar further
            }
        });
    }

tracklistItem layout:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >

        <TextView
            android:id="@+id/audioTitleTextView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="5dp"
            android:layout_marginTop="2dp"
            android:gravity="top|left"
            android:text="Title"
            android:textSize="24dp" />

        <TextView
            android:id="@+id/audioArtistTextView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@id/audioTitleTextView"
            android:layout_marginLeft="5dp"
            android:layout_marginTop="2dp"
            android:text="Artist"
            android:textSize="16sp" />

        <ProgressBar
            android:id="@+id/audioItemProgressBar"
            style="?android:attr/progressBarStyleHorizontal"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentLeft="true"
            android:layout_below="@+id/audioArtistTextView"
            android:visibility="gone" />

    </RelativeLayout>

    <ImageView
        android:id="@+id/isDownloadedImageView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|right"
        android:src="@drawable/downloaded_backgroundv10"
        android:visibility="visible" />

</FrameLayout>

I tried to get the listview item view with getChildAt() of getView() but the same problem occured. Thanks for help and sorry for my bad english.

Update: i found that progressBar i'm using changes parent item when i scroll ListView but i need it to be in one row no matter what part of ListView is currently on screen.


Solution

  • ListView, like all Adapter views, uses views recycling.

    The problem is that when i click the element ProgressBars became visible not only in the row i clicked but in other random rows too.

    I bet it happens when you scroll the view. It works like this: you show the progress bar, then scroll the list. The item with the progress bar scrolls outside of the visible area of the screen. This is the moment the view gets recycled. The system detaches it from listview and attaches again to show another row of your list. This is why you see the progress bar again.

    What to do?

    You should turn the visibility of the progress bar inside the getView() method of your custom adapter (which can be based on BaseAdapter). Whether it's turned on or not should depend on some flag.

    Read this to find out more about views recycling in adapter views.


    i need to change visibility of progressbar and it's progress when i click on the element, not when i create listview.

    Just define a method in your adapter which will change the value of the flag. Then you can call notifyDataSetChanged.

    The important thing to remember when dealing with adapter views is that you change the underlying data, not the view, because you may have several thousand items in your lists, but there are only a few views to deal with the visible rows.