1

私はダウンロードマネージャープロジェクトに取り組んでいるので、ダウンロード/ダウンロードアクションをすべて表示するには、ListViewを使用してダウンロードリストを表示することを好みます。ダウンロードタスクと同じ数があるとすると、すべてのタスクのプログレスバーを更新する必要があります。バックグラウンドダウンロードタスクのために、名前を付けた新しいクラスを作成しましたHttpDownloader。したがって、このクラスのオブジェクトにこれらのプログレスバーを渡します。新しいオブジェクトがタスクリストに追加されると、のコンストラクターを呼び出してHttpDownloader、新しいアイテムの進行状況バーをそのオブジェクトに渡します。私を混乱させたのはWhen i add a new object to tasklist and call notifyDataSetChanged of adapter, my list is refreshed, so all progress bar reset to default layout values but HTTPDownloader Thread is running in background successfully.それで、それは私の質問です、

1. notifyDataSetChangedを呼び出した後、古いリストビューのオブジェクトへの参照は破棄されますか?

2.はいの場合、古いビューの参照を保持するにはどうすればよいですか?

3.いいえの場合、プログレスバーがデフォルトにリセットされ、バックグラウンドプロセスがプログレスバーを通過してプログレスの値を変更しても変更されない理由を説明してください。

HTTPDownloader class

class HttpDownloader implements Runnable {
    public class HttpDownloader (String url, ProgressBar progressBar)
    {
        this.M_url = url;
        this.M_progressBar = progressBar;
    }

    @Override
    public void run()
    {
        DefaultHttpClient client = new DefaultHttpClient();
        HttpGet get = new HttpGet(this.M_url);
        HttpResponse response;

        try{
            response = client.execute(get);
            InputStream is = response.getEntity().getContent();
            long contentLength = response().getEntity().getContentLength();
            long downloadedLen = 0;
            int readBytes = 0;

            byte [] buffer = new byte [1024];


            while ((readBytes = in.read(buffer, 0, buffer.length)) != -1) {
                downloadedLen += readBytes;

                //Some storing to file codes

                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        M_progressBar.setProgress((100f * downloadedLen) / contentLength);
                    }
                });
            }

            is.close();
        } catch (ClientProtocolException e) {
            Log.e("HttpDownloader", "Error while getting response");
        } catch (IOException e) {
            Log.e("HttpDownloader", "Error while reading stream");
        }
    }
}

AdapterClass

class MyAdapter extends ArrayAdapter<String> {
    ArrayList<String> M_list;
    public MyAdapter(ArrayList<String> list) {
        super(MainActivity.this, R.layout.download_item, list);
        this.M_list = list;
    }

    @Override
    public int getCount() {
        return this.M_list.size();
    }

    public View getView(int position, View convertView, ViewGroup parent) {
        LayoutInflater inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
        View rowView = inflater.inflate(R.layout.download_item, parent, false);
        ProgressBar bar = (ProgressBar) rowView.findViewById(R.id.progrees);

        new Thread (new HttpDownloader(this.M_list.get(position), bar)).start();

        return rowView;
    }
}
4

2 に答える 2

1

優れたダウンロード マネージャーを示すための参照を保持したい場合は、その方法を見てみましょう。

概念は簡単に理解できます。アイテムをダウンロードするスレッドがあり、この作業の各部分をタスクと呼びます。各タスクは、リスト ビューの行に関連付けられています。これは、各アイテムのダウンロードの進行状況を表示するためです。

したがって、各タスクが各行に関連付けられている場合、実際に実行中のタスクを保存します。このようにして、実行中のタスクとその状態をいつでも把握できます。前に述べたように、getView() メソッドは何度も呼び出され、進行中の各ダウンロードとその状態を知る必要があるため、これは重要です。

Adapter のコードをいくつか見てみましょう。

class MyAdapter extends ArrayAdapter<Uri> {
    ArrayList<Uri> M_list;
    Map<Uri, HttpDownloader> taskMap;

    public MyAdapter(ArrayList<Uri> list) {
        super(MainActivity.this, R.layout.download_item, list);
        this.M_list = list;

        taskMap = new HashMap<Uri, HttpDownloader>();
    }

    @Override
    public int getCount() {
        return this.M_list.size();
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        View rowView = inflater.inflate(R.layout.download_item, parent, false);

        ProgressBar progressBar = (ProgressBar) rowView.findViewById(R.id.progressBar);

        configureRowWithTask(position, progressBar);

        return rowView;
    }

    private void configureRowWithTask(int position,final ProgressBar bar) {
        Uri url = M_list.get(position);
        HttpDownloader task;

        if (!(taskMapHasTaskWithUrl(url))) {
            task = new HttpDownloader(url, bar);
            Thread thread = new Thread(task);
            thread.start();
            taskMap.put(url, task);
        } else {
            task = taskMap.get(url);
            bar.setProgress(task.getProgress());
            task.setProgressBar(bar);
        }
    }

