2012-08-29 147 views
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.所以,这是我的问题是,Android的ListView和适配器

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; 
    } 
} 

回答

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; 
    } 
} 

一下几点说明:

  • 我已经改变了您的适配器的类型URI的ArrayAdapter合作与链接更好。
  • 我已经添加了一个Map,它有双键值,键是Uris,值是HttpDownloaders。该地图将保存每个正在使用的任务。
  • 我已经添加了两个重要的方法,一个是configureRowWithTask(),就像它自己的名字所说的那样,配置一个任务的行,将它们关联起来,但只有当任务是新的时,在其他情况下才是正在使用的任务。 另一种方法是taskMapHasTaskWithUrl(),只是检查给定的任务(通过它的url)是否当前在taskMap中。
  • 也许,最重要的方法是configureRowWithTask(),我将会深入解释它。

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; 
} 
} 

这个类是基本相同的性差异的是,我用一个处理器来改变进度条在UI线程中的进度,并且仅当对进度条的引用不为null时才更改此进度。

重要的是每次下载字节时,刷新下载进度的值。

我已经检查了测试应用程序中的代码,似乎所有程序运行正常,如果您遇到问题,请告诉我,我会尽力帮助您; )

希望你能理解它。

1

当调用notifyDataSetChanged()或活性恢复,getView()的适配器的方法是通过在列表的每行称为。因此,当您向列表中添加新项目并调用notifyDataSetChanged()时,getView()方法执行的次数与列表中的项目在该时刻的次数相同,再加上一次则会导致添加的新项目。如你所知,getView()方法构建行,因此构建一个新的ProgressBar,并且明确地看到这个ProgressBar开始于进度的位置0。因此,每当你调用notifyDataSetChanged(),getView()方法被调用多次,每行一个(如果你设置android:layout_height参数为0dip和android:layout_weight为1.0),那么答案可以恢复为yes。 ,就像你可能知道的ListViews一样),这就是当你向ListView添加新项目时,你得到ProgressBars“初始化”的原因。

+0

我想保留此参考。当我们调用'notifyDataSetChanged()'时,所有旧的引用都可以改变。我想保留这些参考文献,我该如何保留这些? (我使用ListView编程下载管理器UI,我们应该更改所有进度条的值) –