15

I have a ListView attached to an ArrayAdapter. When the user clicks a download button for an item in the ListView a download starts using the DownloadManager.

What I want to do is to track the download progress with a progress bar (placed in the item layout). How can this be achieved?

The way Pocket Cast does it is exacly what I'm after:

Pocket Cast exampel http://www.mrcrab.net/images/thumb_big/9982-Pocket_Casts_Apk_v4.3.2_Android-0.jpg

Note: I know how to work with the DownloadManager, it's the instant update of the progress bar that is tricky.

Mike
  • 1,000
  • 1
  • 10
  • 18

2 Answers2

17

This is the way I finally solved it (after many iterations and different implementations). It's a bit tricky but basically you need three things:

  1. An AsyncTask that gathers meta data
  2. A scroll listener that tells us when the user has stopped scrolling/flinging
  3. A clever algorithm that finds any visible row that needs updating and asks the adapter to only update that specific row

This is the way I designed and implemented it:

Screenshot

I wrote in more detail about it here, and please see the github code for the complete imlementation.

    private class UpdaterAsyncTask extends AsyncTask<Void, Void, Void> {

    boolean isRunning = true;

    public void stop() {
        isRunning = false;
    }

    @Override
    protected Void doInBackground(Void... params) {

        while (isRunning) {

            // Gather data about your adapter objects
            // If an object has changed, mark it as dirty                

            publishProgress();

            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        return null;
    }

    @Override
    protected void onProgressUpdate(Void... params) {
        super.onProgressUpdate();

        // Update only when we're not scrolling, and only for visible views
        if (mScrollState == OnScrollListener.SCROLL_STATE_IDLE) {
            int start = mListview.getFirstVisiblePosition();
            for(int i = start, j = mListview.getLastVisiblePosition(); i<=j; i++) {
                View view = mListview.getChildAt(i-start);
                if (((Content)mListview.getItemAtPosition(i)).dirty) {
                    mListview.getAdapter().getView(i, view, mListview); // Tell the adapter to update this view
                }

            }
        }

      }
    }
Mike
  • 1,000
  • 1
  • 10
  • 18
  • Hello, I try to make it but I have a pb. When I press BackButton, the dl continues (that's what I want) but when I get back in this view, progressBars don't move. Can you put an example of your adapter. Does it works like me when you press back and return to same view ? – Cocorico Feb 03 '14 at 11:16
  • Yes, it works when the `Activity` starts as well as when it resumes activity (from a paused state). Have a look at `private void refreshScreen()` in the source code here: https://github.com/slidese/SGU/blob/master/src/se/slide/sgu/StartActivity.java – Mike Feb 03 '14 at 14:36
0

For a specific view item, you can retrieve it using getChildAt(int index) and update it.

And you can traverse through all visible items by the help of getFirstVisiblePosition() and getLastVisiblePosition()

huy.nguyen
  • 454
  • 2
  • 9
  • But how do you know if a specific view is in a downloadable state? I assume you would have an `AsyncTask` that would query the `DownloadManager`, so you're left with 0 or more download IDs but have no idea what `ListView` items these correspond to. Or am I missing something here? – Mike Oct 30 '13 at 17:44
  • With download IDs, you can have URIs (either by asking DownloadManager or your own map that matches a download ID with a URI). From a `ListView` position, you can get the URI from your adapter. So now, you have a link between download ID and `ListView` position. – huy.nguyen Oct 30 '13 at 17:55
  • Or you can even have a map that stores download IDs and `ListView` positions. That map can be manipulated when you enqueue a request with the `DownloadManager`. – huy.nguyen Oct 30 '13 at 17:58
  • Thanks, I think this might work. Will try an implementation later tonight. – Mike Oct 30 '13 at 18:20
  • And please share any news :) – huy.nguyen Oct 30 '13 at 18:41
  • 1
    Just posted the way I finally implemented it. You were correct to use the methods you mentioned but it was just a small part of the total implementation I felt a more complete answer was needed. Hope you're ok with that @huy.nguyen – Mike Nov 25 '13 at 16:49