Search code examples
androidlistviewandroid-fragmentsandroid-asynctaskandroid-progressbar

Fail to AsyncTask update progressBar's progress when first enter the fragment ListView, then okay after scroll ListView


The progressbar cannot show progress if without scroll down and back to same position visit again, detail please check this demo:

https://www.youtube.com/watch?v=wGu8MyUHidQ&feature=youtu.be

No exception or error, maybe ListView bug or logic error, anyone have any idea?

DownloadInfo class:

private final static String TAG = DownloadInfo.class.getSimpleName();
public enum DownloadState {
    NOT_STARTED,
    QUEUED,
    DOWNLOADING,
    COMPLETE
}
private volatile DownloadState mDownloadState = DownloadState.NOT_STARTED;
private final String mFilename;
private volatile Integer mProgress;
private final Integer mFileSize;
private volatile ProgressBar mProgressBar;

public DownloadInfo(String filename, Integer size) {
    mFilename = filename;
    mProgress = 0;
    mFileSize = size;
    mProgressBar = null;
}
//Follow by getters & setters

DownloadInfoArrayAdapter Class:

public class DownloadInfoArrayAdapter extends ArrayAdapter<DownloadInfo> {
private static class ViewHolder {
    TextView textView;
    ProgressBar progressBar;
    Button button;
    DownloadInfo info;
}

public DownloadInfoArrayAdapter(Context context, List<DownloadInfo> objects) {
    super(context, objects);
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    View row = convertView;
    final DownloadInfo info = getItem(position);

    ViewHolder holder = null;

    if (null == row) {
        LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        row = inflater.inflate(R.layout.file_download_row, parent, false);

        holder = new ViewHolder();
        holder.textView = (TextView) row.findViewById(R.id.downloadFileName);
        holder.progressBar = (ProgressBar) row.findViewById(R.id.downloadProgressBar);
        holder.button = (Button) row.findViewById(R.id.downloadButton);
        holder.info = info;
        row.setTag(holder);
    } else {
        holder = (ViewHolder) row.getTag();
        holder.info.setProgressBar(null);
        holder.info = info;
        holder.info.setProgressBar(holder.progressBar);
    }

    holder.textView.setText(info.getFilename());
    holder.progressBar.setProgress(info.getProgress());
    holder.progressBar.setMax(info.getFileSize());
    info.setProgressBar(holder.progressBar);

    holder.button.setEnabled(info.getDownloadState() == DownloadState.NOT_STARTED);
    final Button button = holder.button;
    holder.button.setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View v) {
            info.setDownloadState(DownloadState.QUEUED);
            button.setEnabled(false);
            button.invalidate();
            FileDownloadTask task = new FileDownloadTask(info);
            task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
        }
    });
    return row;
}
}

FileDownloadTask class:

@Override
protected void onProgressUpdate(Integer... values) {
    mInfo.setProgress(values[0]);
    ProgressBar bar = mInfo.getProgressBar();
    if (bar != null) {
        bar.setProgress(mInfo.getProgress());
        bar.invalidate();
    }
}

@Override
protected Void doInBackground(Void... params) {
    Log.d(TAG, "Starting download for " + mInfo.getFilename());
    mInfo.setDownloadState(DownloadState.DOWNLOADING);
    for (int i = 0; i <= mInfo.getFileSize(); ++i) {
        try {
            Thread.sleep(16);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        publishProgress(i);
    }
    mInfo.setDownloadState(DownloadState.COMPLETE);
    return null;
}

@Override
protected void onPostExecute(Void result) {
    mInfo.setDownloadState(DownloadState.COMPLETE);
}

@Override
protected void onPreExecute() {
    mInfo.setDownloadState(DownloadState.DOWNLOADING);
}

In the fragment add click listener

lvStickerGroup = (ListView) activity.findViewById(R.id.lvStickerGroup);
    List<DownloadInfo> downloadInfo = new ArrayList<DownloadInfo>();
    for (int i = 0; i < 50; ++i) {
        downloadInfo.add(new DownloadInfo("File " + i, 1000));
    }

    adapter = new DownloadInfoArrayAdapter(activity, downloadInfo);
    lvStickerGroup.setAdapter(adapter); 
    lvStickerGroup.setOnItemClickListener(new AdapterView.OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
            Toast.makeText(activity, "bla" + i, Toast.LENGTH_SHORT).show();
        }
    });

    //Testing all below, no luck
    ((BaseAdapter) adapter).notifyDataSetChanged();
    lvStickerGroup.invalidate();
    lvStickerGroup.invalidateViews();
    lvStickerGroup.refreshDrawableState();

    lvStickerGroup.post(new Runnable() {
        public void run() {
            for (int a = 0; a < lvStickerGroup.getCount(); a++) {
                lvStickerGroup.setSelection(a);
            }
            for (int a = lvStickerGroup.getCount() - 1; a >= 0; a--) {
                lvStickerGroup.setSelection(a);
            }
        }
    });

I tried to programmatically scroll to bottom and back to top, same no luck, except programmatically scroll to the item position will not show in the first page when enter the fragment initially.

Besides, I tried to invalidate(), invalidateView() notifyDataSetChanged on the adapter, same problem occurs, is it possibly a ListView bug?


Solution

  • Nothing wrong with the code above, fully functional. I found the clue in Google ListView IO conference

    https://www.youtube.com/watch?v=wDBM6wVEO70
    

    ListView cannot wrap content, else will have bugs showing up. That why I experience such issue.