2012-08-29 192 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,我們應該更改所有進度條的值) –