我正在开发一个Download Manager项目,因此,为了显示所有下载/下载操作,我更喜欢使用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之后,对旧listview的对象的引用是破坏吗?
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;
}
}
答案 0 :(得分:1)
当你调用notifyDataSetChanged()或Activity恢复时,适配器的getView()方法会被列表的每一行调用。
因此,当您向列表中添加新项并调用notifyDataSetChanged()时,getView()方法将在该时刻执行列表中的项目多次,再加上一项导致您添加的新项目。< / p>
如您所知,getView()方法构建行,因此构建一个新的ProgressBar,并且显示此ProgressBar从进度的第0位开始。
所以答案可以恢复为是,每次调用notifyDataSetChanged()时,多次调用getView()方法,每行一个(如果你设置android:layout_height param为0dip,android:layout_weight为1.0,就像你可能知道ListViews一样),这就是你在ListView中添加新项目时“Progressten”初始化的原因。
答案 1 :(得分:1)
好吧,如果你想保留引用以显示一个好的下载管理器,让我们看看它是如何做的,按部分进行。
这个概念很容易理解,我们有线程下载项目,我们将调用这一项工作任务。每个任务都与列表视图中的一行相关联,因为您希望显示每个项目下载的下载进度。
因此,如果每个任务都与每一行相关联,那么我们将保存实际执行的任务,这样,我们随时都知道正在运行的任务及其状态。这是重要的,因为像我之前说的那样,getView()方法被多次调用,我们需要知道正在进行的每个下载及其状态。
让我们看看我们的适配器的一些代码:
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;
}
}
关于它的一些解释:
configureRowWithTask()方法检查一个任务是否正在使用,这个方法是必需的,因为getView()被多次调用,但我们不希望同一个下载任务被执行多次,所以这个方法检查一下,如果任务是新的(不存在于taskMap中)然后创建一个新的HttpDownloader实例并将新任务放在地图中。
如果taskMap中存在请求的任务,则表示先前已请求该任务,但列表中的行已经消失,并且已对getView()方法进行了新调用。所以任务存在,我们不必再次创建它,可能任务是下载项目,我们唯一要做的就是查看其下载进度并将其设置为行的进度条。最后设置了对进度条的引用,可能是HttpDownloader丢失了这个引用。
有了这部分,我们继续第二部分,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;
}
}
这个类基本相同,不同之处在于我使用Handler来更改UI线程中进度条的进度,并且仅当对进度条的引用不为null时才更改此进度。
重要的是每次下载字节时,下载进度的值都会刷新。
我已经在测试应用程序中检查了代码,似乎所有运行正常,如果您遇到一些问题,请告诉我,我会尽力帮助您; )
希望你明白。