    private boolean taskMapHasTaskWithUrl(Uri url) {
        if (url != null) {
            Iterator taskMapIterator = taskMap.entrySet().iterator();

            while(taskMapIterator.hasNext()) {
                Map.Entry<Uri, HttpDownloader> entry = (Map.Entry<Uri, HttpDownloader>) taskMapIterator.next();
                if (entry.getKey().toString().equalsIgnoreCase(url.toString())) {
                    return true;
                }
            }
        }

        return false;
    }
}

それについてのいくつかの説明:

  • リンクでより適切に動作するように、アダプタのタイプを Uris の ArrayAdapter に変更しました。
  • キーと値のペアを持つマップを追加しました。キーは Uris で、値は HttpDownloaders です。このマップは、使用中の各タスクを保存します。
  • 2 つの重要なメソッドを追加しました。1 つは configureRowWithTask() で、その名前が示すように、タスクを使用して行を構成し、それらを関連付けますが、タスクが新しい場合のみで、それ以外の場合は使用中のタスクです。もう 1 つのメソッドは taskMapHasTaskWithUrl() で、指定されたタスクが (URL によって) 現在 taskMap にあるかどうかを単純にチェックします。
  • おそらく最も重要なメソッドは configureRowWithTask() ですので、詳しく説明します。

configureRowWithTask() メソッドは、1 つのタスクが使用中かどうかをチェックします。getView() が何度も呼び出されるため、このメソッドが必要ですが、同じダウンロード タスクを複数回実行したくないため、このメソッドはタスクが新しいかどうかをチェックします。 (taskMap には存在しません) 次に、HttpDownloader の新しいインスタンスを作成し、新しいタスクをマップに配置します。

要求されたタスクが taskMap に存在する場合、そのタスクは以前に要求されていましたが、リスト内の行がなくなり、getView() メソッドへの新しい呼び出しがあったことを意味します。タスクが存在するため、再度作成する必要はありません。おそらくタスクはアイテムをダウンロードしており、ダウンロードの進行状況を確認して行の進行状況バーに設定するだけです。最後に進行状況バーへの参照を設定します。おそらく HttpDownloader がこの参照を失っています。

この部分が明確になったら、2 番目の部分である HttpDownloader クラスに進み、いくつかのコードを見てみましょう。

public class HttpDownloader implements Runnable {

private Uri url;

private ProgressBar progressBar;
private int progress = 0;

private Handler handler;

public HttpDownloader (Uri url, ProgressBar progressBar) {
    this.url = url;
    this.progressBar = progressBar;
    progressBar.setProgress(progress);

    handler = new Handler();
}

@Override
public void run() {
    DefaultHttpClient client = new DefaultHttpClient();
    HttpGet get = new HttpGet(url.toString());
    HttpResponse response;


    try {
        response = client.execute(get);
        InputStream is = response.getEntity().getContent();
        long contentLength = response.getEntity().getContentLength();
        int downloadedLen = 0;
        int readBytes = 0;

        byte [] buffer = new byte [1024];


        while ((readBytes = is.read(buffer, 0, buffer.length)) != -1) {
            downloadedLen += readBytes;

            //Some storing to file codes

            progress = (int) ((100f * downloadedLen) / contentLength);

            handler.post(new Runnable() {
                @Override
                public void run() {
                    if (progressBar != null) {
                        progressBar.setProgress(progress);
                    }
                }
            });
        }

    } catch (ClientProtocolException e) {
        Log.e("HttpDownloader", "Error while getting response");
    } catch (IOException e) {
        e.printStackTrace();
        Log.e("HttpDownloader", "Error while reading stream");
    }
}

public int getProgress() {
    return progress;
}

public void setProgressBar(ProgressBar progressBar) {
    this.progressBar = progressBar;
}
}

このクラスは基本的に同じです。違いは、ハンドラーを使用して UI スレッドの進行状況バーの進行状況を変更し、進行状況バーへの参照が null でない場合にのみこの進行状況を変更することです。

重要なことは、バイトがダウンロードされるたびに、ダウンロードの進行状況の値が更新されることです。

テスト アプリでコードを確認しましたが、すべて問題なく動作するようです。問題がある場合はお知らせください。お手伝いします。)

ご理解いただければ幸いです。

于 2012-09-05T21:44:48.540 に答える
1

notifyDataSetChanged() を呼び出すか、Activity を再開すると、アダプターの getView() メソッドがリストの各行によって呼び出されます。

したがって、リストに新しいアイテムを追加して notifyDataSetChanged() を呼び出すと、その時点でリストにあるアイテムの数だけ getView() メソッドが実行され、追加した新しいアイテムがもう 1 つ発生します。

ご存じのように、getView() メソッドは行を構築するため、新しい ProgressBar を構築し、明らかにこの ProgressBar は進行状況の位置 0 から始まります。

したがって、notifyDataSetChanged() を呼び出すたびに、getView() メソッドが何度も呼び出され、行ごとに 1 回ずつ (android:layout_height param を 0dip に設定し、android:layout_weight を 1.0 に設定した場合)、答えは「はい」として再開される可能性があります。これが、ListView に新しい項目を追加したときに ProgressBars が「初期化」される理由です。

于 2012-09-05T07:07:34.187 に答える